diff --git a/CHANGELOG.md b/CHANGELOG.md index 80a1183..7ac3520 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,36 @@ All notable changes to this project will be documented here. Format based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). This project follows [Semantic Versioning](https://semver.org/). + +## v4.0.0 — 2025-10-19 +**Status:** Public Release (Feature Complete) + +### Major Changes +- Introduced unified cross-platform installers: + - `retroiptv_linux.sh` replaces all legacy shell installers + - `retroiptv_windows.ps1` adds native PowerShell support + - `retroiptv_rpi.sh` updated for Ubuntu 24.04.2 ARM builds +- Added Android/Fire/Google TV mode with glowing CRT-style header and TV-optimized layout +- Added `manage_users.html` for integrated user creation, management, and deletion +- Updated `app.py` for unified configuration handling, improved session persistence, and tuner logic cleanup +- Modernized UI templates: `guide.html`, `login.html`, `about.html`, `logs.html`, `change_password.html`, and `change_tuner.html` +- Refreshed `CHANGELOG.md`, `README.md`, and `ROADMAP.md` to match unified architecture + +### Removed / Consolidated +- Removed legacy install/uninstall scripts (`install.*`, `uninstall.*`, `iptv-server.service`) +- Consolidated multiple user templates (`add_user.html`, `new_user.html`, etc.) into `manage_users.html` + +### Known Limitations +- HTTPS mode remains experimental (local/internal network use recommended) +- Android TV session persistence under further testing +- Performance optimization ongoing for large EPG datasets + +### Upcoming Development +- Optional HTTPS + token authentication +- Per-user tuner assignment system +- PlutoTV / custom tuner aggregation features +- Enhanced guide refresh logic for long-running sessions + --- ## [Unreleased] @@ -32,7 +62,8 @@ This project follows [Semantic Versioning](https://semver.org/). --- -## v3.2.0 - 2025-10-11 + +## [v3.2.0] - 2025-10-11 ### Added - **Containerization & TrueNAS Deployment Support** - Added official Dockerfile and `docker-compose.yml` for cross‑platform container deployments. @@ -50,7 +81,6 @@ This project follows [Semantic Versioning](https://semver.org/). ### Fixed - Corrected GHCR tag formatting for TrueNAS (eliminated `:latest:latest` errors). - Fixed workflow permissions with explicit `packages: write` and PAT authentication. - --- ## v3.1.0 - 2025-10-09 @@ -102,8 +132,10 @@ This project follows [Semantic Versioning](https://semver.org/). - Prevented guide from displaying outdated EPG after tuner change. - Corrected case where missing XML data produced empty grid. + --- + ## [3.0.0] - 2025-10-03 ### Added - **Windows Support**: @@ -132,8 +164,6 @@ This project follows [Semantic Versioning](https://semver.org/). - Consistent logging of user agreement and installer actions. - Ensured firewall rule removal on Windows during uninstall. ---- - ## [2.3.2] - 2025-09-26 ### Added - Introduced unified **Themes submenu** (Light, Dark, AOL/CompuServe, TV Guide Magazine) across all admin and user pages. @@ -209,7 +239,9 @@ This project follows [Semantic Versioning](https://semver.org/). - Fixed alignment of tuner forms with consistent dropdowns and validation. - Ensured flash messages and logging work consistently across all tuner operations. -## [v2.0.0] 2025-09-24 + + +## [v2.0.0] – 2025-09-24 ### Added - Tuner URL validation: new validate_tuner_url() function checks XML/M3U inputs before saving. - Detects invalid/empty URLs, unresolvable hostnames, and distinguishes between public vs. private IPs. diff --git a/INSTALL.md b/INSTALL.md index 3246db8..8926330 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,9 +1,11 @@ # Installation Guide -**Version:** v3.2.0 -**Last Updated:** 2025-10-11 +**Version:** v4.0.0 +**Last Updated:** 2025-10-19 -## Requirements +--- + +## 🧰 Requirements - Python 3.10+ (Linux) / Python 3.12+ (Windows) - **Linux (Debian/Ubuntu with systemd)**, **Windows 10/11**, or **Raspberry Pi 3 / 4 / 5 (Headless OS) / Docker** - Administrative privileges: @@ -12,134 +14,103 @@ --- -## Installation +## 🛠 Installation -Clone the repository and run the installer. Choose the command based on your OS. +### 🧱 Docker (Generic Linux / macOS / Windows) -### Linux / WSL +## 🐳 Quick Docker Run + +The fastest way to launch **RetroIPTVGuide v3.2.0**: -#### One-liner ```bash -git clone https://github.com/thehack904/RetroIPTVGuide.git && cd RetroIPTVGuide && sudo chmod +x install.sh && sudo ./install.sh +docker pull ghcr.io/thehack904/retroiptvguide:latest +docker run -d --name retroiptvguide -p 5000:5000 -e TZ=America/Chicago -e SECRET_KEY=$(openssl rand -hex 32) -v $(pwd)/config:/app/config -v $(pwd)/logs:/app/logs -v $(pwd)/data:/app/data ghcr.io/thehack904/retroiptvguide:latest ``` -**What the installer does (Linux/WSL):** -- Detects Linux/WSL environment -- Ensures script is run with sudo -- Creates a system user `iptv` -- Installs into `/home/iptv/iptv-server` -- Ensures `python3-venv` is installed -- Creates Python virtual environment and installs dependencies -- Creates and enables the `iptv-server` systemd service -- Starts the service -- Logs the install to `install_YYYY-MM-DD_HH-MM-SS.log` - ---- - -### Windows 10 / 11 - -Run this one-liner from an **Administrator PowerShell** prompt: +#### Using Docker Compose -```powershell -Invoke-WebRequest https://github.com/thehack904/RetroIPTVGuide/archive/refs/heads/main.zip -OutFile RetroIPTVGuide.zip ; tar -xf RetroIPTVGuide.zip ; cd RetroIPTVGuide-main ; .\install.bat +```bash +git clone https://github.com/thehack904/RetroIPTVGuide.git +cd RetroIPTVGuide/docker +cp .env.example .env +docker compose up -d ``` -**What the installer does (Windows):** -- Bootstraps Chocolatey (if missing) -- Installs dependencies: `python`, `git`, `nssm` -- Registers Windows App Paths for `python` / `python3` -- Adds Python to Git Bash (`~/.bashrc`) -- Clones RetroIPTVGuide and runs `install.sh` under Git Bash to set up venv + requirements -- Creates an NSSM service to run `venv\Scripts\python.exe app.py` -- Opens Windows Firewall port 5000 -- Starts the RetroIPTVGuide service -- Logs the install to `install_YYYY-MM-DD_HH-MM-SS.log` - ---- - -### Raspberry Pi 3 / 4 / 5 (Headless Edition) - -#### Interactive install +### 🐧 Linux +#### Automated ```bash -curl -sSL https://raw.githubusercontent.com/thehack904/RetroIPTVGuide/refs/heads/main/retroiptv_rpi.sh | sudo bash -s install +curl -sSL https://raw.githubusercontent.com/thehack904/RetroIPTVGuide/main/retroiptv_linux.sh | sudo bash -s install --agree --yes ``` -#### Unattended / non-interactive +#### Manual ```bash -curl -sSL https://raw.githubusercontent.com/thehack904/RetroIPTVGuide/refs/heads/main/retroiptv_rpi.sh | sudo bash -s install --yes --agree +wget https://raw.githubusercontent.com/thehack904/RetroIPTVGuide/main/retroiptv_linux.sh +sudo bash ./retroiptv_linux.sh install ``` -**What the installer does (Raspberry Pi):** -- Detects Pi model (3 / 4 / 5) -- Installs required packages (`python3-venv`, `ffmpeg`, `git`, etc.) using `apt-get` -- Creates user `iptv` and installs into `/home/iptv/iptv-server` -- Configures GPU memory automatically (128 MB on Pi 3 / 256 MB on Pi 4/5) -- Sets up Python virtual environment and dependencies -- Creates systemd service `retroiptvguide` -- Performs post-install HTTP check (localhost:5000) with up-to-15 s polling -- Logs all activity to `/var/log/retroiptvguide/install-TIMESTAMP.log` -- Optionally reboots to apply GPU memory changes - -**Requirements** -- Raspberry Pi OS (Bookworm or later) -- Minimum 8 GB SD card and 1 GB RAM (512 MB swap recommended) -- SSH or console access with sudo - --- -## 🚀 Containerized Deployment - -RetroIPTVGuide v3.2.0 introduces **official Docker and TrueNAS SCALE App support**, allowing one‑click installation and persistent storage. - -### 🧱 Docker (Generic Linux / macOS / Windows) - -#### Using Docker Compose - +### 🍓 Raspberry Pi +#### Automated ```bash -git clone https://github.com/thehack904/RetroIPTVGuide.git -cd RetroIPTVGuide/docker -cp .env.example .env -docker compose up -d +curl -sSL https://raw.githubusercontent.com/thehack904/RetroIPTVGuide/main/retroiptv_rpi.sh | sudo bash -s install --agree --yes ``` - -## 🐳 Quick Docker Run - -The fastest way to launch **RetroIPTVGuide v3.2.0**: - +#### Manual ```bash -docker pull ghcr.io/thehack904/retroiptvguide:latest -docker run -d --name retroiptvguide -p 5000:5000 -e TZ=America/Chicago -e SECRET_KEY=$(openssl rand -hex 32) -v $(pwd)/config:/app/config -v $(pwd)/logs:/app/logs -v $(pwd)/data:/app/data ghcr.io/thehack904/retroiptvguide:latest +wget https://raw.githubusercontent.com/thehack904/RetroIPTVGuide/main/retroiptv_rpi.sh +sudo bash ./retroiptv_rpi.sh install ``` --- -## Access +### 🪟 Windows (PowerShell) +#### Automated +```powershell +Set-ExecutionPolicy Bypass -Scope Process -Force +Invoke-WebRequest https://raw.githubusercontent.com/thehack904/RetroIPTVGuide/main/retroiptv_windows.ps1 -OutFile retroiptv_windows.ps1 +.\retroiptv_windows.ps1 install +``` + +#### Manual +1. Download `retroiptv_windows.ps1` from the GitHub repository. +2. Open **PowerShell as Administrator**. +3. Navigate to the folder containing the script. +4. Run: + ```powershell + Set-ExecutionPolicy Bypass -Scope Process -Force + .\retroiptv_windows.ps1 install + ``` -Once installed, open your browser: +--- +## 🌐 Access +After installation: ``` -http://:5000 +🌐 RetroIPTVGuide Web Interface: http://:5000 +🔑 Default Login: admin / strongpassword123 ``` -Default login: **admin / strongpassword123** -⚠️ This is a **BETA** build for internal network use only. +> ⚠️ **Beta Notice:** +> This version is feature-complete and stable, but still displays a *Beta* disclaimer during installation for liability protection. +> Do not expose your instance directly to the public Internet. --- ## 🔄 Updating -### Linux / WSL +### 🐧 Linux ```bash -sudo -u iptv bash -H -c "cd /home/iptv/iptv-server && git fetch --all && git reset --hard origin/main" && sudo systemctl daemon-reload && sudo systemctl restart iptv-server.service +sudo retroiptv_linux.sh update ``` -### Raspberry Pi +### 🍓 Raspberry Pi ```bash -sudo -u iptv bash -H -c "cd /home/iptv/iptv-server && git fetch --all && git reset --hard origin/main" && sudo systemctl daemon-reload && sudo systemctl restart retroiptvguide.service +sudo retroiptv_rpi.sh update ``` -### Windows 10 / 11 +### 🪟 Windows +**Alignment with Linux/Pi currently on track for v4.0.1 release** ```powershell git fetch --all ; git reset --hard origin/main ; Restart-Service RetroIPTVGuide ``` @@ -151,23 +122,21 @@ docker compose pull && docker compose up -d --- -## Uninstallation +## 📘 Uninstall -### Linux / WSL +### 🐧 Linux ```bash -sudo -u iptv bash -H -c "cd /home/iptv/iptv-server" && sudo bash /home/iptv/iptv-server/uninstall.sh +sudo retroiptv_linux.sh uninstall ``` -### Raspberry Pi +### 🍓 Raspberry Pi ```bash -curl -sSL https://raw.githubusercontent.com/thehack904/RetroIPTVGuide/refs/heads/main/retroiptv_rpi.sh | sudo bash -s uninstall --yes +sudo retroiptv_rpi.sh uninstall ``` -### Windows -From an Administrator PowerShell prompt: -```powershell -.\uninstall_windows.ps1 -``` +### 🪟 Windows +1. Double-click or right-click on `retroiptv_windows.bat` and select **Run as Administrator** +2. Select **Uninstall** #### Docker ```bash @@ -178,10 +147,12 @@ docker compose down -v ⚠️ To completely remove the project, manually delete the project folder after uninstalling. --- -## License -Licensed under CC BY-NC-SA 4.0. See `LICENSE` for details. +## ⚙️ Notes +- All installers log activity with timestamps (stored in the same directory or `/var/log/retroiptvguide/`). +- Uninstallers remove services and dependencies cleanly but preserve user data unless explicitly deleted. +- These scripts are intended for **local or internal networks only**. --- -⚠️ **Initial BETA Notice** -This project is currently in **BETA** and should **not** be exposed directly to the Internet or used in production without additional hardening. +## License +Licensed under **CC BY-NC-SA 4.0**. See `LICENSE` for details. diff --git a/README.md b/README.md index 41a0afc..894e465 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# RetroIPTVGuide +# 📺 RetroIPTVGuide v4.0.0

- Version + Version GHCR @@ -15,8 +15,19 @@

+Welcome to **RetroIPTVGuide**, a lightweight self-hosted IPTV + EPG web interface inspired by classic cable TV guides — built for modern home labs, retro media setups, and Android/Fire/Google TV screens. +RetroIPTVGuide is an IPTV Web Interface inspired by 90s/2000s cable TV guides. +It is designed to work with [ErsatzTV](https://ersatztv.org/) [(GitRepo)](https://github.com/ErsatzTV/ErsatzTV/tree/main) but supports any `.m3u`, `.m3u8`, and `.xml` IPTV source. +Includes **Docker and TrueNAS SCALE deployment** for easy installation and persistence. +---- ---- +## 🚀 Features +- 🧰 Unified installers for Linux, Raspberry Pi, and Windows +- 🖥️ Android / Fire / Google TV optimized mode with animated CRT-style glow header +- 🧭 Integrated channel guide (EPG) viewer +- 👥 User management web interface (`manage_users.html`) +- ⚙️ Lightweight Flask backend, easy to self-host +- 🔐 Local-only by default — no external dependencies ## 📦 Image Information @@ -24,12 +35,7 @@ |-----------|--------|----------------|----------| | **GitHub Container Registry** | `ghcr.io/thehack904/retroiptvguide:latest` | amd64 / arm64 | Automatically via CI/CD | ---- - -RetroIPTVGuide is an IPTV Web Interface inspired by 90s/2000s cable TV guides. -It is designed to work with [ErsatzTV](https://ersatztv.org/) [(GitRepo)](https://github.com/ErsatzTV/ErsatzTV/tree/main) but supports any `.m3u`, `.m3u8`, and `.xml` IPTV source. -Now includes **Docker and TrueNAS SCALE deployment support** for easy installation and persistence. - +---- ⚠️ **Note:** This is still a BETA release. It is not recommended for direct Internet/public-facing deployments. - [Installation / Uninstall Guide](INSTALL.md) @@ -37,16 +43,16 @@ Now includes **Docker and TrueNAS SCALE deployment support** for easy installati - [Roadmap](ROADMAP.md) - [License](LICENSE) ---- -## 🚀 Containerized Deployment (v3.2.0) +---- + + +## 🛠 Installation ### 🐳 Docker ```bash docker pull ghcr.io/thehack904/retroiptvguide:latest docker run -d -p 5000:5000 ghcr.io/thehack904/retroiptvguide:latest ``` -Access the web interface at: -`http://:5000` ### 🧩 TrueNAS SCALE App - Upload the provided `retroiptvguide-3.2.0.zip` chart. @@ -54,115 +60,67 @@ Access the web interface at: - Tag: `latest` - Exposes port `5000`. ---- - -## ✨ Features (v3.2.0) +### 🐧 Linux +```bash +curl -sSL https://raw.githubusercontent.com/thehack904/RetroIPTVGuide/main/retroiptv_linux.sh | sudo bash -s install --agree --yes +``` -- Official **Docker / TrueNAS** support with persistent volumes. -- Automatic build/publish to **GHCR** via GitHub Actions. -- Same RetroIPTVGuide web interface and EPG features from v3.1.x. -- Backward compatible with Linux, Windows, and Raspberry Pi installs. +### 🍓 Raspberry Pi +```bash +curl -sSL https://raw.githubusercontent.com/thehack904/RetroIPTVGuide/main/retroiptv_rpi.sh | sudo bash -s install --agree --yes +``` +### 🪟 Windows (PowerShell) +```powershell +Set-ExecutionPolicy Bypass -Scope Process -Force +Invoke-WebRequest https://raw.githubusercontent.com/thehack904/RetroIPTVGuide/main/retroiptv_windows.ps1 -OutFile retroiptv_windows.ps1 +.\retroiptv_windows.ps1 install +``` ---- +## 🌐 Access +After installation: +``` +🌐 RetroIPTVGuide Web Interface: http://:5000 +🔑 Default Login: admin / strongpassword123 +``` -### 🔑 User Authentication -- Login/logout system with hashed passwords. -- Admin and regular user accounts. -- Password change functionality. -- Admin-only user management (add/delete users). - -### 📡 Tuner Management -- Multiple tuner support stored in `tuners.db`. -- Switch between active tuners via the web UI. -- Update `.m3u`, `.m3u8`, and `.xml` tuner URLs (persisted in DB). -- Active tuner refreshes instantly when changed — **no logout required**. -- Detection for invalid XML inputs (e.g., `.m3u` pasted into `.xml` field). - -### 📺 Guide & Playback -- Program guide rendered from XMLTV data. -- **Automatic fallback:** Channels missing XMLTV data display *“No Guide Data Available”*. -- Channel list parsed from M3U playlist. -- Video playback using HTML5 + HLS.js. -- Playback events logged with user + channel + timestamp. -- Scalable time grid and responsive EPG layout. - -### 📑 Logging -- `activity.log` records authentication events, tuner changes, playback, and admin actions. -- Admin-only **Logs page** with real-time log viewing. -- Log file size display with color coding. -- Admin-only “Clear Logs” button to truncate logs. - -### 🎨 UI Enhancements -- Unified header across all pages: Guide, Logs, Add User, Delete User, Change Password, Change Tuner, and Login. -- Active tuner display + live clock in header. -- “No Guide Data Available” placeholders styled in gray/italic with dashed border. -- **Themes submenu** with multiple options: - - Light - - Dark - - AOL/CompuServe - - TV Guide Magazine - - DirecTV (v.3.3.0) - - Comcast (v.3.3.0) -- Theme persistence stored in browser localStorage, applied instantly across all pages. -- **About Page under Settings** — shows version, Python, OS, uptime, and paths. -- **Login Page Redesign (v3.0.1)**: - - Floating centered login box with 3D shadow. - - RetroIPTVGuide logo positioned upper-right. - -### ⚙️ System -- Automatic initialization of `users.db` and `tuners.db` on first run. -- SQLite databases use WAL mode for better concurrency. -- Preloads tuner/channel/guide data from DB on startup. -- **Cross-platform installers:** - - Linux / WSL (`install.sh`) - - Windows (`install_windows.ps1`) - - Raspberry Pi (`retroiptv_rpi.sh`) -- **Uninstaller scripts for all platforms** -- **Automated version bump tool (`bump_version.py`)** — now updates both `install.sh` and `retroiptv_rpi.sh`. +> ⚠️ **Beta Notice:** +> This version is feature-complete and stable, but still displays a *Beta* disclaimer during installation for liability protection. +> Do not expose your instance directly to the public Internet. --- -## 🧩 Version History - -| Version | Date | Key Features | -|----------|------|---------------| -| **v3.3.0** | 2025-10-15 | Add Themes DirecTV / Comcast | -| **v3.2.0** | 2025-10-11 | Containerized Deployment / TrueNAS Scale App installer | -| **v3.1.0** | 2025-10-09 | Raspberry Pi installer, verified GPU setup, improved HTTP service check | -| **v3.0.1** | 2025-10-07 | EPG fallback system, tuner refresh fix, login redesign | -| **v3.0.0** | 2025-10-03 | Windows installer/uninstaller, cross-platform setup, unified UI | -| **v2.3.x** | 2025-09 | Unified theming, About page, installer logging, tuner rename/delete | -| **v2.0.0** | 2025-09 | Persistent tuners.db, user logs, unified headers | -| **v1.x.x** | 2025-08 | Initial IPTV Flask prototype | +### 🔄 Updating ---- +### 🐧 Linux +```bash +sudo retroiptv_linux.sh update --yes +``` -## 🌐 Browser Compatibility -RetroIPTVGuide is compatible with all modern browsers: +### 🍓 Raspberry Pi +```bash +sudo retroiptv_rpi.sh update --yes +``` -- Firefox -- Chrome -- Safari -- Edge +### 🪟 Windows +**Currently on track for v.4.0.1 release** --- -## 💻 Tested Devices & Operating Systems -- **Ubuntu 24.04 (desktop/server)** -- **TrueNAS SCALE (Docker)** -- **Windows 10 / 11** -- **Raspberry Pi 3B+ / 4 / 5 (Raspberry Pi OS Bookworm)** -- **macOS Monterey / Ventura** -- **iOS (mobile/tablet)** -- **Android (Samsung / Pixel)** +## 📘 Uninstall ---- +### 🐧 Linux +```bash +sudo retroiptv_linux.sh uninstall --yes +``` -## 🛠️ Installation Platforms -- Debian-based Linux (Ubuntu, Pop!\_OS, Mint) -- Windows 10/11 (via PowerShell + NSSM) -- Raspberry Pi 3 / 4 / 5 (Headless OS) -- Docker (Generic Linux / macOS / Windows) +### 🍓 Raspberry Pi +```bash +sudo retroiptv_rpi.sh uninstall --yes +``` + +### 🪟 Windows +1. Double click or Right Click on retroiptv_windows.bat and select Run As Administrator +2. Select Uninstall --- @@ -184,7 +142,6 @@ RetroIPTVGuide is compatible with all modern browsers: ![AOL Theme](docs/screenshots/AOL_Compuserve_Theme.png) --- - ## 🤝 Contributing Contributions are welcome! Here’s how you can help: @@ -197,25 +154,13 @@ All contributions will be reviewed before merging into the main branch. --- -## 🧭 Roadmap (Next Planned Features) -- [ ] `.m3u8` single-channel tuner support -- [ ] Log filtering and pagination -- [ ] Guide search/filter -- [ ] Favorites and notifications -- [ ] Responsive mobile layout -- [ ] Auto-refresh EPG schedule +## 🧭 Project Info +- **Homepage:** [GitHub – RetroIPTVGuide](https://github.com/thehack904/RetroIPTVGuide) +- **License:** CC BY-NC-SA 4.0 +- **Maintainer:** @thehack904 See [ROADMAP.md](ROADMAP.md) for full details. - ---- - -## 🧰 Maintainer Notes -- **Repo:** [RetroIPTVGuide](https://github.com/thehack904/RetroIPTVGuide) -- **Maintainer:** J.H. -- **License:** [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](https://creativecommons.org/licenses/by-nc-sa/4.0/) -- **Version:** v3.2.0 -- **Release Date:** 2025-10-11 - --- -© 2025 RetroIPTVGuide Project — *Licensed under CC BY-NC-SA 4.0. Inspired by the golden era of cable TV.* +## 💡 Tip +Combine this with **ErsatzTV** for full media channel playout and a seamless retro-TV experience! diff --git a/ROADMAP.md b/ROADMAP.md index 30bdda8..1ec7f0b 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,130 +1,128 @@ -# 📌 IPTV Flask Project — Roadmap +# 📌 RetroIPTVGuide — Roadmap This document tracks **planned upgrades** and ideas for improving the IPTV Flask server. These are **not yet implemented**, but provide a development path for future releases. +--- +# Current Version: v4.0.0 (2025-10-19) +The 4.0.0 release merges all Testing branch updates into Main, introducing unified installers, new UI templates, and Android TV optimizations. + --- ## 🔮 Feature Upgrades ### 1. Tuner Management -- [x] Add ability to **add/remove tuners** from the UI (v2.3.0). +- [x] Add ability to **add/remove tuners** from the UI (v2.3.0). - [x] Add ability to rename tuners via the UI (v2.3.0). - [ ] Support for **.m3u8 single-channel playlists** as tuner sources (planned v3.2.0). - Option A: Special-case `.m3u8` handling in parser. - Option B: Add explicit `hls` column to `tuners.db`. - [x] Validate tuner URLs (ping/check format before saving) (v2.0.0). -- [ ] Optional auto-refresh of tuner lineup on a schedule. +- [ ] Optional auto-refresh of tuner lineup on a schedule. +- [ ] Add per-user tuner assignment and default tuner preferences. 🆕 *(v4.1.x planned)* +- [ ] Introduce combined tuner builder (custom tuner aggregation). 🆕 *(v5.x.x planned)* + +--- ### 2. Logging & Monitoring - [ ] Move logs from flat file (`activity.log`) into **SQLite DB** for better querying. - [ ] Add filtering and pagination in logs view (by user, action, or date). - [ ] Add system health checks (e.g., tuner reachability, XMLTV freshness) to logs. -- [x] **Admin log management**: add button/route to clear logs (with confirmation) (v2.3.1). -- [x] Display log file size on logs page (v2.3.1) -- [x] Post-install HTTP service verification added in Pi installer (v3.1.0) +- [x] **Admin log management**: add button/route to clear logs (with confirmation) (v2.3.1). +- [x] Display log file size on logs page (v2.3.1). +- [x] Post-install HTTP service verification added in Pi installer (v3.1.0). +- [ ] Add unified “Refresh Guide” scheduler (configurable intervals). 🆕 *(v4.2.x planned)* + +--- ### 3. Guide & Playback - [ ] Add **search/filter box** to guide for channels/programs. - [ ] Add ability to set **favorites** for quick channel access. - [x] Add fallback message (“No Guide Data Available”) for channels missing EPG info (v3.0.1). - [ ] Add **reminders/notifications** for upcoming programs. +- [ ] Add EPG caching for faster guide reloads. 🆕 *(v5.x.x planned)* + +--- ### 4. User Management +- [x] Add **manage_users.html** for integrated user control panel. ✅ *(v4.0.0)* - [ ] Add role-based access control (admin, regular user, read-only). - [ ] Add **email or 2FA support** for login (optional). - [ ] Show last login time in admin panel. +- [ ] Enhance user management (roles, channel restrictions). 🆕 *(v5.x.x planned)* + +--- ### 5. UI/UX Improvements -- [x] Unified theming across all templates (Light, Dark, AOL/CompuServe, TV Guide Magazine) (v2.3.2) +- [x] Unified theming across all templates (Light, Dark, AOL/CompuServe, TV Guide Magazine) (v2.3.2). +- [x] Android / Fire / Google TV optimized mode with CRT glow header. ✅ *(v4.0.0)* +- [x] Consolidated and modernized UI templates (`guide.html`, `login.html`, `about.html`, `logs.html`, etc.). ✅ *(v4.0.0)* - [ ] Unify CSS across all templates (minimize inline styles). - [ ] Make guide responsive (mobile/tablet view). - [ ] Add dark/light theme auto-detect from browser/system. -- [ ] Frozen header Timeline to prevent scrolling with channel listing -- [x] About page under Settings menu (v2.3.1) +- [ ] Frozen header timeline to prevent scrolling with channel listing. +- [x] About page under Settings menu (v2.3.1). + +--- ### 6. Cross-platform - [x] Create installable container. +- [x] Unified Linux, Windows, and Raspberry Pi installers. ✅ *(v4.0.0)* +- [x] Windows installer via PowerShell + NSSM service (v3.0.0). +- [x] Pi installer auto-configures GPU and verifies HTTP service (v3.1.0). +- [x] Add **Windows update/uninstall parity planned**. 🆕 *(v4.1.x target)* - [ ] Create MacOS install/executable. -- [x] Create Microsoft Windows install/executable (full support via PowerShell + NSSM service) (v3.0.0) -- [x] Add uninstall_windows.ps1 and uninstall.bat (v3.0.0) -- [x] Windows installer bootstraps Chocolatey, installs Python, Git, NSSM, and configures service (v3.0.0) -- [x] Windows uninstaller cleans service, firewall rule, and optionally removes Chocolatey (v3.0.0) - - [ ] Validate/test installer fully on Windows environments -- [x] **Add Raspberry Pi headless installer (v3.1.0)** - - Detects Pi 3 / 4 / 5 models and sets GPU memory dynamically - - Installs under `/home/iptv/iptv-server` using user `iptv` - - Adds post-install HTTP verification and log summary - - Logs full install to `/var/log/retroiptvguide/install-TIMESTAMP.log` +- [x] Validate/test installers fully on all Windows environments. +- [ ] Explore TrueNAS SCALE App Catalog certification. 🆕 *(v5.x.x planned)* + +--- ### 7. New Features -- [ ] Add the ability to have an auto play video stream upon login from a specific channel (Ersatz currently) to act similar to the 90/2000's tv guide that played "Commercials" until a channel was selected. -- [ ] Option to play a known or unlisted channel when implemented on ErsatzTV for auto play video stream +- [ ] Add the ability to have an **auto-play video stream** upon login (ErsatzTV source). +- [ ] Option to play a known or unlisted channel as default auto-play source. +- [ ] Begin integration path for **PlutoTV / external IPTV services**. 🆕 *(v5.x.x)* + +--- ### 8. Planned Enhancements - [ ] Add **safety checks** in `add_tuner()`: - Prevent inserting duplicate tuner names. - - Validate XML/M3U URLs are not empty before committing to DB. -- [x] Add **GPU verification** after `raspi-config` call (v3.1.0). -- [x] Suppress `rfkill` Wi-Fi message during GPU configuration (v3.1.0). -- [x] Post-install adaptive HTTP check loop (15s poll) (v3.1.0). + - Validate XML/M3U URLs before commit. +- [x] Add **GPU verification** after `raspi-config` call (v3.1.0). +- [x] Suppress `rfkill` Wi-Fi message during GPU configuration (v3.1.0). +- [x] Post-install adaptive HTTP check loop (15s poll) (v3.1.0). +- [x] Reorganized project structure and documentation. ✅ *(v4.0.0)* --- ## ⚙️ Technical Improvements - [x] Add uninstall.sh (v2.3.0). - - [ ] Validate/test uninstall script fully on Windows environments. -- [ ] Add HTTPS support. -- [ ] Refactor tuner handling to rely only on DB (remove in-memory fallback). -- [ ] Add migrations for DB changes (via Alembic or custom script). -- [ ] Containerize app (Dockerfile + Compose for deployment). +- [ ] Validate/test uninstall script fully on Windows environments. +- [ ] Add HTTPS + optional token-based authentication. 🆕 *(v4.5.x)* +- [x] Refactor tuner handling for unified DB structure. ✅ *(v4.0.0)* +- [ ] Add migrations for DB schema changes. +- [x] Containerize app (Dockerfile + Compose for deployment). +- [x] Automated version bump tool updates all key scripts (v3.1.0). +- [ ] Add CI/CD automation for official .deb and .zip builds. 🆕 *(v5.x.x)* - [ ] Add test suite for tuner parsing, authentication, and logging. -- [x] **Automated version bump tool** (`bump_version.py`) now updates: - - `APP_VERSION` in `app.py` - - `install.sh` - - `retroiptv_rpi.sh` - - Adds new section in `CHANGELOG.md` (v3.1.0). --- -## 📅 Priority Suggestions -- Short term: finalize Docker image testing across TrueNAS and generic Linux. -- Medium term: implement `.m3u8` tuner and DB log storage (v3.3.x). -- Long term: HTTPS support and mobile UI redesign. - ---- - -## 🍓 Installer Enhancement: Kiosk vs Headless Mode (Planned) -**Target Version:** v3.2.0 -**Status:** Planned -**Effort:** Medium - -- Add an interactive mode selector to the Raspberry Pi installer. -- Headless mode → install to `/home/iptv/iptv-server` with service user `iptv`. -- Kiosk mode → install to `/opt/RetroIPTVGuide` and auto-launch Chromium in fullscreen displaying RetroIPTVGuide. -- Support command-line flags: `--mode kiosk` or `--mode headless` for non-interactive installs. -- Ensure logs and services are properly separated between modes. - ---- -## ✅ Completed (v3.2.0) -- [x] **Containerization & TrueNAS Deployment Support** - - Dockerfile and docker‑compose finalized for GHCR. - - TrueNAS SCALE App chart added for persistent deployment. - - CI/CD publishing pipeline via GitHub Actions and GHCR_PAT. - - Updated documentation (INSTALL, README, CHANGELOG). - -## ✅ Completed (v3.1.0) -- [x] **Raspberry Pi headless installer completed** (`retroiptv_rpi.sh`). -- [x] Aligned Pi installer with Debian/Windows structure. -- [x] Added logging to `/var/log/retroiptvguide/`. -- [x] Added adaptive HTTP service verification. -- [x] Added GPU verification with silent `raspi-config` call. -- [x] Added system resource check (SD card size, RAM, swap). -- [x] Integrated `--yes` and `--agree` installer flags. -- [x] Verified functionality on Pi 3 and Pi 4. +## 🍓 Installer Enhancements +- [x] Unified Linux/Windows/RPi installer architecture. ✅ *(v4.0.0)* +- [ ] Add interactive mode selector (Kiosk vs Headless). +- [ ] Add command-line flag `--mode kiosk` for non-interactive installs. +- [ ] Ensure logs/services properly isolated between modes. +- [ ] Validate update/uninstall paths on all OSes. --- -## ✅ Completed (v3.0.0) -- [x] Full Windows installer/uninstaller support with NSSM service, firewall rule, and Chocolatey integration. -- [x] Documentation updates (README.md, INSTALL.md) to reflect Windows support. +## ✅ Completed (v4.0.0) +- [x] Unified cross-platform installers (`retroiptv_linux.sh`, `retroiptv_windows.ps1`, `retroiptv_rpi.sh`) +- [x] Android/Fire/Google TV mode added with animated CRT glow +- [x] Added `manage_users.html` for full web-based user management +- [x] Modernized `guide.html`, `login.html`, `about.html`, `logs.html`, etc. +- [x] Refactored `app.py` for unified configuration + session logic +- [x] Removed legacy installers (`install.*`, `uninstall.*`, `iptv-server.service`) +- [x] Reorganized documentation (CHANGELOG, README, ROADMAP) +- [x] Release tagged as **v4.0.0** diff --git a/app.py b/app.py index 5d0a800..d40cea6 100644 --- a/app.py +++ b/app.py @@ -1,7 +1,7 @@ -APP_VERSION = "v3.3.0" +APP_VERSION = "v4.0.0" APP_RELEASE_DATE = "2025-10-11" -from flask import Flask, render_template, request, redirect, url_for, flash +from flask import Flask, render_template, request, redirect, url_for, flash, session from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user from werkzeug.security import generate_password_hash, check_password_hash import sqlite3 @@ -22,6 +22,8 @@ # ------------------- Config ------------------- app = Flask(__name__) app.secret_key = os.urandom(24) # replace with a fixed key in production +app.config['REMEMBER_COOKIE_DURATION'] = timedelta(days=30) + DATABASE = 'users.db' TUNER_DB = 'tuners.db' @@ -301,8 +303,10 @@ def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] + remember = 'remember' in request.form # ✅ new: detect Save Session checkbox user = get_user(username) if user and check_password_hash(user.password_hash, password): + login_user(user, remember=remember) # ✅ persist login if checked login_user(user) log_event(username, "Logged in") tuners = get_tuners() @@ -327,6 +331,14 @@ def logout(): logout_user() return redirect(url_for('login')) +def revoke_user_sessions(username): + # Placeholder: later this can use a session-tracking table or Redis + # For now, it clears any "remember" cookie or stored flag + session_key = f"user_session_{username}" + if session_key in session: + session.pop(session_key, None) + log_event("admin", f"Revoked sessions for {username}") + @app.route('/change_password', methods=['GET','POST']) @login_required def change_password(): @@ -400,6 +412,69 @@ def delete_user(): users = [row[0] for row in c.fetchall()] return render_template("delete_user.html", current_tuner=get_current_tuner(), users=users) +@app.route('/manage_users', methods=['GET', 'POST']) +@login_required +def manage_users(): + ua = request.headers.get('User-Agent', '').lower() + + # Detect Android / Fire / Google TV browsers + tv_patterns = ['silk', 'aft', 'android tv', 'googletv', 'mibox', 'bravia', 'shield', 'tcl', 'hisense', 'puffin', 'tv bro'] + is_tv = any(p in ua for p in tv_patterns) + + # Restrict access + if current_user.username != 'admin' or is_tv: + # Log unauthorized or TV-based attempt + log_event(current_user.username, f"Unauthorized attempt to access /manage_users from UA: {ua}") + flash("Unauthorized access.") + return redirect(url_for('guide')) + + # ---- Normal admin logic below ---- + with sqlite3.connect(DATABASE, timeout=10) as conn: + c = conn.cursor() + c.execute('SELECT username FROM users WHERE username != "admin"') + users = [row[0] for row in c.fetchall()] + + if request.method == 'POST': + action = request.form.get('action') + username = request.form.get('username') + password = request.form.get('password') + + if action == 'add': + if not username or not password: + flash("Please provide both username and password.") + else: + try: + with sqlite3.connect(DATABASE, timeout=10) as conn: + c = conn.cursor() + c.execute('INSERT INTO users (username, password) VALUES (?, ?)', + (username, generate_password_hash(password))) + conn.commit() + log_event(current_user.username, f"Added user {username}") + flash(f"✅ User '{username}' added successfully.") + except sqlite3.IntegrityError: + flash("⚠️ Username already exists.") + + elif action == 'delete': + if username == 'admin': + flash("❌ Cannot delete the admin account.") + else: + with sqlite3.connect(DATABASE, timeout=10) as conn: + c = conn.cursor() + c.execute('DELETE FROM users WHERE username=?', (username,)) + conn.commit() + log_event(current_user.username, f"Deleted user {username}") + flash(f"🗑 Deleted user '{username}'.") + + elif action == 'signout': + revoke_user_sessions(username) + log_event(current_user.username, f"Revoked sessions for {username}") + flash(f"🚪 Signed out all active logins for '{username}'.") + + return redirect(url_for('manage_users')) + + return render_template('manage_users.html', users=users, current_tuner=get_current_tuner()) + + @app.route("/about") @login_required # optional def about(): @@ -571,6 +646,7 @@ def view_logs(): log_event(current_user.username, "Accessed logs page") entries = [] log_size = 0 + if os.path.exists(LOG_PATH): log_size = os.path.getsize(LOG_PATH) with open(LOG_PATH, "r") as f: @@ -578,9 +654,15 @@ def view_logs(): parts = line.strip().split(" | ") if len(parts) == 3: user, action, timestamp = parts - entries.append((user, action, timestamp)) + log_type = "security" if any( + x in action.lower() for x in + ["unauthorized", "revoked", "failed", "denied"] + ) else "activity" + entries.append((user, action, timestamp, log_type)) else: - entries.append(("system", line.strip(), "")) + entries.append(("system", line.strip(), "", "activity")) + else: + entries = [("system", "No log file found.", "", "activity")] return render_template( "logs.html", @@ -589,22 +671,20 @@ def view_logs(): log_size=log_size ) -@app.route("/clear_logs") + + +@app.route('/clear_logs', methods=['POST']) @login_required def clear_logs(): - if current_user.username != "admin": - flash("Unauthorized: only admin can clear logs.", "error") - return redirect(url_for("view_logs")) + if current_user.username != 'admin': + flash("Unauthorized access.") + return redirect(url_for('view_logs')) - try: - # Truncate the log file instead of deleting it - with open(LOG_PATH, "w"): - pass - flash("✅ Logs cleared successfully.", "success") - except Exception as e: - flash(f"⚠️ Error clearing logs: {e}", "error") + open(LOG_PATH, "w").close() # clear the file + log_event("admin", "Cleared log file") + flash("🧹 Logs cleared successfully.") + return redirect(url_for('view_logs')) - return redirect(url_for("view_logs")) # ------------------- Constants ------------------- SCALE = 5 diff --git a/bump_version.sh b/bump_version.sh new file mode 100644 index 0000000..1a3dc93 --- /dev/null +++ b/bump_version.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash +# ──────────────────────────────────────────────────────────────── +# bump_version.sh — Version bump utility for RetroIPTVGuide +# Author: RetroIPTVGuide Dev Team +# License: CC BY-NC-SA 4.0 +# Usage: +# ./bump_version.sh 4.0.0 +# ./bump_version.sh 4.0.0 --commit +# ──────────────────────────────────────────────────────────────── + +set -euo pipefail +# macOS locale workaround for sed "illegal byte sequence" +export LC_CTYPE=C +export LANG=C + +# --- Color setup --- +RED=$(tput setaf 1) +GREEN=$(tput setaf 2) +YELLOW=$(tput setaf 3) +RESET=$(tput sgr0) + +# --- Argument parsing --- +NEW_VERSION="${1:-}" +DO_COMMIT="${2:-}" +TODAY=$(date +%Y-%m-%d) + +if [[ -z "$NEW_VERSION" ]]; then + echo "${YELLOW}Usage:${RESET} $0 [--commit]" + exit 1 +fi + +# Strip leading v +NEW_VERSION="${NEW_VERSION#v}" + +# --- File targets --- +FILES=( + "retroiptv_linux.sh" + "retroiptv_rpi.sh" + "retroiptv_windows.ps1" + "app.py" +) + +CHANGELOG="CHANGELOG.md" + +# --- Detect sed flavor --- +if sed --version >/dev/null 2>&1; then + # GNU sed + SED_INPLACE=(-i) +else + # BSD/macOS sed + SED_INPLACE=(-i "") +fi + +echo "🔢 Bumping version to v${NEW_VERSION}" +echo "───────────────────────────────────────────────" + +# --- Update app.py --- +if [[ -f "app.py" ]]; then + if grep -q '^APP_VERSION' app.py; then + sed "${SED_INPLACE[@]}" "s/^APP_VERSION.*/APP_VERSION = \"v${NEW_VERSION}\"/" app.py + else + sed "${SED_INPLACE[@]}" "1iAPP_VERSION = \"v${NEW_VERSION}\"" app.py + fi + echo "${GREEN}✔ Updated app.py${RESET}" +fi + +# --- Update version in scripts --- +for FILE in "${FILES[@]}"; do + [[ ! -f "$FILE" ]] && continue + + case "$FILE" in + *.sh) + if grep -q '^VERSION=' "$FILE"; then + sed "${SED_INPLACE[@]}" "s/^VERSION=.*/VERSION=\"${NEW_VERSION}\"/" "$FILE" + else + sed "${SED_INPLACE[@]}" "1iVERSION=\"${NEW_VERSION}\"" "$FILE" + fi + ;; + *.ps1|*.bat) + if grep -q 'VERSION' "$FILE"; then + sed "${SED_INPLACE[@]}" "s/^[\$]*VERSION.*/\$VERSION = \"${NEW_VERSION}\"/" "$FILE" + else + sed "${SED_INPLACE[@]}" "1i\$VERSION = \"${NEW_VERSION}\"" "$FILE" + fi + ;; + esac + echo "${GREEN}✔ Updated ${FILE}${RESET}" +done + +# --- Update CHANGELOG.md --- +if [[ -f "$CHANGELOG" ]]; then + TMP_FILE=$(mktemp) + awk -v ver="v${NEW_VERSION}" -v date="$TODAY" ' + /^---$/ && !inserted { + print "" + print "## " ver " - " date + print "### Added" + print "- (empty)\n" + print "### Fixed" + print "- (empty)\n" + print "---\n" + inserted=1 + } + {print} + ' "$CHANGELOG" > "$TMP_FILE" + mv "$TMP_FILE" "$CHANGELOG" + echo "${GREEN}✔ Updated CHANGELOG.md${RESET}" +fi + +# --- Optional Git commit --- +if [[ "$DO_COMMIT" == "--commit" ]]; then + git add app.py "$CHANGELOG" "${FILES[@]}" || true + if git diff --cached --quiet; then + echo "${YELLOW}⚠ No changes to commit.${RESET}" + else + git commit -m "Bump version to v${NEW_VERSION}" || true + echo "${GREEN}✔ Git commit created${RESET}" + fi +fi + +echo "───────────────────────────────────────────────" +echo "${GREEN}🎉 Version bump complete → v${NEW_VERSION}${RESET}" + diff --git a/install.bat b/install.bat deleted file mode 100644 index 938f524..0000000 --- a/install.bat +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0install_windows.ps1" diff --git a/install.sh b/install.sh deleted file mode 100644 index efdf675..0000000 --- a/install.sh +++ /dev/null @@ -1,239 +0,0 @@ -#!/bin/bash -set -e - -VERSION="3.3.0" -# RetroIPTVGuide Debian/Ubuntu Installer -# License: CC BY-NC-SA 4.0 -TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") -LOGFILE="install_${TIMESTAMP}.log" - -# Log everything to file + console -exec > >(tee -a "$LOGFILE") 2>&1 - -echo "=== RetroIPTVGuide Installer (v$VERSION) ===" -echo "Start time: $(date)" -echo "Log file: $LOGFILE" - -# --- Detect Environment --- -if grep -qi microsoft /proc/version 2>/dev/null; then - ENVIRONMENT="WSL" -elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]]; then - ENVIRONMENT="GITBASH" -elif [[ "$OSTYPE" == "linux-gnu"* ]]; then - ENVIRONMENT="LINUX" -else - ENVIRONMENT="UNKNOWN" -fi - -echo "Detected environment: $ENVIRONMENT" - -windows_install() { -#!/usr/bin/env bash -# install.sh - Installer for RetroIPTVGuide -# Detects environment, creates venv, ensures pip is up-to-date only if needed. - -set -e - -echo "=== RetroIPTVGuide Installer ===" - -# Detect environment -if grep -qi "microsoft" /proc/version 2>/dev/null; then - ENVIRONMENT="WSL" -elif [ -n "$WINDIR" ] && [ -x "/bin/bash" ] && uname -s | grep -qi "mingw"; then - ENVIRONMENT="GITBASH" -else - ENVIRONMENT="LINUX" -fi - -echo "Environment detected: $ENVIRONMENT" - -# Ensure Python is available -if ! command -v python3 >/dev/null 2>&1 && ! command -v python >/dev/null 2>&1; then - echo "Python is not installed. Please install Python 3 and rerun." - exit 1 -fi - -# Pick python executable -if command -v python3 >/dev/null 2>&1; then - PY=python3 -else - PY=python -fi - -# Create virtual environment -if [ "$ENVIRONMENT" = "GITBASH" ]; then - $PY -m venv venv - PYTHON_BIN="$PWD/venv/Scripts/python.exe" -else - $PY -m venv venv - PYTHON_BIN="$PWD/venv/bin/python" -fi - -# Upgrade pip only if needed -CURRENT_PIP=$($PYTHON_BIN -m pip --version | awk '{print $2}') -LATEST_PIP=$(curl -s https://pypi.org/pypi/pip/json | grep -oP '"version":\s*"\K[0-9.]+' | head -1) - -if [ "$CURRENT_PIP" != "$LATEST_PIP" ] && [ -n "$LATEST_PIP" ]; then - echo "Upgrading pip from $CURRENT_PIP to $LATEST_PIP..." - $PYTHON_BIN -m pip install --upgrade pip -else - echo "pip is already up-to-date ($CURRENT_PIP)" -fi - -# Install requirements -$PYTHON_BIN -m pip install -r requirements.txt - -} - - -linux_install(){ -# Ensure script is run with sudo on Linux/WSL -if [[ "$ENVIRONMENT" == "LINUX" || "$ENVIRONMENT" == "WSL" ]]; then - if [[ $EUID -ne 0 ]]; then - echo "This script must be run with sudo." - exit 1 - fi -fi - -echo "" -echo "============================================================" -echo " RetroIPTVGuide Installer Agreement " -echo "============================================================" -echo "" -echo "This installer will perform the following actions:" -echo " - Detect whether you are running on Linux, WSL, or Git Bash" -echo " - On Linux/WSL:" -echo " * Ensure the script is run with sudo" -echo " * Create dedicated system user 'iptv' (if not already present)" -echo " * Ensure python3-venv package is installed" -echo " * Copy project files into /home/iptv/iptv-server" -echo " * Create and configure a Python virtual environment" -echo " * Upgrade pip and install requirements" -echo " * Create and enable the iptv-server systemd service" -echo " * Start the iptv-server service" -echo " - On Git Bash (Windows):" -echo " * Verify Python is installed (via bootstrap)" -echo " * Create and configure a Python virtual environment" -echo " * Upgrade pip if outdated, then install requirements" -echo " * Virtual environment setup complete (Windows service manager handles app start)" -echo "" -echo "By continuing, you acknowledge and agree that:" -echo " - This software should ONLY be run on internal networks." -echo " - It must NOT be exposed to the public Internet." -echo " - You accept all risks; the author provides NO WARRANTY." -echo " - The author is NOT responsible for any damage, data loss," -echo " or security vulnerabilities created by this installation." -echo "" -read -p "Do you agree to these terms? (yes/no): " agreement - -if [ "$agreement" != "yes" ]; then - echo "Installation aborted by user." - exit 1 -fi - -# Variables -APP_USER="iptv" -APP_HOME="/home/$APP_USER" -APP_DIR="$APP_HOME/iptv-server" -SERVICE_FILE="/etc/systemd/system/iptv-server.service" - -# --- Linux/WSL Install Path --- -if [[ "$ENVIRONMENT" == "LINUX" || "$ENVIRONMENT" == "WSL" ]]; then - # Create system user if not exists - if id "$APP_USER" &>/dev/null; then - echo "User $APP_USER already exists." - else - echo "Creating iptv system user..." - adduser --system --home "$APP_HOME" --group "$APP_USER" - fi - - # Ensure python3-venv is installed - echo "Checking for python3-venv..." - if ! dpkg -s python3-venv >/dev/null 2>&1; then - echo "python3-venv not found. Installing..." - apt-get update - apt-get install -y python3-venv - else - echo "python3-venv is already installed." - fi - - # Create app directory - mkdir -p "$APP_DIR" - chown -R $APP_USER:$APP_USER "$APP_HOME" - - # Copy project files - echo "Copying project files..." - rsync -a --exclude 'venv' ./ "$APP_DIR/" - chown -R $APP_USER:$APP_USER "$APP_DIR" - - # Setup virtual environment - cd "$APP_DIR" - echo "Setting up Python virtual environment..." - sudo -u $APP_USER python3 -m venv venv - - echo "Upgrading pip..." - sudo -u $APP_USER $APP_DIR/venv/bin/pip install --upgrade pip - - echo "Installing requirements..." - sudo -u $APP_USER $APP_DIR/venv/bin/pip install -r "$APP_DIR/requirements.txt" - - # Create systemd service - echo "Creating systemd service..." - cat > "$SERVICE_FILE" </dev/null 2>&1; then - echo "Error: Python not found in PATH. Please install Python 3 manually." - exit 1 - fi - echo "Setting up Python virtual environment..." - python -m venv venv - source venv/Scripts/activate - echo "Upgrading pip..." - pip install --upgrade pip - echo "Installing requirements..." - pip install -r requirements.txt - echo "Starting RetroIPTVGuide in development mode..." - python app.py & -else - echo "Unsupported environment: $OSTYPE" - exit 1 -fi - -echo "" -echo "Installation complete!" -echo "End time: $(date)" -echo "Access the server in your browser at: http://:5000" -echo "Default login: admin / strongpassword123" -echo "NOTE: This is a **BETA build**. Do not expose it directly to the public internet." -echo "" -} - -if [ "$ENVIRONMENT" = "GITBASH" ]; then - windows_install -else - linux_install -fi diff --git a/install_windows.ps1 b/install_windows.ps1 deleted file mode 100644 index d517c07..0000000 --- a/install_windows.ps1 +++ /dev/null @@ -1,320 +0,0 @@ -$VERSION = "3.3.0" -<# -RetroIPTVGuide Windows Installer -Clean version with only prerequisite checks and service setup -#> - - -# === Start Transcript === -$logDir = "$PSScriptRoot\logs" -if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Force -Path $logDir | Out-Null } -$logFile = Join-Path $logDir ("install_{0:yyyyMMdd_HHmmss}.log" -f (Get-Date)) -Start-Transcript -Path $logFile -Append -Write-Host "=== RetroIPTVGuide Windows Installer ===" -ForegroundColor Cyan -Write-Host "Log file: $logFile" -ForegroundColor Gray - -# --- Ensure Admin Privileges --- -# Elevation check -$IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) -if (-not $IsAdmin) { - Write-Host "Re-launching with Administrator privileges..." -ForegroundColor Yellow - Stop-Transcript - Start-Process -FilePath "powershell.exe" -Verb RunAs -ArgumentList "-ExecutionPolicy Bypass -File `"$PSCommandPath`"" - exit -} - -Write-Host "" -Write-Host "============================================================" -ForegroundColor Yellow -Write-Host " RetroIPTVGuide Installer Agreement " -ForegroundColor Cyan -Write-Host "============================================================" -ForegroundColor Yellow -Write-Host "" -Write-Host "This installer will perform the following actions:" -ForegroundColor White -Write-Output " - Bootstraps / Installs Chocolatey" -Write-Output " - Installs dependencies: python, git, nssm" -Write-Output " - Registers Windows App Paths for python/python3" -Write-Output " - Adds Python to Git Bash (~/.bashrc)" -Write-Output " - Clones RetroIPTVGuide into the same folder as the installer" -Write-Output " - Runs install.sh via Git Bash" -Write-Output " - Creates an NSSM service to run venv\\Scripts\\python.exe app.py" -Write-Output " - Open Windows Firewall port 5000 for RetroIPTVGuide Service" -Write-Output " - Starts the RetroIPTVGuide service" -Write-Host "" -Write-Host "By continuing, you acknowledge and agree that:" -ForegroundColor White -Write-Output " - This software should ONLY be run on internal networks." -Write-Output " - It must NOT be exposed to the public Internet." -Write-Output " - You accept all risks; the author provides NO WARRANTY." -Write-Output " - The author is NOT responsible for any damage, data loss," -Write-Output " or security vulnerabilities created by this installation." -Write-Host "" -Write-Host "Do you agree to these terms? (yes/no)" -ForegroundColor Yellow - -$agreement = Read-Host "Type yes or no" -Write-Output "User response to agreement: $agreement" - -if ($agreement.ToLower() -ne "yes") { - Write-Host "Installation aborted by user." -ForegroundColor Red - Stop-Transcript - exit 1 -} - -# --- Chocolatey --- -Write-Host "Checking for Chocolatey..." -ForegroundColor Cyan -$choco = Get-Command choco -ErrorAction SilentlyContinue -if (-not $choco) { - Write-Host "Installing Chocolatey..." -ForegroundColor Yellow - Set-ExecutionPolicy Bypass -Scope Process -Force - [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 - Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") -} - -# --- Python --- -Write-Host "Checking for Python..." -ForegroundColor Cyan -if (choco list | Select-String -Pattern "^python3 ") { - Write-Host "Python3 is installed" -} else { - Write-Host "Python3 not found, installing..." - choco install python3 -y -} - -# --- Fix Python alias stubs from Microsoft Store --- -$aliases = @( - "$env:LOCALAPPDATA\Microsoft\WindowsApps\python.exe", - "$env:LOCALAPPDATA\Microsoft\WindowsApps\python3.exe", - "$env:LOCALAPPDATA\Microsoft\WindowsApps\python3.*.exe" -) - -foreach ($alias in $aliases) { - if (Test-Path $alias) { - try { - Remove-Item $alias -Force - Write-Host "Removed Microsoft Store alias: $alias" -ForegroundColor Yellow - } catch { - Write-Warning "Failed to remove alias ${alias}: $_" - } - } -} - -# --- Ensure Chocolatey shims directory comes first in PATH --- -$chocoBin = "C:\ProgramData\chocolatey\bin" -$currentPath = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") - -if (-not ($currentPath -split ";" | ForEach-Object { $_.Trim() } | Where-Object { $_ -eq $chocoBin })) { - $newPath = "$chocoBin;$currentPath" - [System.Environment]::SetEnvironmentVariable("PATH", $newPath, "Machine") - Write-Host "Updated PATH to prioritize Chocolatey bin: $chocoBin" -ForegroundColor Green -} else { - Write-Host "Chocolatey bin already in PATH." -ForegroundColor Green -} - -# --- Add registry App Paths aliases for python/python3 --- -try { - $pythonExe = (Get-Command python.exe -ErrorAction SilentlyContinue).Source - if (-not $pythonExe) { - $pythonExe = "C:\Python313\python.exe" # fallback if not resolved via PATH - } - - if (Test-Path $pythonExe) { - New-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\App Paths\python.exe" -Force | Out-Null - Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\App Paths\python.exe" -Name "(Default)" -Value $pythonExe - - New-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\App Paths\python3.exe" -Force | Out-Null - Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\App Paths\python3.exe" -Name "(Default)" -Value $pythonExe - - Write-Host "Registered App Path aliases: python, python3 -> $pythonExe" -ForegroundColor Green - } else { - Write-Warning "Python executable not found for alias registration." - } -} catch { - Write-Warning "Failed to register python/python3 aliases in App Paths: $_" -} - - -# --- Ensure Chocolatey is installed --- -if (-not (Get-Command choco.exe -ErrorAction SilentlyContinue)) { - Write-Host "Installing Chocolatey..." -ForegroundColor Cyan - Set-ExecutionPolicy Bypass -Scope Process -Force - [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 - Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) -} else { - Write-Host "Chocolatey already installed." -ForegroundColor Green -} - -# --- Ensure dependencies --- -$packages = @("python3", "nssm", "git") - -foreach ($pkg in $packages) { - $installed = choco list --limit-output | Select-String -Pattern "^$pkg " - if (-not $installed) { - Write-Host "Installing $pkg..." -ForegroundColor Cyan - choco install $pkg -y | Out-Null - } else { - Write-Host "$pkg already installed." -ForegroundColor Green - } -} - -Write-Host "All dependencies installed." -ForegroundColor Green - -# --- Ensure Python aliases point to correct installed version --- -try { - $pythonExePath = (Get-Command python3.exe -ErrorAction SilentlyContinue).Source - if (-not $pythonExePath) { - $pythonExePath = (Get-Command python.exe -ErrorAction SilentlyContinue).Source - } - - if ($pythonExePath) { - Write-Host "Found Python executable at $pythonExePath" -ForegroundColor Cyan - - # Persist App Path so Windows resolves python/python3 correctly - $aliases = @("python.exe", "python3.exe") - foreach ($alias in $aliases) { - $regPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\App Paths\$alias" - if (-not (Test-Path $regPath)) { - New-Item -Path $regPath -Force | Out-Null - } - Set-ItemProperty -Path $regPath -Name "(Default)" -Value $pythonExePath - Set-ItemProperty -Path $regPath -Name "Path" -Value (Split-Path $pythonExePath) - } - - Write-Host "Registered App Path aliases: python, python3 -> $pythonExePath" -ForegroundColor Green - } else { - Write-Warning "Python executable not found in PATH. Aliases not created." - } -} catch { - Write-Warning "Failed to configure Python aliases: $_" -} - -# --- Ensure Python aliases point to correct installed version --- -try { - # Look up Python installation folder from Chocolatey - $pythonPkg = choco list | Select-String -Pattern "^python3 " - if ($pythonPkg) { - # Most choco python3 installs under C:\Python3xx - $pythonDir = Get-ChildItem "C:\Python*" -Directory | Sort-Object LastWriteTime -Descending | Select-Object -First 1 - $pythonExePath = Join-Path $pythonDir.FullName "python.exe" - - if (Test-Path $pythonExePath) { - Write-Host "Found Python executable at $pythonExePath" -ForegroundColor Cyan - - # Persist App Path so Windows resolves python/python3 correctly - $aliases = @("python.exe", "python3.exe") - foreach ($alias in $aliases) { - $regPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\App Paths\$alias" - if (-not (Test-Path $regPath)) { - New-Item -Path $regPath -Force | Out-Null - } - Set-ItemProperty -Path $regPath -Name "(Default)" -Value $pythonExePath - Set-ItemProperty -Path $regPath -Name "Path" -Value $pythonDir.FullName - } - - Write-Host "Registered App Path aliases: python, python3 -> $pythonExePath" -ForegroundColor Green - - # --- Add Python to Git Bash PATH via ~/.bashrc --- - $gitBashHome = Join-Path $env:USERPROFILE ".bashrc" - $bashrcLine = "export PATH=""/c/$($pythonDir.Name):/c/$($pythonDir.Name)/Scripts:`$PATH""" - - if (Test-Path $gitBashHome) { - if (-not (Select-String -Path $gitBashHome -Pattern $pythonDir.Name -Quiet)) { - Add-Content -Path $gitBashHome -Value $bashrcLine - Write-Host "Added Python to Git Bash PATH in .bashrc" -ForegroundColor Yellow - } - } else { - Set-Content -Path $gitBashHome -Value $bashrcLine - Write-Host "Created .bashrc and added Python PATH for Git Bash" -ForegroundColor Yellow - } - - } else { - Write-Warning "Python executable not found in expected location." - } - } else { - Write-Warning "Python not installed by Chocolatey, skipping alias setup." - } -} catch { - Write-Warning "Failed to configure Python aliases: $_" -} - - - - - -# --- Clone RetroIPTVGuide --- -$installDir = "$PSScriptRoot\RetroIPTVGuide" -if (Test-Path $installDir) { Remove-Item -Recurse -Force $installDir } -# Resolve Git path -$gitExe = (Get-Command git.exe -ErrorAction SilentlyContinue).Source -if (-not $gitExe) { $gitExe = "C:\Program Files\Git\bin\git.exe" } -if (-not (Test-Path $gitExe)) { $gitExe = "C:\Program Files (x86)\Git\bin\git.exe" } - -if (-not (Test-Path $gitExe)) { - Write-Error "Git executable not found even after installation. Please verify Git installation." - exit 1 -} - -# Clone the repository -#& $gitExe clone -b windows https://github.com/thehack904/RetroIPTVGuide.git $installDir - -# --- Run install.sh using Git Bash --- -try { - $gitBash = "C:\Program Files\Git\bin\bash.exe" - #$repoDir = Join-Path $PSScriptRoot "RetroIPTVGuide" - $repoDir = $PSScriptRoot - - if (Test-Path $gitBash -PathType Leaf) { - if (Test-Path (Join-Path $repoDir "install.sh")) { - Write-Host "Running install.sh with Git Bash..." -ForegroundColor Cyan - & "$gitBash" --login -i -c "cd '$repoDir' && chmod +x install.sh && ./install.sh" - } else { - Write-Warning "install.sh not found in $repoDir" - } - } else { - Write-Warning "Git Bash not found at $gitBash. Skipping install.sh" - } -} catch { - Write-Warning "Failed to run install.sh in Git Bash: $_" -} - -# --- PATCH: Open firewall port for RetroIPTVGuide --- -Write-Host "Opening Windows Firewall port 5000 for RetroIPTVGuide..." -ForegroundColor Yellow -netsh advfirewall firewall add rule name="RetroIPTVGuide" dir=in action=allow protocol=TCP localport=5000 | Out-Null - -# --- Configure NSSM service for RetroIPTVGuide --- -try { - $nssm = "C:\ProgramData\chocolatey\bin\nssm.exe" - #$repoDir = Join-Path $PSScriptRoot "RetroIPTVGuide" - $repoDir = $PSScriptRoot - $venvPython = Join-Path $repoDir "venv\Scripts\python.exe" - $appPy = Join-Path $repoDir "app.py" - - if ((Test-Path $nssm -PathType Leaf) -and (Test-Path $venvPython) -and (Test-Path $appPy)) { - Write-Host "Setting up NSSM service for RetroIPTVGuide..." -ForegroundColor Cyan - - # Install the service to directly run venv python + app.py - & $nssm install RetroIPTVGuide $venvPython $appPy - - # Set service parameters - & $nssm set RetroIPTVGuide Start SERVICE_AUTO_START - & $nssm set RetroIPTVGuide AppDirectory $repoDir - - Write-Host "NSSM service 'RetroIPTVGuide' installed successfully." -ForegroundColor Green - - # Start the service right away - Start-Service RetroIPTVGuide - Write-Host "Service 'RetroIPTVGuide' started." -ForegroundColor Green - } else { - Write-Warning "NSSM, venv python, or app.py not found. Could not create service." - } -} catch { - Write-Warning "Failed to configure NSSM service: $_" -} - -echo "" -Write-Host "Installation complete!" -ForegroundColor Cyan -echo "End time: $(date)" -echo "Access the server in your browser at: http://:5000" -echo "Default login: admin / strongpassword123" -echo "NOTE: This is a **BETA build**. Do not expose it directly to the public internet." -echo "" - -# === Done === -Stop-Transcript -pause diff --git a/iptv-server.service b/iptv-server.service deleted file mode 100644 index 3ef6234..0000000 --- a/iptv-server.service +++ /dev/null @@ -1,12 +0,0 @@ -[Unit] -Description=IPTV Flask Server -After=network.target - -[Service] -User=iptv -WorkingDirectory=/home/iptv/iptv-server -ExecStart=/home/iptv/iptv-server/venv/bin/python app.py -Restart=always - -[Install] -WantedBy=multi-user.target diff --git a/retroiptv_linux.sh b/retroiptv_linux.sh new file mode 100644 index 0000000..d2ca44d --- /dev/null +++ b/retroiptv_linux.sh @@ -0,0 +1,281 @@ +#!/usr/bin/env bash +# retroiptv_linux.sh — Unified installer/updater/uninstaller for RetroIPTVGuide (Linux only) +# License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) + +set -euo pipefail +VERSION="4.0.0" +TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") +LOGFILE="retroiptv_${TIMESTAMP}.log" +exec > >(tee -a "$LOGFILE") 2>&1 + +echo "" +echo "==============================" +echo "---* USING TESTING BRANCH *---" +echo "==============================" +echo "" + +# --- Banner --- +cat <<'EOF' +░█████████ ░██ ░██████░█████████ ░██████████░██ ░██ ░██████ ░██ ░██ +░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ +░██ ░██ ░███████ ░████████ ░██░████ ░███████ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░████████ ░███████ +░█████████ ░██ ░██ ░██ ░███ ░██ ░██ ░██ ░█████████ ░██ ░██ ░██ ░██ █████ ░██ ░██ ░██░██ ░██ ░██ ░██ +░██ ░██ ░█████████ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ██ ░██ ░██ ░██░██ ░██ ░█████████ +░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██░██ ░██ ░███ ░██ ░███ ░██░██ ░███ ░██ +░██ ░██ ░███████ ░████ ░██ ░███████ ░██████░██ ░██ ░███ ░█████░█ ░█████░██ ░██ ░█████░██ ░███████ + +EOF +printf "===========================================================================\n" +echo " RetroIPTVGuide | Linux Edition (Headless)" +printf "===========================================================================\n\n" + +ACTION="${1:-}"; shift || true +AGREE_TERMS=false; AUTO_YES=false +for arg in "$@"; do + case "$arg" in + --agree|-a) AGREE_TERMS=true ;; + --yes|-y) AUTO_YES=true ;; + esac +done +[[ $(id -u) -ne 0 ]] && { echo "Run as root (sudo)."; exit 1; } + +APP_USER="iptv"; APP_HOME="/home/$APP_USER"; APP_DIR="" +SERVICE_NAME="iptv-server"; SYSTEMD_FILE="/etc/systemd/system/${SERVICE_NAME}.service" +LOG_DIR_LINUX="/var/log/iptv" + +# --- OS detection ------------------------------------------------------------ +DISTRO_ID="" +[[ -f /etc/os-release ]] && . /etc/os-release && DISTRO_ID=${ID,,} + +case "$DISTRO_ID" in + ubuntu|debian|raspbian) + PKG_MANAGER="apt"; PKG_INSTALL="apt-get install -y"; PKG_UPDATE="apt-get update -y"; APP_DIR_DEFAULT="$APP_HOME/iptv-server" ;; + rhel|centos|rocky|almalinux|fedora) + PKG_MANAGER=$(command -v dnf >/dev/null 2>&1 && echo dnf || echo yum) + PKG_INSTALL="$PKG_MANAGER install -y"; PKG_UPDATE="$PKG_MANAGER -y makecache && $PKG_MANAGER upgrade -y || true" + APP_DIR_DEFAULT="/opt/retroiptvguide" ;; + *) PKG_MANAGER=$(command -v apt-get >/dev/null 2>&1 && echo apt || echo dnf) + PKG_INSTALL="$PKG_MANAGER install -y"; PKG_UPDATE="$PKG_MANAGER -y makecache || true" + APP_DIR_DEFAULT="/opt/retroiptvguide" ;; +esac +APP_DIR="$APP_DIR_DEFAULT" + +# --- Utility Functions ------------------------------------------------------- +usage(){ echo "Usage: sudo $0 [install|update|uninstall] [--agree|-a] [--yes|-y]"; } + +agree_terms() { + # Skip if user pre-agreed + if [[ "$AGREE_TERMS" == true ]]; then + echo "User pre-agreed to license terms via flag (--agree)." + return + fi + + echo "" + echo "============================================================" + echo " RetroIPTVGuide Installer Agreement " + echo "============================================================" + echo "" + echo "This installer will perform the following actions:" + echo " - Detect whether you are running on Linux, WSL, or Git Bash" + echo " - On Linux/WSL:" + echo " * Ensure the script is run with sudo" + echo " * Create dedicated system user 'iptv' (if not already present)" + echo " * Ensure python3-venv package is installed" + echo " * Copy project files into /home/iptv/iptv-server (Debian/Ubuntu)" + echo " * Copy project files into /opt/retroiptvguide (Fedora/RHEL)" + echo " * Create and configure a Python virtual environment" + echo " * Upgrade pip and install requirements" + echo " * Create and enable the iptv-server systemd service" + echo " * Start the iptv-server service" + echo "" + echo "By continuing, you acknowledge and agree that:" + echo " - This software should ONLY be run on internal networks." + echo " - It must NOT be exposed to the public Internet." + echo " - You accept all risks; the author provides NO WARRANTY." + echo " - The author is NOT responsible for any damage, data loss," + echo " or security vulnerabilities created by this installation." + echo "" + read -rp "Do you agree to these terms? (yes/no): " agreement + + if [[ "$agreement" != "yes" ]]; then + echo "Installation aborted by user." + exit 1 + fi +} + +ensure_packages(){ + echo "Installing base packages..." + local pkgs=(git curl wget rsync python3 python3-pip unzip) + [[ "$PKG_MANAGER" == apt ]] && pkgs+=(python3-venv sqlite3) || pkgs+=(sqlite) + [[ "$PKG_MANAGER" =~ dnf|yum ]] && pkgs+=(policycoreutils-python-utils firewalld) + eval "$PKG_UPDATE"; eval "$PKG_INSTALL ${pkgs[*]}" +} + +ensure_user(){ + echo "Ensuring system user..." + NOLOGIN=$(command -v nologin 2>/dev/null || echo /usr/sbin/nologin) + getent group "$APP_USER" >/dev/null || groupadd --system "$APP_USER" + if ! id "$APP_USER" >/dev/null 2>&1; then + useradd -r -m -d "$APP_HOME" -s "$NOLOGIN" -g "$APP_USER" "$APP_USER" + fi + chmod 755 "$APP_HOME" || true +} + +clone_or_stage_project(){ + mkdir -p "$APP_DIR"; chown -R "$APP_USER":"$APP_USER" "$APP_DIR" + TMP="/tmp/retroiptvguide"; rm -rf "$TMP" + git clone --depth 1 -b dev https://github.com/thehack904/RetroIPTVGuide.git "$TMP" + rsync -a --delete --exclude 'venv' "$TMP/" "$APP_DIR/" + chown -R "$APP_USER":"$APP_USER" "$APP_DIR" +} + +make_venv_and_install(){ + echo "Setting up virtualenv..." + # Fedora/RHEL may need ensurepip to activate venv properly; Debian doesn't + if [[ ! "$DISTRO_ID" =~ (debian|ubuntu|raspbian) ]]; then + sudo -u "$APP_USER" python3 -m ensurepip --upgrade 2>/dev/null || true + fi + sudo -u "$APP_USER" python3 -m venv "$APP_DIR/venv" || \ + { sudo -u "$APP_USER" python3 -m pip install --user virtualenv; sudo -u "$APP_USER" "$APP_HOME/.local/bin/virtualenv" "$APP_DIR/venv"; } + sudo -u "$APP_USER" "$APP_DIR/venv/bin/pip" install --upgrade pip + sudo -u "$APP_USER" "$APP_DIR/venv/bin/pip" install -r "$APP_DIR/requirements.txt" +} + +write_systemd_service(){ + echo "Creating systemd service..." + local PYEXEC="$APP_DIR/venv/bin/python"; [[ -x "$APP_DIR/venv/bin/python3" ]] && PYEXEC="$APP_DIR/venv/bin/python3" + cat >"$SYSTEMD_FILE"</dev/null 2>&1; then + semanage port -a -t http_port_t -p tcp 5000 2>/dev/null || semanage port -m -t http_port_t -p tcp 5000 + fi +} + +start_and_verify(){ + systemctl daemon-reload + systemctl enable --now "$SERVICE_NAME" + sleep 3 + if systemctl is-active --quiet "$SERVICE_NAME"; then + echo "✅ Service active." + else + echo "❌ Service failed. See: sudo journalctl -u $SERVICE_NAME" + fi +} + +install_linux(){ + agree_terms + ensure_packages; ensure_user; clone_or_stage_project; make_venv_and_install; write_systemd_service + rhel_firewall_selinux; start_and_verify + echo "Installed to: $APP_DIR" + echo "End time: $(date)" + echo "Access at: http://$(hostname -I | awk '{print $1}'):5000" + echo "Default login: admin / strongpassword123" + echo "NOTE: This is a **BETA build**. Do not expose it directly to the public internet." + echo "Installation complete!" +} + +update_linux(){ + echo "Updating app..." + sudo -u "$APP_USER" bash -c "cd '$APP_DIR' && git fetch --all && git reset --hard origin/main" + systemctl daemon-reload; systemctl restart "$SERVICE_NAME" + echo "✅ Updated and restarted." +} + +uninstall_linux(){ + echo "Stopping and disabling service..." + systemctl stop "$SERVICE_NAME" 2>/dev/null || true + systemctl disable "$SERVICE_NAME" 2>/dev/null || true + [[ -f "$SYSTEMD_FILE" ]] && rm -f "$SYSTEMD_FILE" && systemctl daemon-reload + + echo "Removing files..." + rm -rf "$LOG_DIR_LINUX" 2>/dev/null || true + [[ -d "/opt/retroiptvguide" ]] && { echo "Removing /opt/retroiptvguide ..."; rm -rf /opt/retroiptvguide; } + [[ -d "$APP_HOME/iptv-server" ]] && { echo "Removing $APP_HOME/iptv-server ..."; rm -rf "$APP_HOME/iptv-server"; } + + echo "Reverting firewall and SELinux changes (if applicable)..." + + # --- Firewalld --- + if command -v firewall-cmd >/dev/null 2>&1 && systemctl is-active --quiet firewalld; then + echo " - Removing TCP port 5000 rule from firewalld" + firewall-cmd --permanent --remove-port=5000/tcp 2>/dev/null || true + firewall-cmd --reload 2>/dev/null || true + fi + + # --- UFW --- + if command -v ufw >/dev/null 2>&1; then + if ufw status | grep -q "5000/tcp"; then + echo " - Removing TCP port 5000 rule from UFW" + ufw delete allow 5000/tcp >/dev/null 2>&1 || true + fi + fi + + # --- SELinux --- + if command -v semanage >/dev/null 2>&1; then + echo " - Removing SELinux http_port_t mapping for TCP/5000" + semanage port -d -t http_port_t -p tcp 5000 2>/dev/null || true + fi + + echo "Removing user/group..." + +# Stop processes that may still run as iptv +pkill -u "$APP_USER" 2>/dev/null || true + +# Remove system user and home directory if it exists +if id "$APP_USER" &>/dev/null; then + echo " - Deleting user '$APP_USER' and home directory" + userdel -r "$APP_USER" 2>/dev/null || true +else + echo " - User '$APP_USER' not found, skipping" +fi + +# Remove group if it still exists +if getent group "$APP_USER" >/dev/null 2>&1; then + echo " - Deleting group '$APP_USER'" + groupdel "$APP_USER" 2>/dev/null || true +fi + + + echo "" + echo "============================================================" + echo " Uninstallation Complete " + echo "============================================================" + echo "Removed:" + echo " - Systemd service and unit file" + echo " - Application directories (/opt/retroiptvguide or /home/iptv/iptv-server)" + echo " - Firewall rules (firewalld/UFW) for TCP 5000" + echo " - SELinux port context (if previously set)" + echo " - User and group 'iptv'" + echo "" + echo "✅ Uninstall complete. Full log saved to $LOGFILE." +} + + +case "$ACTION" in + install) install_linux ;; + update) update_linux ;; + uninstall) uninstall_linux ;; + -h|--help|help|"") usage ;; + *) usage ;; +esac +echo "End time: $(date)" diff --git a/retroiptv_rpi.sh b/retroiptv_rpi.sh index 6bca85a..6546bd3 100644 --- a/retroiptv_rpi.sh +++ b/retroiptv_rpi.sh @@ -1,5 +1,5 @@ #!/bin/bash -VERSION="3.3.0" +VERSION="4.0.0" # RetroIPTVGuide Raspberry Pi Installer (Headless, Pi3/4/5) # Installs to /home/iptv/iptv-server for consistency with Debian/Windows # Logs to /var/log/retroiptvguide/install-YYYYMMDD-HHMMSS.log diff --git a/retroiptv_windows.bat b/retroiptv_windows.bat new file mode 100644 index 0000000..3286aa9 --- /dev/null +++ b/retroiptv_windows.bat @@ -0,0 +1,3 @@ +@echo off +mode con: cols=160 lines=50 +powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0retroiptv_windows.ps1" \ No newline at end of file diff --git a/retroiptv_windows.ps1 b/retroiptv_windows.ps1 new file mode 100644 index 0000000..4613cfe --- /dev/null +++ b/retroiptv_windows.ps1 @@ -0,0 +1,518 @@ +<# +RetroIPTVGuide Windows Installer/Uninstaller +Filename: retroiptv_windows.ps1 +Version: 4.0.0 + +License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International +https://creativecommons.org/licenses/by-nc-sa/4.0/ + +Usage: + .\retroiptv_windows.ps1 install [--yes|-y] [--agree|-a] + .\retroiptv_windows.ps1 uninstall [--yes|-y] + +This script: + - Auto-installs Chocolatey (if missing), then Python, Git, NSSM + - Creates a Python venv and installs requirements.txt + - Registers and starts a Windows service via NSSM named "RetroIPTVGuide" + - Verifies service status and checks the web UI on http://127.0.0.1:5000 + - Uninstall mode stops/deletes the service and cleans local files (with confirmation) + +#> + +param( + [Parameter(Mandatory=$false)] + [ValidateSet('install','uninstall')] + [string] $Action, + + [switch] $yes, + [switch] $y, + [switch] $agree, + [switch] $a +) + +# --- Elevation check with confirmation --- +if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + Write-Host "" + Write-Host "[ELEVATION] This installer needs administrator rights to continue." -ForegroundColor Yellow + $resp = Read-Host "Do you want to relaunch as Administrator? (yes/no)" + if ($resp -match '^(y|yes)$') { + Write-Host "?? Relaunching with elevated privileges..." + Start-Process -FilePath "powershell.exe" ` + -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" ` + -Verb RunAs + exit + } else { + Write-Host "? Operation cancelled administrator rights required." -ForegroundColor Red + exit 1 + } +} + + + +# ------------------------------- +# Globals & Paths +# ------------------------------- +$ErrorActionPreference = 'Stop' +$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8' + +$VERSION = "4.0.0" +$ScriptDir = Split-Path -Parent -Path $MyInvocation.MyCommand.Path +Set-Location $ScriptDir + +$Timestamp = (Get-Date -Format "yyyy-MM-dd_HH-mm-ss") +$logFile = Join-Path $ScriptDir ("install_{0}.log" -f $Timestamp) + +$ServiceName = "RetroIPTVGuide" +$Port = 5000 +$Url = "http://127.0.0.1:$Port" +$VenvDir = Join-Path $ScriptDir "venv" +$VenvPy = Join-Path $VenvDir "Scripts\python.exe" +$NssmExe = "nssm.exe" # rely on PATH from Chocolatey + +# ------------------------------- +# Helpers (colored output) +# ------------------------------- +function Write-Title($text) { Write-Host $text -ForegroundColor Cyan } +function Write-Info($text) { Write-Host $text -ForegroundColor White } +function Write-Warn($text) { Write-Host $text -ForegroundColor Yellow } +function Write-ErrorMsg($text) { Write-Host $text -ForegroundColor Red } +function Write-Ok($text) { Write-Host $text -ForegroundColor Green } + +function Confirm-YesNo($Prompt) { + if ($yes -or $y) { return $true } + $resp = Read-Host "$Prompt (yes/no)" + return ($resp -eq 'yes') +} + +function Add-Log($text) { + Write-Host $text +} + + +# ------------------------------- +# Environment guard (Windows only) +# ------------------------------- +if (-not $env:OS -or $env:OS -notmatch 'Windows_NT') { + Write-ErrorMsg "Unsupported environment. Please run this on Windows PowerShell." + exit 1 +} + +# ------------------------------- +# Start transcript & Banner +# ------------------------------- +try { Start-Transcript -Path $logFile -Append | Out-Null } catch {} + +"" +$banner = @" + + + + + + + +"@ + +try { + # Set console width/height (character columns x lines) + $cols = 145 + $lines = 45 + mode con: cols=$cols lines=$lines | Out-Null + + Start-Sleep -Milliseconds 200 + + # Move/resize only if running under classic PowerShell console + $hwnd = (Get-Process -Id $PID).MainWindowHandle + if ($hwnd -ne 0) { + Add-Type @" + using System; + using System.Runtime.InteropServices; + public static class Win32 { + [DllImport("user32.dll")] + public static extern bool MoveWindow(IntPtr hwnd, int x, int y, int width, int height, bool repaint); + } +"@ + } +} catch { + Write-Host "[WARN] Could not resize PowerShell window: $($_.Exception.Message)" -ForegroundColor Yellow +} + + +Write-Host $banner -ForegroundColor Cyan +Write-Title "===========================================================================" +Write-Title " RetroIPTVGuide | Windows Edition (Headless)" +Write-Title "===========================================================================" +Write-Info "" +Write-Info "=== RetroIPTVGuide Unified Script (v$VERSION) ===" +Write-Info ("Start time: {0}" -f (Get-Date)) +Write-Info ("Log file: {0}" -f $logFile) +Write-Info "" + +# ------------------------------- +# Agreement text (shared style) +# ------------------------------- +$needAgree = -not ($agree -or $a) +$AgreementText = @" +============================================================ + RetroIPTVGuide Installer Agreement +============================================================ + +This installer will perform the following actions: + - Verify and install dependencies (Chocolatey, Python, Git, NSSM) + - Create and configure a Python virtual environment + - Upgrade pip and install requirements + - Register and start the RetroIPTVGuide Windows service (NSSM) + +By continuing, you acknowledge and agree that: + - This software should ONLY be run on internal networks. + - It must NOT be exposed to the public Internet. + - You accept all risks; the author provides NO WARRANTY. + - The author is NOT responsible for any damage, data loss, + or security vulnerabilities created by this installation. + +"@ + +# ------------------------------- +# Dependency helpers +# ------------------------------- +function Ensure-Choco { + Write-Info "Checking for Chocolatey..." + if (-not (Get-Command choco.exe -ErrorAction SilentlyContinue)) { + Write-Warn "Chocolatey not found. Installing..." + # Allow TLS 1.2 + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + Set-ExecutionPolicy Bypass -Scope Process -Force + try { + Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) + Write-Ok "Chocolatey installed." + } catch { + Write-ErrorMsg "Failed to install Chocolatey. $_" + throw + } + } else { + Write-Ok "Chocolatey already installed." + } +} + +function Ensure-ChocoPkg($pkgName) { + if (-not (choco list --local-only | Select-String -Quiet ("^{0} " -f [regex]::Escape($pkgName)))) { + Write-Info "Installing $pkgName via Chocolatey..." + choco install $pkgName -y | Out-Null + Write-Ok "$pkgName installed." + } else { + Write-Ok "$pkgName already installed." + } +} + +function Resolve-Python { + # Prefer the Python launcher if available + $py = Get-Command py.exe -ErrorAction SilentlyContinue + if ($py) { return "py.exe" } + + $python = Get-Command python.exe -ErrorAction SilentlyContinue + if ($python) { return "python.exe" } + + return $null +} + +function New-Venv { + param([string] $VenvPath) + + if (Test-Path $VenvPy) { + Write-Ok "Existing virtual environment detected." + return + } + + $pyCmd = Resolve-Python + if (-not $pyCmd) { + Write-ErrorMsg "Python not found in PATH even after install." + throw "PythonNotFound" + } + + Write-Info "Setting up Python virtual environment..." + if ($pyCmd -eq "py.exe") { + & $pyCmd -3 -m venv $VenvPath + } else { + & $pyCmd -m venv $VenvPath + } + Write-Ok "Virtual environment created at $VenvPath" +} + +function Upgrade-PipAndInstallReqs { + if (-not (Test-Path $VenvPy)) { + Write-ErrorMsg "Virtual environment Python not found: $VenvPy" + throw "VenvMissing" + } + Write-Info "Upgrading pip..." + & $VenvPy -m pip install --upgrade pip + Write-Info "Installing requirements..." + $req = Join-Path $ScriptDir "requirements.txt" + if (-not (Test-Path $req)) { + Write-Warn "requirements.txt not found in $ScriptDir; skipping package install." + } else { + & $VenvPy -m pip install -r $req + } + Write-Ok "Python dependencies installed." +} + +# ------------------------------- +# NSSM service helpers +# ------------------------------- +function Ensure-Service { + param([string] $Name) + + # Install NSSM if missing + Ensure-ChocoPkg "nssm" + + $svc = Get-Service -Name $Name -ErrorAction SilentlyContinue + if ($svc) { + Write-Warn "Service '$Name' already exists. It will be updated to use the current directory." + # stop first + try { Stop-Service -Name $Name -Force -ErrorAction SilentlyContinue } catch {} + # reconfigure below + } else { + Write-Info "Creating Windows service '$Name' (NSSM)..." + & $NssmExe install $Name $VenvPy "app.py" + } + + # Configure AppDirectory / stdout / stderr / start type + & $NssmExe set $Name AppDirectory $ScriptDir | Out-Null + & $NssmExe set $Name Start SERVICE_AUTO_START | Out-Null + + $logDir = Join-Path $ScriptDir "logs" + if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir | Out-Null } + & $NssmExe set $Name AppStdout (Join-Path $logDir "service_stdout.log") | Out-Null + & $NssmExe set $Name AppStderr (Join-Path $logDir "service_stderr.log") | Out-Null + & $NssmExe set $Name AppStopMethodConsole 1500 | Out-Null + & $NssmExe set $Name AppKillProcessTree 1 | Out-Null + + Write-Ok "Service '$Name' configured." + Write-Info "Starting service..." + & $NssmExe start $Name | Out-Null + Start-Sleep -Seconds 1 + Write-Ok "Service start issued." +} + +function Remove-ServiceSafe { + param([string] $Name) + $svc = Get-Service -Name $Name -ErrorAction SilentlyContinue + if ($svc) { + Write-Info "Stopping service '$Name'..." + try { & $NssmExe stop $Name | Out-Null } catch {} + Start-Sleep -Seconds 1 + Write-Info "Removing service '$Name'..." + try { & $NssmExe remove $Name confirm | Out-Null } catch {} + Write-Ok "Service '$Name' removed." + } else { + Write-Warn "Service '$Name' not found. Skipping." + } +} + +# ------------------------------- +# HTTP verification (mirrors Linux) +# ------------------------------- +function Verify-ServiceAndHttp { + param( + [int]$MaxWaitSeconds = 60, + [int]$PollIntervalSeconds = 2 + ) + + Write-Info "" + Write-Info "Verifying service status..." + Start-Sleep -Seconds 2 + + $svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue + if (-not $svc -or $svc.Status -ne 'Running') { + Write-ErrorMsg "- Service not active. Run: Get-Service -Name $ServiceName | Format-List *" + return + } + + Write-Ok "- Service is active." + Write-Info "Waiting for TCP port $Port to accept connections..." + + $end = (Get-Date).AddSeconds($MaxWaitSeconds) + $tcpConnected = $false + + while ((Get-Date) -lt $end) { + try { + $client = New-Object System.Net.Sockets.TcpClient + $ar = $client.BeginConnect("127.0.0.1", $Port, $null, $null) + if ($ar.AsyncWaitHandle.WaitOne(1500, $false)) { + $client.EndConnect($ar) + $client.Close() + $tcpConnected = $true + Write-Ok "- TCP port $Port is accepting connections." + break + } + $client.Close() + } catch { } + Start-Sleep -Seconds $PollIntervalSeconds + } + + if (-not $tcpConnected) { + Write-Warn "-- Port $Port did not open within $MaxWaitSeconds s." + return + } + + # optional lightweight HTTP confirmation + Write-Info "Confirming HTTP response..." + try { + $req = [Net.WebRequest]::Create("http://127.0.0.1:$Port/") + $req.Timeout = 3000 + $resp = $req.GetResponse() + Write-Ok "- HTTP responded: $($resp.StatusDescription)" + $resp.Close() + Add-Log "- HTTP verified on port $Port" + } catch { + Write-Warn "-- TCP open but HTTP verification failed likely still initializing Flask." + Add-Log "-- TCP open but HTTP verification failed." + } +} + + +# ------------------------------- +# INSTALL +# ------------------------------- +function Do-Install { + # Agreement + Write-Info $AgreementText + if ($needAgree) { + if (-not (Confirm-YesNo "Do you agree to these terms?")) { + Write-Warn "Installation aborted by user." + exit 1 + } + } else { + Write-Info "License auto-accepted via --agree." + } + + # Dependencies + Ensure-Choco + Ensure-ChocoPkg "python" + Ensure-ChocoPkg "git" + Ensure-ChocoPkg "nssm" + + # Python & venv + New-Venv -VenvPath $VenvDir + Upgrade-PipAndInstallReqs + + # Service + Ensure-Service -Name $ServiceName + + # Verify & Summary + Verify-ServiceAndHttp + + Write-Title "" + Write-Title "============================================================" + Write-Title " Installation Complete" + Write-Title "============================================================" + Write-Info ("End time: {0}" -f (Get-Date)) + Write-Info ("Access in browser: http://{0}:{1}" -f $env:COMPUTERNAME, $Port) + Write-Info ("Default login: admin / strongpassword123") + Write-Info ("Log file: {0}" -f $logFile) + Write-Title "============================================================" +} + +# ------------------------------- +# UNINSTALL +# ------------------------------- +function Do-Uninstall { + Write-Host "" + Write-Title "============================================================" + Write-Title " RetroIPTVGuide Uninstaller" + Write-Title "============================================================" + Write-Info ("Start time: {0}" -f (Get-Date)) + Write-Host "" + + if (-not ($yes -or $y)) { + if (-not (Confirm-YesNo "Proceed with uninstall of RetroIPTVGuide service and local environment?")) { + Write-Warn "Uninstall aborted by user." + Write-Host "" + Write-Host "Press ENTER to close this window..." -ForegroundColor Yellow + Read-Host + exit 1 + } + } else { + Write-Info "Auto-confirmed via --yes." + } + + # Make sure nssm is callable, quietly + try { + if (-not (Get-Command nssm.exe -ErrorAction SilentlyContinue)) { + choco install nssm -y | Out-Null + } + } catch {} + Remove-ServiceSafe -Name $ServiceName + + + # Remove venv & optional artifacts + if (Test-Path $VenvDir) { + Write-Info "Removing virtual environment ($VenvDir)..." + try { Remove-Item -Recurse -Force $VenvDir } catch {} + } + + # Optionally offer to clean logs + $logsDir = Join-Path $ScriptDir "logs" + if ((Test-Path $logsDir) -and -not ($yes -or $y)) { + if (Confirm-YesNo "Remove log files in '$logsDir'?") { + try { Remove-Item -Recurse -Force $logsDir } catch {} + Write-Ok "Logs removed." + } + } + + Write-Host "" + Write-Title "============================================================" + Write-Title " Uninstallation Complete" + Write-Title "============================================================" + Write-Ok "All requested components removed." + Write-Info ("End time: {0}" -f (Get-Date)) + Write-Title "============================================================" + Write-Host "" + Write-Host "RetroIPTVGuide has been successfully removed from this system." -ForegroundColor Cyan + Write-Host "" + Write-Host "Press ENTER to close this window..." -ForegroundColor Yellow + Read-Host +} + + +if (-not $Action) { + Write-Host "" + Write-Host "RetroIPTVGuide Windows Installer Menu" -ForegroundColor Cyan + Write-Host "===========================================================" + Write-Host "[1] Install RetroIPTVGuide" + Write-Host "[2] Uninstall RetroIPTVGuide" + Write-Host "[3] Exit" + Write-Host "===========================================================" + $choice = Read-Host "Select an option (1-3)" + switch ($choice) { + '1' { $Action = 'install' } + '2' { $Action = 'uninstall' } + '3' { Write-Host "Exiting..." ; exit } + default { Write-Host "Invalid selection." ; exit } + } +} + + +# ------------------------------- +# Dispatch +# ------------------------------- +try { + switch ($Action) { + 'install' { Do-Install } + 'uninstall' { Do-Uninstall } + } +} catch { + Write-ErrorMsg "- An error occurred: $($_.Exception.Message)" + exit 1 +} finally { + try { + Stop-Transcript | Out-Null + Start-Sleep -Milliseconds 200 + } catch {} + + Write-Host "" + Write-Host "============================================================" -ForegroundColor Cyan + Write-Host "RetroIPTVGuide process complete." -ForegroundColor Green + Write-Host "You may review the log above or press ENTER to close this window." -ForegroundColor Yellow + Write-Host "============================================================" + Read-Host +} \ No newline at end of file diff --git a/templates/about.html b/templates/about.html index de0a493..763d323 100644 --- a/templates/about.html +++ b/templates/about.html @@ -209,35 +209,30 @@ HOME {% if current_user is defined and current_user.username == 'admin' %} - + + MANAGE USERS {% endif %} - diff --git a/templates/change_tuner.html b/templates/change_tuner.html index f32d16e..b0a12a6 100644 --- a/templates/change_tuner.html +++ b/templates/change_tuner.html @@ -189,36 +189,34 @@