import os import sys import time import ctypes import requests import subprocess import json from PyQt5.QtWidgets import ( QApplication, QMainWindow, QVBoxLayout, QListWidget, QWidget, QHBoxLayout, QListWidgetItem, QLabel, QSplitter, QFrame ) from PyQt5.QtCore import Qt, QTimer from PyQt5.QtGui import QGuiApplication import vlc import subprocess class VideoFrame(QFrame): def __init__(self, parent=None): super().__init__(parent) self.setFrameStyle(QFrame.Panel | QFrame.Sunken) self.setStyleSheet("background-color: black;") self.is_fullscreen = False def mouseDoubleClickEvent(self, event): if self.is_fullscreen: self.parent().showNormal() self.is_fullscreen = False else: self.parent().showFullScreen() self.is_fullscreen = True class VLCPlayer: def __init__(self, video_frame): self.instance = vlc.Instance() self.media_player = self.instance.media_player_new() self.video_frame = video_frame if sys.platform == "win32": self.media_player.set_hwnd(int(self.video_frame.winId())) else: self.media_player.set_xwindow(int(self.video_frame.winId())) def play_stream(self, url): if self.media_player.is_playing(): self.media_player.stop() time.sleep(0.5) media = self.instance.media_new(url) media.add_option(":network-caching=3000") self.media_player.set_media(media) self.media_player.play() def stop(self): self.media_player.stop() def release(self): self.media_player.release() self.instance.release() class IPTVWindow(QMainWindow): def play_next_channel(self): current_row = self.channel_list.currentRow() if current_row < self.channel_list.count() - 1: self.channel_list.setCurrentRow(current_row + 1) self.on_channel_clicked(self.channel_list.currentItem()) def play_previous_channel(self): current_row = self.channel_list.currentRow() if current_row > 0: self.channel_list.setCurrentRow(current_row - 1) self.on_channel_clicked(self.channel_list.currentItem()) def toggle_play_pause(self): if self.player.media_player.is_playing(): self.player.media_player.pause() else: self.player.media_player.play() def toggle_fullscreen(self): if self.isFullScreen(): self.showNormal() else: self.showFullScreen() def __init__(self, php_dir): super().__init__() self.setWindowTitle("IPTV Player with EPG") self.setGeometry(0, 0, 1280, 720) self.php_dir = php_dir self.stream_info_cache = {} self.init_ui() self.download_playlist() def init_ui(self): splitter = QSplitter(Qt.Horizontal) # Left panel: Playlist self.channel_list = QListWidget() self.channel_list.itemClicked.connect(self.on_channel_clicked) splitter.addWidget(self.channel_list) # Right panel: Video on top, EPG on bottom right_panel = QWidget() right_layout = QVBoxLayout() self.video_frame = VideoFrame(self) right_layout.addWidget(self.video_frame, 3) self.epg_display = QLabel("EPG info will appear here") self.epg_display.setStyleSheet("background-color: #222; color: white; padding: 10px;") right_layout.addWidget(self.epg_display, 1) # Control buttons control_layout = QHBoxLayout() from PyQt5.QtWidgets import QPushButton self.btn_prev = QPushButton("⏮️") self.btn_play = QPushButton("⏯️") self.btn_next = QPushButton("⏭️") self.btn_fullscreen = QPushButton("🖵") self.btn_prev.clicked.connect(self.play_previous_channel) self.btn_play.clicked.connect(self.toggle_play_pause) self.btn_next.clicked.connect(self.play_next_channel) self.btn_fullscreen.clicked.connect(self.toggle_fullscreen) control_layout.addWidget(self.btn_prev) control_layout.addWidget(self.btn_play) control_layout.addWidget(self.btn_next) control_layout.addWidget(self.btn_fullscreen) right_layout.addLayout(control_layout) right_panel.setLayout(right_layout) splitter.addWidget(right_panel) splitter.setSizes([400, 880]) self.setCentralWidget(splitter) self.player = VLCPlayer(self.video_frame) def on_channel_clicked(self, item): url = item.data(Qt.UserRole) epg = item.data(Qt.UserRole + 1) self.player.play_stream(url) def update_info(attempt=0): width = self.player.media_player.video_get_width() height = self.player.media_player.video_get_height() fps = self.player.media_player.get_fps() if width > 0 and height > 0: res_label = "SD" if width >= 3840: res_label = "4K" elif width >= 1920: res_label = "FHD" elif width >= 1280: res_label = "HD" quality = f"{res_label}, {fps:.2f}FPS" current_text = item.text() if '(' in current_text: base_name = current_text.split('(')[0].strip() item.setText(f"{base_name} ({quality})") elif attempt < 10: QTimer.singleShot(500, lambda: update_info(attempt + 1)) QTimer.singleShot(1000, update_info) def download_playlist(self): try: os.makedirs(self.php_dir, exist_ok=True) playlist_url = "https://iptv.nywebforum.com/playlist.m3u" response = requests.get(playlist_url, timeout=10) content = response.text content = content.replace( "https://iptv.nywebforum.com", f"http://localhost:8888" ) playlist_path = os.path.join(self.php_dir, "playlist.m3u") with open(playlist_path, "w", encoding="utf-8") as f: f.write(content) self.load_playlist(playlist_path) except Exception as e: print(f"Failed to download playlist: {e}") def load_playlist(self, playlist_path): try: with open(playlist_path, "r", encoding="utf-8") as f: lines = f.readlines() self.channel_list.clear() current_name = None first_url = None first_item = None for line in lines: line = line.strip() if line.startswith("#EXTINF:"): parts = line.split(',') current_name = parts[-1].strip() if len(parts) > 1 else "Unknown" elif line and not line.startswith("#") and current_name: url = line display_text = f"{current_name} (Loading...)" item = QListWidgetItem(display_text) item.setData(Qt.UserRole, url) item.setData(Qt.UserRole + 1, "EPG info placeholder") self.channel_list.addItem(item) if first_url is None: first_url = url first_item = item current_name = None if first_item: self.channel_list.setCurrentItem(first_item) QTimer.singleShot(1000, lambda: self.on_channel_clicked(first_item)) except Exception as e: print(f"Failed to load playlist: {e}") def start_php_server(php_dir, port=8888): try: os.chdir(php_dir) return subprocess.Popen( ["php", "-S", f"localhost:{port}"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0 ) except Exception as e: print(f"Failed to start PHP server: {e}") return None if __name__ == "__main__": php_dir = os.path.join(os.path.dirname(__file__), "php_files") os.makedirs(php_dir, exist_ok=True) php_process = start_php_server(php_dir) app = QApplication(sys.argv) window = IPTVWindow(php_dir) window.show() exit_code = app.exec_() window.player.stop() window.player.release() if php_process: php_process.terminate() try: php_process.wait(timeout=2) except subprocess.TimeoutExpired: php_process.kill() sys.exit(exit_code)