import os
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import vlc
import subprocess
import threading
import time
# Path to VLC executable
VLC_PATH = r"C:\Program Files\VideoLAN\VLC\vlc.exe"
class ChannelPlayer:
def __init__(self, root):
self.root = root
self.root.title("Najeeb Live Videos and IPTV Channels Player")
self.channels = {}
self.current_instance = None
self.player = None
self.media = None
self.is_dragging_slider = False
self.is_fullscreen = False
self.is_muted = False
self.process = None
self.recording_process = None # To store the subprocess for recording
self.create_widgets()
self.auto_load_channels()
def auto_load_channels(self):
script_dir = os.path.dirname(os.path.abspath(__file__))
channels_dir = os.path.join(script_dir, "CHANNELS")
if not os.path.exists(channels_dir):
os.makedirs(channels_dir)
playlist_files = [file for file in os.listdir(channels_dir) if file.endswith(('.m3u', '.m3u8', '.txt'))]
for file_name in playlist_files:
file_path = os.path.join(channels_dir, file_name)
self.load_channels(file_path)
def load_channels(self, file_path):
try:
with open(file_path, 'r', encoding='utf-8') as file:
for line in file:
parts = line.strip().split(maxsplit=1)
if len(parts) == 2:
self.channels[parts[0]] = parts[1]
self.update_channel_dropdown()
except FileNotFoundError:
messagebox.showerror("Error", f"File not found: {file_path}")
except UnicodeDecodeError:
messagebox.showerror("Error", "Error decoding the file. Please ensure it is in UTF-8 format.")
except Exception as e:
messagebox.showerror("Error", str(e))
def update_channel_dropdown(self):
self.channel_dropdown['values'] = list(self.channels.keys())
def create_widgets(self):
style = ttk.Style()
style.configure('TButton', font=('Helvetica', 10))
style.configure('TCombobox', font=('Helvetica', 10))
# Search field for channel title
self.search_label = ttk.Label(self.root, text="Search Channel Title:")
self.search_label.grid(row=0, column=0, padx=5, pady=5, sticky="w")
self.search_var = tk.StringVar()
self.search_entry = ttk.Entry(self.root, textvariable=self.search_var)
self.search_entry.grid(row=0, column=1, columnspan=2, padx=5, pady=5, sticky="ew")
self.search_entry.bind("<KeyRelease>", self.filter_channels)
# Browse button for URLs file
self.browse_button = ttk.Button(self.root, text="Browse", command=self.browse_file)
self.browse_button.grid(row=0, column=3, padx=5, pady=5, sticky="ew")
# Channel selection
self.channel_var = tk.StringVar()
self.channel_dropdown = ttk.Combobox(self.root, textvariable=self.channel_var)
self.channel_dropdown.grid(row=1, column=0, columnspan=2, padx=5, pady=5, sticky="ew")
# Preview button
self.preview_button = ttk.Button(self.root, text="Preview", command=self.preview_channel)
self.preview_button.grid(row=1, column=2, padx=5, pady=5, sticky="ew")
# Stop preview button
self.stop_preview_button = ttk.Button(self.root, text="Stop Preview", command=self.stop_preview)
self.stop_preview_button.grid(row=1, column=3, padx=5, pady=5, sticky="ew")
# Play button
self.play_button = ttk.Button(self.root, text="Play", command=self.play_channel)
self.play_button.grid(row=1, column=4, padx=5, pady=5, sticky="ew")
# Volume control
self.volume_slider = ttk.Scale(self.root, from_=0, to=200, orient=tk.HORIZONTAL, command=self.set_volume)
self.volume_slider.set(50) # Set initial volume to 50%
self.volume_slider.grid(row=1, column=5, padx=5, pady=5, sticky="ew")
# Mute button
self.mute_button = ttk.Button(self.root, text="Mute", command=self.toggle_mute)
self.mute_button.grid(row=1, column=6, padx=5, pady=5, sticky="ew")
# Full screen button
self.fullscreen_button = ttk.Button(self.root, text="Full Screen", command=self.toggle_fullscreen)
self.fullscreen_button.grid(row=1, column=7, padx=5, pady=5, sticky="ew")
# VLC video frame
self.video_frame = tk.Frame(self.root, bg="black", height=300)
self.video_frame.grid(row=2, column=0, columnspan=8, padx=5, pady=5, sticky="nsew")
# Video control slider
self.video_slider = ttk.Scale(self.root, from_=0, to=1000, orient=tk.HORIZONTAL)
self.video_slider.grid(row=3, column=0, columnspan=8, padx=5, pady=5, sticky="ew")
self.video_slider.bind("<ButtonPress-1>", self.slider_pressed)
self.video_slider.bind("<ButtonRelease-1>", self.slider_released)
# Capture video button
self.capture_video_button = ttk.Button(self.root, text="Capture Video", command=self.capture_video)
self.capture_video_button.grid(row=4, column=0, padx=5, pady=5, sticky="ew")
# Record audio button
self.record_audio_button = ttk.Button(self.root, text="Record Audio", command=self.record_audio)
self.record_audio_button.grid(row=4, column=1, padx=5, pady=5, sticky="ew")
# Stop recording button
self.stop_record_button = ttk.Button(self.root, text="Stop Recording", command=self.stop_recording)
self.stop_record_button.grid(row=4, column=2, padx=5, pady=5, sticky="ew")
# Capture screenshots button
self.capture_screenshots_button = ttk.Button(self.root, text="Capture Screenshots", command=self.capture_screenshots)
self.capture_screenshots_button.grid(row=4, column=3, padx=5, pady=5, sticky="ew")
# Copy to clipboard button
self.copy_button = ttk.Button(self.root, text="Copy URL", command=self.copy_to_clipboard)
self.copy_button.grid(row=4, column=4, padx=5, pady=5, sticky="ew")
self.root.grid_rowconfigure(2, weight=1)
self.root.grid_rowconfigure(3, weight=0)
self.root.grid_rowconfigure(4, weight=0)
self.root.grid_columnconfigure(0, weight=1)
self.root.grid_columnconfigure(1, weight=1)
self.root.grid_columnconfigure(2, weight=1)
self.root.grid_columnconfigure(3, weight=1)
self.root.grid_columnconfigure(4, weight=1)
self.root.grid_columnconfigure(5, weight=1)
self.root.grid_columnconfigure(6, weight=1)
self.root.grid_columnconfigure(7, weight=1)
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
def browse_file(self):
file_path = filedialog.askopenfilename(
title="Select URLs file",
filetypes=(("M3U files", "*.m3u"), ("M3U8 files", "*.m3u8"), ("TEXT files", "*.txt"), ("All files", "*.*"))
)
if file_path:
self.load_channels(file_path)
def filter_channels(self, event):
search_term = self.search_var.get().lower()
filtered_channels = [channel for channel in self.channels.keys() if search_term in channel.lower()]
self.channel_dropdown['values'] = filtered_channels
def preview_channel(self):
self.stop_preview()
channel = self.channel_var.get()
if channel and channel in self.channels:
url = self.channels[channel]
self.current_instance = vlc.Instance()
self.player = self.current_instance.media_player_new()
self.player.set_hwnd(self.video_frame.winfo_id())
self.media = self.current_instance.media_new(url)
self.player.set_media(self.media)
self.player.play()
self.update_video_slider()
def stop_preview(self):
if self.player:
self.player.stop()
self.player.release()
self.player = None
if self.current_instance:
self.current_instance.release()
self.current_instance = None
def play_channel(self):
self.stop_preview()
channel = self.channel_var.get()
if channel and channel in self.channels:
url = self.channels[channel]
subprocess.Popen([VLC_PATH, url])
def set_volume(self, value):
if self.player:
volume = int(float(value))
if volume > 200:
volume = 200
self.player.audio_set_volume(volume)
def toggle_mute(self):
if self.player:
self.is_muted = not self.is_muted
self.player.audio_toggle_mute()
self.mute_button.config(text="Unmute" if self.is_muted else "Mute")
def slider_pressed(self, event):
self.is_dragging_slider = True
def slider_released(self, event):
self.is_dragging_slider = False
self.set_position(self.video_slider.get())
def set_position(self, value):
if self.player:
self.player.set_position(float(value) / 1000.0)
def update_video_slider(self):
if self.player and not self.is_dragging_slider:
position = self.player.get_position() * 1000
self.video_slider.set(position)
self.root.after(1000, self.update_video_slider)
def toggle_fullscreen(self):
self.is_fullscreen = not self.is_fullscreen
self.root.attributes("-fullscreen", self.is_fullscreen)
self.fullscreen_button.config(text="Exit Full Screen" if self.is_fullscreen else "Full Screen")
def on_closing(self):
self.stop_preview()
if self.process:
self.process.terminate()
if self.recording_process:
self.recording_process.terminate()
self.root.destroy()
def capture_video(self):
try:
selected_channel = self.channel_var.get().strip()
if selected_channel in self.channels:
url = self.channels[selected_channel]
filename = filedialog.asksaveasfilename(defaultextension=".mp4", filetypes=[("MP4 files", "*.mp4")])
if filename:
command = ['ffmpeg', '-y', '-i', url, '-t', '03:55:00', '-c', 'copy', filename]
threading.Thread(target=lambda: subprocess.run(command)).start()
messagebox.showinfo("Capturing", f"Capturing 03:55 minutes of video to {filename}")
else:
messagebox.showerror("Error", "Selected text is not a valid URL.")
except tk.TclError:
messagebox.showerror("Error", "No text selected.")
def record_audio(self):
try:
selected_channel = self.channel_var.get().strip()
if selected_channel in self.channels:
url = self.channels[selected_channel]
filename = filedialog.asksaveasfilename(defaultextension=".mp3", filetypes=[("MP3 files", "*.mp3")])
if filename:
command = ['ffmpeg', '-y', '-i', url, '-f', 'mp3', '-c:a', 'libmp3lame', filename]
self.process = subprocess.Popen(command)
messagebox.showinfo("Recording", f"Recording audio to {filename}")
else:
messagebox.showerror("Error", "Selected text is not a valid URL.")
except tk.TclError:
messagebox.showerror("Error", "No text selected.")
def stop_recording(self):
if self.process:
self.process.terminate()
self.process = None
messagebox.showinfo("Stopped", "Recording stopped")
def capture_screenshots(self):
if self.player.get_media():
filename_base = filedialog.asksaveasfilename(defaultextension=".png", filetypes=[("PNG files", "*.png")])
if filename_base:
interval = 5
num_screenshots = 5
for i in range(num_screenshots):
time.sleep(interval)
filename = f"{filename_base}_{i + 1}.png"
self.player.video_take_snapshot(0, filename, 0, 0)
messagebox.showinfo("Capturing Screenshots", f"Captured {num_screenshots} screenshots every {interval} seconds to {filename_base}")
def copy_to_clipboard(self):
channel = self.channel_var.get()
if channel and channel in self.channels:
url = self.channels[channel]
self.root.clipboard_clear()
self.root.clipboard_append(url)
messagebox.showinfo("Copied", f"URL copied to clipboard: {url}")
else:
messagebox.showerror("Error", "No channel selected or invalid channel.")
if __name__ == "__main__":
root = tk.Tk()
app = ChannelPlayer(root)
root.mainloop()