import os import sys import urllib.parse import requests import time from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QListWidget, QSlider, QMessageBox, QTextEdit) from PyQt5.QtCore import Qt, QTimer import vlc import subprocess class VLCIPTVPlayer(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("VLC IPTV Player") self.resize(1024, 768) self.php_port = 8888 self.php_dir = os.path.join(os.path.dirname(__file__), "php_files") os.makedirs(self.php_dir, exist_ok=True) self.php_process = None # Initialize UI first self.init_ui() # Then initialize VLC self.init_vlc() # Finally start PHP server (which will use the debug console) self.start_php_server() # Download and process files self.download_and_process_files() def init_ui(self): """Initialize the user interface""" main_widget = QWidget(self) self.setCentralWidget(main_widget) layout = QVBoxLayout() main_widget.setLayout(layout) # Debug console - create this first self.debug_console = QTextEdit() self.debug_console.setReadOnly(True) layout.addWidget(self.debug_console, 1) # Video frame self.video_frame = QWidget() self.video_frame.setStyleSheet("background-color: black;") layout.addWidget(self.video_frame, 3) # Playlist self.playlist = QListWidget() self.playlist.itemDoubleClicked.connect(self.play_selected) layout.addWidget(self.playlist, 1) # Controls control_layout = QHBoxLayout() self.play_button = QPushButton("Play") self.play_button.clicked.connect(self.play_selected) self.stop_button = QPushButton("Stop") # self.stop_button.clicked.connect(self.stop) self.reload_button = QPushButton("Update Files") self.reload_button.clicked.connect(self.download_and_process_files) self.volume_slider = QSlider(Qt.Horizontal) self.volume_slider.setRange(0, 100) self.volume_slider.setValue(50) self.volume_slider.valueChanged.connect(self.set_volume) control_layout.addWidget(self.play_button) control_layout.addWidget(self.stop_button) control_layout.addWidget(self.reload_button) control_layout.addWidget(QLabel("Volume:")) control_layout.addWidget(self.volume_slider) layout.addLayout(control_layout) def start_php_server(self): """Start the built-in PHP server with port fallback""" max_attempts = 5 for attempt in range(max_attempts): try: port = self.php_port + attempt os.chdir(self.php_dir) self.php_process = subprocess.Popen( ["php", "-S", f"localhost:{port}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0 ) # Verify server is running time.sleep(1) test_url = f"http://localhost:{port}/mac_list.php" try: response = requests.get(test_url, timeout=5) if "Mac List File is OK" in response.text: self.php_port = port # Update to successful port self.log_message(f"PHP server started on port {port}") return except: continue except Exception as e: continue # If we get here, all attempts failed self.log_message(f"Failed to start PHP server after {max_attempts} attempts", error=True) QMessageBox.critical(self, "PHP Server Error", "Could not start PHP server on any port.\n" "Please check if PHP is properly embedded.") def init_vlc(self): """Initialize VLC media player""" try: self.instance = vlc.Instance() self.media_player = self.instance.media_player_new() if sys.platform == "win32": self.media_player.set_hwnd(self.video_frame.winId()) else: self.media_player.set_xwindow(self.video_frame.winId()) self.log_message("VLC initialized successfully") except Exception as e: self.log_message(f"VLC initialization failed: {str(e)}", error=True) def log_message(self, message, error=False): """Add message to debug console""" timestamp = time.strftime("%H:%M:%S", time.localtime()) if error: self.debug_console.setTextColor(Qt.red) self.debug_console.append(f"[ERROR {timestamp}] {message}") else: self.debug_console.setTextColor(Qt.black) self.debug_console.append(f"[INFO {timestamp}] {message}") self.debug_console.setTextColor(Qt.black) self.debug_console.ensureCursorVisible() def download_and_process_files(self): """Download and process all required files""" files = { "myportal.php": "https://iptv.nywebforum.com/myportal.php?download=true", "mac_list.php": "https://iptv.nywebforum.com/mac_list.php?download=true", "playlist.m3u": "https://iptv.nywebforum.com/playlist.m3u" } for filename, url in files.items(): try: # Download to temporary file first temp_path = os.path.join(self.php_dir, f"temp_{filename}") response = requests.get(url, timeout=10) with open(temp_path, 'wb') as f: f.write(response.content) # Verify content with open(temp_path, 'r', encoding='utf-8') as f: content = f.read() if filename == "myportal.php" and "Mac List File is OK" in content: raise Exception("Downloaded wrong content for myportal.php") # Replace original file final_path = os.path.join(self.php_dir, filename) if os.path.exists(final_path): os.remove(final_path) os.rename(temp_path, final_path) self.log_message(f"Downloaded and verified {filename}") except Exception as e: self.log_message(f"Error processing {filename}: {str(e)}", error=True) if os.path.exists(temp_path): os.remove(temp_path) # Process URL replacements self.process_url_replacements() self.load_playlist() def process_url_replacements(self): """Replace all server URLs with localhost URLs""" try: playlist_path = os.path.join(self.php_dir, "playlist.m3u") if os.path.exists(playlist_path): with open(playlist_path, 'r', encoding='utf-8') as f: content = f.read() content = content.replace( "https://iptv.nywebforum.com", f"http://localhost:{self.php_port}" ) with open(playlist_path, 'w', encoding='utf-8') as f: f.write(content) self.log_message("URL replacements completed") except Exception as e: self.log_message(f"Error during URL replacement: {str(e)}", error=True) def load_playlist(self): """Load the processed playlist""" playlist_path = os.path.join(self.php_dir, "playlist.m3u") if os.path.exists(playlist_path): try: with open(playlist_path, 'r', encoding='utf-8') as f: lines = f.readlines() self.playlist.clear() for line in lines: line = line.strip() if line and not line.startswith("#"): # Ensure all URLs point to local server line = line.replace( "https://iptv.nywebforum.com", f"http://localhost:{self.php_port}" ) self.playlist.addItem(line) self.log_message("Playlist loaded successfully") except Exception as e: self.log_message(f"Error loading playlist: {str(e)}", error=True) def play_selected(self): """Play the selected stream through PHP server""" selected_items = self.playlist.selectedItems() if not selected_items: self.log_message("No stream selected", error=True) return stream_url = selected_items[0].text() self.log_message(f"Attempting to play: {stream_url}") try: # For PHP URLs, make request to local PHP server if "myportal.php" in stream_url: try: # Don't follow redirects - we want to see the Location header response = requests.get(stream_url, timeout=15, allow_redirects=False) # Check for redirect location if 300 <= response.status_code < 400 and 'Location' in response.headers: stream_url = response.headers['Location'] self.log_message(f"PHP redirect to: {stream_url}") else: # If no redirect, use the response content stream_url = response.text.strip() self.log_message(f"PHP returned: {stream_url[:200]}...") if not stream_url.startswith(("http://", "https://", "rtmp://", "rtsp://")): raise Exception(f"Invalid stream URL: {stream_url[:200]}...") except requests.exceptions.RequestException as e: self.log_message(f"PHP server request failed: {str(e)}", error=True) raise # Create media with timeout options media = self.instance.media_new(stream_url) # Set network caching (in milliseconds) media.add_option(':network-caching=3000') media.add_option(':rtsp-tcp') # Force TCP for RTSP media.add_option(':live-caching=3000') self.media_player.set_media(media) # Start playback with timeout check if self.media_player.play() == -1: raise Exception("VLC failed to start playback") # Set up playback timeout check self.playback_timeout = QTimer() self.playback_timeout.setSingleShot(True) self.playback_timeout.timeout.connect(self.check_playback_status) self.playback_timeout.start(5000) # Check after 5 seconds except Exception as e: self.log_message(f"Playback error: {str(e)}", error=True) self.stop() QMessageBox.critical(self, "Playback Error", f"Could not play stream:\n{str(e)}") def check_playback_status(self): """Check if playback actually started""" if not self.media_player.is_playing(): self.log_message("Playback timeout - stream not playing", error=True) self.stop() """Stop playback""" self.media_player.stop() self.log_message("Playback stopped") def set_volume(self, value): """Set player volume""" self.media_player.audio_set_volume(value) self.log_message(f"Volume set to {value}%") def closeEvent(self, event): """Clean up when closing""" if self.php_process: self.php_process.terminate() try: self.php_process.wait(timeout=1) except subprocess.TimeoutExpired: self.php_process.kill() super().closeEvent(event) if __name__ == "__main__": # Check dependencies try: import requests import vlc except ImportError as e: QMessageBox.critical(None, "Missing Dependencies", f"Required packages missing:\n{str(e)}\n" "Please install with: pip install python-vlc requests") sys.exit(1) # Check if PHP is available try: subprocess.run(["php", "-v"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except: QMessageBox.critical(None, "PHP Not Found", "PHP is required but not found in system PATH.\n" "Please install PHP or place PHP binaries in the application directory.") sys.exit(1) app = QApplication(sys.argv) player = VLCIPTVPlayer() player.show() sys.exit(app.exec_())