Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import tkinter as tk
- from tkinter import ttk, filedialog, simpledialog, messagebox
- import vlc
- import os, sys, json
- from urllib.parse import unquote
- from datetime import datetime
- import requests
- #================ADD-IMAGE-ICON=================
- import sys
- def resource_path(relative_path):
- """ Get the absolute path to the resource, works for PyInstaller. """
- if getattr(sys, '_MEIPASS', False):
- return os.path.join(sys._MEIPASS, relative_path)
- return os.path.join(os.path.abspath("."), relative_path)
- # Use this function to load files:
- splash_image = resource_path("THEME/splash-7.png")
- icon_path = resource_path("THEME/P.ico")
- #================ADD-IMAGE-ICON=================
- CONFIG_FILE = "config.json"
- VLC_EXE = r"C:\Program Files\VideoLAN\VLC\vlc.exe"
- #================ADD-SPLASH-IMAGE=================
- import tkinter as tk
- from PIL import Image, ImageTk
- import time
- def show_splash(image_path):
- # Create splash screen window
- splash = tk.Tk()
- splash.overrideredirect(True) # Remove window border
- # Load the image
- image = Image.open(image_path)
- img = ImageTk.PhotoImage(image)
- # Get image dimensions
- img_width, img_height = image.size
- # Calculate position to center the splash screen
- screen_width = splash.winfo_screenwidth()
- screen_height = splash.winfo_screenheight()
- x = (screen_width - img_width) // 2
- y = (screen_height - img_height) // 2
- splash.geometry(f"{img_width}x{img_height}+{x}+{y}")
- # Set transparent background
- splash.config(bg="white")
- splash.attributes("-transparentcolor", "white") # Make white color transparent
- # Display the image
- label = tk.Label(splash, image=img, bg="white")
- label.pack()
- # Display the splash screen for 5 seconds
- splash.after(5000, splash.destroy)
- splash.mainloop()
- # Call splash screen function
- show_splash("THEME/splash-7.png") # Replace with your image file path
- # ==================================================
- class IPTVPlayerV3:
- def __init__(self, root):
- self.root = root
- self.root.title("Najeeb Pro-Level IPTV M3U Player v3 ")
- self.root.geometry("1200x720")
- self.root.minsize(1000, 600)
- self.root.iconbitmap(icon_path)
- root.tk.call("source", r"THEME\azure.tcl")
- root.tk.call("set_theme", "dark")
- # ================= DATA =================
- self.playlists = [[]]
- self.titles = [[]]
- self.filtered_indices = []
- self.is_playing = False
- self.in_fullscreen = False
- # ================= CONFIG =================
- self.config = self.load_config()
- self.dark_mode = self.config.get("dark_mode", False)
- self.volume = self.config.get("volume", 80)
- self.last_file = self.config.get("last_file", "")
- # ================= UI =================
- self.create_widgets()
- self.apply_theme()
- self.search_var.trace_add("write", self.filter_playlist)
- # ================= VLC =================
- self.instance = vlc.Instance("--quiet")
- self.player = self.instance.media_player_new()
- self.root.after(500, self.embed_vlc)
- if self.last_file and os.path.isfile(self.last_file):
- self.load_m3u_file(self.last_file)
- self.setup_shortcuts()
- self.update_seek()
- # =====================================================
- # VLC EMBED
- # =====================================================
- def embed_vlc(self):
- self.video_frame.update_idletasks()
- hwnd = self.video_frame.winfo_id()
- if sys.platform.startswith("win"):
- self.player.set_hwnd(hwnd)
- elif sys.platform == "darwin":
- self.player.set_nsobject(hwnd)
- else:
- self.player.set_xwindow(hwnd)
- # =====================================================
- # ================= BUSY OVERLAY =================
- def show_busy(self, text="Loading..."):
- self.busy_frame = tk.Frame(self.video_frame, bg="black")
- self.busy_frame.place(relx=0, rely=0, relwidth=1, relheight=1)
- tk.Label(
- self.busy_frame,
- text=text,
- fg="white",
- bg="black",
- font=("Segoe UI", 16, "bold")
- ).pack(expand=True)
- self.root.update()
- def hide_busy(self):
- if hasattr(self, "busy_frame"):
- self.busy_frame.destroy()
- # UI
- # =====================================================
- def create_widgets(self):
- # LEFT
- self.left_frame = tk.Frame(self.root, width=300)
- self.left_frame.pack(side=tk.LEFT, fill=tk.Y)
- self.left_frame.pack_propagate(False)
- self.search_var = tk.StringVar()
- self.search_entry = tk.Entry(self.left_frame, textvariable=self.search_var)
- self.search_entry.pack(fill=tk.X, padx=5, pady=5)
- self.search_entry.insert(0, "🔍 Search...")
- self.search_entry.bind("<FocusIn>", self.on_search_focus_in)
- self.search_entry.bind("<FocusOut>", self.on_search_focus_out)
- self.tab_control = ttk.Notebook(self.left_frame)
- self.tab_control.pack(fill=tk.BOTH, expand=True, padx=5)
- self.playlist_boxes = []
- self.add_playlist_tab("Playlist 1")
- btns = tk.Frame(self.left_frame)
- btns.pack(fill=tk.X, pady=5)
- for text, cmd in [
- ("Open M3U", self.load_m3u),
- ("➕ URL", self.add_item),
- ("➕ Tab", self.new_tab),
- ("Dark", self.toggle_theme),
- ("Save", self.save_m3u),
- ]:
- tk.Button(btns, text=text, command=cmd).pack(
- side=tk.LEFT, expand=True, fill=tk.X, padx=1
- )
- # RIGHT
- self.right_frame = tk.Frame(self.root)
- self.right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
- self.video_container = tk.Frame(self.right_frame, bg="black")
- self.video_container.pack(fill=tk.BOTH, expand=True)
- self.video_frame = tk.Frame(self.video_container, bg="black")
- self.video_frame.pack(fill=tk.BOTH, expand=True)
- controls = tk.Frame(self.right_frame)
- controls.pack(fill=tk.X)
- tk.Button(controls, text="▶ Play", command=self.play).pack(side=tk.LEFT)
- tk.Button(controls, text="⏸ Pause", command=self.pause).pack(side=tk.LEFT)
- tk.Button(controls, text="⏹ Stop", command=self.stop).pack(side=tk.LEFT)
- self.fs_btn = tk.Button(
- controls, text="➕ Fullscreen", command=self.toggle_fullscreen
- )
- self.fs_btn.pack(side=tk.RIGHT)
- tk.Label(controls, text="Vol").pack(side=tk.RIGHT)
- self.vol_slider = tk.Scale(
- controls, from_=0, to=100, orient=tk.HORIZONTAL,
- command=self.set_volume, showvalue=0, length=120
- )
- self.vol_slider.set(self.volume)
- self.vol_slider.pack(side=tk.RIGHT)
- self.seek_slider = tk.Scale(
- self.right_frame, from_=0, to=100,
- orient=tk.HORIZONTAL, showvalue=0, command=self.seek
- )
- self.seek_slider.pack(fill=tk.X)
- self.vlc_menu = tk.Menu(self.root, tearoff=0)
- self.vlc_menu.add_command(label="📸 Snapshot", command=self.snapshot)
- self.vlc_menu.add_command(label="📺 Open in VLC", command=self.open_in_vlc)
- self.video_frame.bind("<Button-3>", self.show_vlc_menu)
- # =====================================================
- # PLAYLIST TAB + SCROLLBAR (FIXED)
- # =====================================================
- def add_playlist_tab(self, name):
- frame = tk.Frame(self.tab_control)
- self.tab_control.add(frame, text=name)
- container = tk.Frame(frame)
- container.pack(fill=tk.BOTH, expand=True)
- sb = ttk.Scrollbar(container, orient="vertical")
- sb.pack(side=tk.RIGHT, fill=tk.Y)
- lb = tk.Listbox(container, yscrollcommand=sb.set, exportselection=False)
- lb.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
- sb.config(command=lb.yview)
- lb.bind("<Double-1>", lambda e: self.on_double_click())
- self.playlist_boxes.append(lb)
- def new_tab(self):
- self.playlists.append([])
- self.titles.append([])
- self.add_playlist_tab(f"Playlist {len(self.playlists)}")
- def current_listbox(self):
- return self.playlist_boxes[self.tab_control.index("current")]
- def current_playlist_index(self):
- return self.tab_control.index("current")
- # =====================================================
- # PLAYBACK
- # =====================================================
- def on_double_click(self):
- sel = self.current_listbox().curselection()
- if sel:
- self.play_selected(sel[0])
- def play_selected(self, idx):
- tab = self.current_playlist_index()
- real = self.filtered_indices[idx] if self.filtered_indices else idx
- url = self.playlists[tab][real]
- self.show_busy("Buffering Stream Najeeb...")
- media = self.instance.media_new(url)
- self.player.set_media(media)
- self.player.play()
- self.is_playing = True
- self.root.after(6000, self.hide_busy)
- #def play_selected(self, idx):
- #tab = self.current_playlist_index()
- #real = self.filtered_indices[idx] if self.filtered_indices else idx
- #media = self.instance.media_new(self.playlists[tab][real])
- #self.player.set_media(media)
- #self.player.play()
- #self.is_playing = True
- def play(self): self.player.play(); self.is_playing = True
- def pause(self): self.player.pause()
- def stop(self): self.player.stop(); self.is_playing = False
- def set_volume(self, v):
- self.player.audio_set_volume(int(v))
- self.config["volume"] = int(v)
- self.save_config()
- def seek(self, v):
- if self.player.get_length() > 0:
- self.player.set_time(int(self.player.get_length() * float(v) / 100))
- def update_seek(self):
- if self.is_playing and self.player.get_length() > 0:
- self.seek_slider.set(
- (self.player.get_time() / self.player.get_length()) * 100
- )
- self.root.after(500, self.update_seek)
- # =====================================================
- # M3U
- # =====================================================
- def load_m3u(self):
- path = filedialog.askopenfilename(filetypes=[("M3U", "*.m3u *.m3u8")])
- if path:
- self.load_m3u_file(path)
- def load_m3u_file(self, path):
- idx = self.current_playlist_index()
- lb = self.current_listbox()
- lb.delete(0, tk.END)
- self.playlists[idx].clear()
- self.titles[idx].clear()
- with open(path, "r", encoding="utf-8", errors="ignore") as f:
- lines = f.read().splitlines()
- i = 0
- while i < len(lines):
- if lines[i].startswith("#EXTINF"):
- title = lines[i].split(",", 1)[-1]
- i += 1
- self.playlists[idx].append(lines[i])
- self.titles[idx].append(title)
- lb.insert(tk.END, title)
- i += 1
- self.config["last_file"] = path
- self.save_config()
- def add_item(self):
- url = simpledialog.askstring("M3U URL", "Enter M3U URL")
- if not url:
- return
- r = requests.get(url, timeout=15)
- lines = r.text.splitlines()
- idx = self.current_playlist_index()
- lb = self.current_listbox()
- lb.delete(0, tk.END)
- self.playlists[idx].clear()
- self.titles[idx].clear()
- i = 0
- while i < len(lines):
- if lines[i].startswith("#EXTINF"):
- title = lines[i].split(",", 1)[-1]
- i += 1
- self.playlists[idx].append(lines[i])
- self.titles[idx].append(title)
- lb.insert(tk.END, title)
- i += 1
- def save_m3u(self):
- idx = self.current_playlist_index()
- path = filedialog.asksaveasfilename(defaultextension=".m3u")
- if not path:
- return
- with open(path, "w", encoding="utf-8") as f:
- f.write("#EXTM3U\n")
- for t, u in zip(self.titles[idx], self.playlists[idx]):
- f.write(f"#EXTINF:-1,{t}\n{u}\n")
- # =====================================================
- # SEARCH
- # =====================================================
- def filter_playlist(self, *a):
- q = self.search_var.get().lower()
- lb = self.current_listbox()
- lb.delete(0, tk.END)
- self.filtered_indices.clear()
- idx = self.current_playlist_index()
- for i, t in enumerate(self.titles[idx]):
- if q in t.lower():
- lb.insert(tk.END, t)
- self.filtered_indices.append(i)
- def on_search_focus_in(self, e):
- if self.search_entry.get().startswith("🔍"):
- self.search_entry.delete(0, tk.END)
- def on_search_focus_out(self, e):
- if not self.search_var.get():
- self.search_entry.insert(0, "🔍 Search...")
- # =====================================================
- # THEME (FIXED)
- # =====================================================
- def toggle_theme(self):
- self.dark_mode = not self.dark_mode
- self.config["dark_mode"] = self.dark_mode
- self.apply_theme()
- self.save_config()
- def apply_theme(self):
- bg = "#1e1e1e" if self.dark_mode else "#f5f5f5"
- fg = "#ffffff" if self.dark_mode else "#000000"
- self.root.configure(bg=bg)
- self.left_frame.configure(bg=bg)
- self.right_frame.configure(bg=bg)
- self.search_entry.configure(bg=bg, fg=fg, insertbackground=fg)
- for lb in self.playlist_boxes:
- lb.configure(
- bg="#252526" if self.dark_mode else "#ffffff",
- fg=fg,
- selectbackground="#3e3e42" if self.dark_mode else "#0078d7",
- selectforeground="white"
- )
- # =====================================================
- # MENU / SHORTCUTS
- # =====================================================
- def show_vlc_menu(self, e):
- self.vlc_menu.tk_popup(e.x_root, e.y_root)
- def snapshot(self):
- name = f"snapshot_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
- self.player.video_take_snapshot(0, name, 0, 0)
- messagebox.showinfo("Saved", name)
- def open_in_vlc(self):
- media = self.player.get_media()
- if media and os.path.exists(VLC_EXE):
- os.spawnl(os.P_NOWAIT, VLC_EXE, VLC_EXE, media.get_mrl())
- # === SHORTCUTS ===
- # =================
- def setup_shortcuts(self):
- r = self.root
- r.bind("<space>", lambda e: self.pause() if self.is_playing else self.play())
- r.bind("<Return>", lambda e: self.on_double_click())
- r.bind("<F11>", lambda e: self.toggle_fullscreen())
- r.bind("<Escape>", lambda e: self.exit_fullscreen())
- r.bind('<Delete>', lambda e: self.remove_selected())
- r.bind('<Control-o>', lambda e: self.load_m3u())
- r.bind('<Control-n>', lambda e: self.new_tab())
- #r.bind('<Up>', self.navigate_list)
- #r.bind('<Down>', self.navigate_list)
- # New shortcuts for snapshot and open in VLC
- r.bind('<Control-s>', lambda e: self.snapshot())
- r.bind('<Control-v>', lambda e: self.open_in_vlc())
- # =====================================================
- # FULLSCREEN
- # =====================================================
- def toggle_fullscreen(self):
- self.in_fullscreen = not self.in_fullscreen
- if self.in_fullscreen:
- self.left_frame.pack_forget()
- self.fs_btn.config(text="➖ Exit Fullscreen")
- else:
- self.left_frame.pack(side=tk.LEFT, fill=tk.Y)
- self.fs_btn.config(text="➕ Fullscreen")
- def exit_fullscreen(self):
- if self.in_fullscreen:
- self.toggle_fullscreen()
- # =====================================================
- # CONFIG
- # =====================================================
- def load_config(self):
- if os.path.exists(CONFIG_FILE):
- try:
- with open(CONFIG_FILE, "r", encoding="utf-8") as f:
- return json.load(f)
- except:
- pass
- return {}
- def save_config(self):
- with open(CONFIG_FILE, "w", encoding="utf-8") as f:
- json.dump(self.config, f, indent=2)
- # =====================================================
- # RUN
- # =====================================================
- if __name__ == "__main__":
- root = tk.Tk()
- IPTVPlayerV3(root)
- root.mainloop()
Add Comment
Please, Sign In to add comment