import os import sys import time import ctypes import requests import subprocess import vlc from PyQt5.QtWidgets import ( QApplication, QMainWindow, QVBoxLayout, QListWidget, QWidget, QHBoxLayout, QListWidgetItem, QLabel, QPushButton ) from PyQt5.QtCore import Qt, QTimer, QTime, pyqtSignal import win32gui import win32api import win32con class VLCPlayer: def __init__(self): vlc_args = [ "--avcodec-hw=any", "--network-caching=3000", "--no-video-title-show", "--vout=direct3d11" ] if sys.platform == "win32": vlc_dir = os.path.join(os.path.dirname(__file__), "vlc") os.environ['PATH'] = vlc_dir + os.pathsep + os.environ['PATH'] try: ctypes.windll.kernel32.SetDllDirectoryW(vlc_dir) except: pass self.instance = vlc.Instance(vlc_args) self.media_player = self.instance.media_player_new() def play_stream(self, url): media = self.instance.media_new(url) media.add_option(":network-caching=3000") self.media_player.set_media(media) self.media_player.play() self.try_move_window() def try_move_window(self): def try_move(): hwnd = self.media_player.get_hwnd() if hwnd: rect = win32gui.GetWindowRect(hwnd) w = rect[2] - rect[0] h = rect[3] - rect[1] if w > 50 and h > 50: screen_w = win32api.GetSystemMetrics(0) screen_h = win32api.GetSystemMetrics(1) width = int(screen_w / 2) height = int(screen_h / 2) win32gui.MoveWindow(hwnd, screen_w - width, 0, width, height, True) return QTimer.singleShot(500, try_move) try_move() def stop(self): self.media_player.stop() def toggle_fullscreen(self): self.media_player.set_fullscreen(not self.media_player.get_fullscreen()) def pause(self): self.media_player.pause() def is_playing(self): return self.media_player.is_playing() def release(self): self.media_player.release() self.instance.release() class Overlay(QWidget): def __init__(self): super().__init__(None) self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool) self.setAttribute(Qt.WA_TranslucentBackground) self.setMouseTracking(True) self.channel_label = QLabel("Channel", self) self.resolution_label = QLabel("Resolution", self) self.clock_label = QLabel("Clock", self) for label in [self.channel_label, self.resolution_label, self.clock_label]: label.setStyleSheet("color: white; background-color: rgba(0,0,0,0.4); padding: 5px;") label.setAttribute(Qt.WA_TransparentForMouseEvents) self.channel_label.move(10, 10) self.resolution_label.move(10, 40) self.clock_label.move(10, 70) self.timer = QTimer(self) self.timer.timeout.connect(self.update_clock) self.timer.start(1000) self.update_clock() self.update_timer = QTimer(self) self.update_timer.timeout.connect(self.sync_with_vlc) self.update_timer.start(500) def update_clock(self): current_time = QTime.currentTime().toString("HH:mm:ss") self.clock_label.setText(f"🕒 {current_time}") def update_info(self, channel, resolution): self.channel_label.setText(f"📺 {channel}") self.resolution_label.setText(f"🎥 {resolution}") def sync_with_vlc(self): hwnd = vlc_hwnd() if hwnd: rect = win32gui.GetWindowRect(hwnd) x, y, right, bottom = rect w = right - x h = bottom - y self.setGeometry(x, y, w, h) self.raise_() def vlc_hwnd(): try: return player.media_player.get_hwnd() except: return None class PlaylistWindow(QMainWindow): channel_selected = pyqtSignal(str, str) def __init__(self, php_dir): super().__init__() self.setWindowTitle("IPTV Playlist") self.setGeometry(100, 100, 500, 600) self.php_dir = php_dir widget = QWidget() layout = QVBoxLayout(widget) self.channel_list = QListWidget() self.channel_list.itemDoubleClicked.connect(self.on_channel_double_clicked) layout.addWidget(self.channel_list) btn_layout = QHBoxLayout() self.prev_button = QPushButton("⏮ Prev") self.play_pause_button = QPushButton("⏯ Play/Pause") self.next_button = QPushButton("⏭ Next") btn_layout.addWidget(self.prev_button) btn_layout.addWidget(self.play_pause_button) btn_layout.addWidget(self.next_button) layout.addLayout(btn_layout) self.prev_button.clicked.connect(self.play_prev) self.play_pause_button.clicked.connect(self.toggle_play_pause) self.next_button.clicked.connect(self.play_next) self.setCentralWidget(widget) self.download_playlist() def on_channel_double_clicked(self, item): if item: name = item.text().split('(')[0].strip() url = item.data(Qt.UserRole) self.channel_selected.emit(url, name) def play_next(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_double_clicked(self.channel_list.currentItem()) def play_prev(self): current_row = self.channel_list.currentRow() if current_row > 0: self.channel_list.setCurrentRow(current_row - 1) self.on_channel_double_clicked(self.channel_list.currentItem()) def toggle_play_pause(self): if player.is_playing(): player.pause() else: player.media_player.play() def download_playlist(self): try: os.makedirs(self.php_dir, exist_ok=True) url = "https://iptv.nywebforum.com/playlist.m3u" content = requests.get(url, timeout=10).text.replace( "https://iptv.nywebforum.com", "http://localhost:8888" ) path = os.path.join(self.php_dir, "playlist.m3u") with open(path, "w", encoding="utf-8") as f: f.write(content) self.load_playlist(path) except Exception as e: print(f"Failed to download playlist: {e}") def load_playlist(self, path): try: lines = open(path, encoding="utf-8").read().splitlines() self.channel_list.clear() self.items = [] name = None for line in lines: if line.startswith("#EXTINF:"): name = line.split(',')[-1].strip() elif line and not line.startswith("#") and name: item = QListWidgetItem(f"{name} (Loading...)") item.setData(Qt.UserRole, line) self.channel_list.addItem(item) self.items.append(item) name = None if self.items: item = self.items[0] self.channel_list.setCurrentItem(item) QTimer.singleShot(1000, lambda: self.channel_selected.emit( item.data(Qt.UserRole), item.text().split('(')[0].strip() )) 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) player = VLCPlayer() overlay = Overlay() overlay.show() playlist_window = PlaylistWindow(php_dir) def on_channel_selected(url, name): player.play_stream(url) def update(): w = player.media_player.video_get_width() h = player.media_player.video_get_height() fps = player.media_player.get_fps() if w > 0 and h > 0: label = "SD" if w >= 3840: label = "4K" elif w >= 1920: label = "FHD" elif w >= 1280: label = "HD" info = f"{label}, {fps:.2f}FPS" overlay.update_info(name, info) for i in range(playlist_window.channel_list.count()): it = playlist_window.channel_list.item(i) base = it.text().split('(')[0].strip() if base == name: it.setText(f"{base} ({info})") break QTimer.singleShot(1500, update) playlist_window.channel_selected.connect(on_channel_selected) playlist_window.show() exit_code = app.exec_() player.stop() player.release() if php_process: php_process.terminate() php_process.wait(2) sys.exit(exit_code)