Najeebsk

NSK-IPTV-M3U.pyw

Apr 21st, 2025
17
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.53 KB | None | 0 0
  1. import tkinter as tk
  2. from tkinter import messagebox, filedialog, ttk
  3. import requests
  4. import re
  5.  
  6. class M3UTransformer:
  7.     def __init__(self, root):
  8.         self.root = root
  9.         self.root.title("NAJEEB M3U Transformer")
  10.         self.root.geometry("900x600")  # Set fixed window size
  11.         self.root.resizable(False, False)  # Lock window size
  12.         self.channels = []  # List to store channel data
  13.  
  14.         # Define a style for ttk widgets
  15.         self.style = ttk.Style()
  16.         self.style.theme_use("clam")  # Modern theme
  17.         self.style.configure("TLabel", font=("Arial", 10), background="#f0f0f0", foreground="#333333", padding=5)
  18.         self.style.configure("TButton", font=("Arial", 10), padding=5)
  19.         self.style.map("Download.TButton", background=[("active", "#4CAF50"), ("!active", "#45a049")])
  20.         self.style.map("Browse.TButton", background=[("active", "#2196F3"), ("!active", "#1E88E5")])
  21.         self.style.map("Convert.TButton", background=[("active", "#FF9800"), ("!active", "#FB8C00")])
  22.         self.style.configure("TEntry", font=("Arial", 10), padding=5)
  23.  
  24.         # GUI Components
  25.         self.create_widgets()
  26.  
  27.     def create_widgets(self):
  28.         # URL Entry
  29.         ttk.Label(self.root, text="Enter M3U URL:").grid(row=0, column=0, sticky="e", padx=10, pady=10)
  30.         self.url_entry = ttk.Entry(self.root, width=90)
  31.         self.url_entry.grid(row=0, column=1, padx=10, pady=10)
  32.  
  33.         # Download Button
  34.         ttk.Button(self.root, text="Download & Transform", style="Download.TButton", command=self.download_and_transform).grid(
  35.             row=0, column=2, padx=10, pady=10
  36.         )
  37.  
  38.         # Browse File Button
  39.         ttk.Button(self.root, text="Browse & Transform", style="Browse.TButton", command=self.browse_and_transform).grid(
  40.             row=1, column=2, padx=10, pady=10
  41.         )
  42.  
  43.         # Raw Text Conversion Section
  44.         ttk.Label(self.root, text="Paste Raw Text:").grid(row=1, column=0, sticky="ne", padx=10, pady=10)
  45.  
  46.         # Add a scrollbar to the raw text field
  47.         self.raw_text_frame = ttk.Frame(self.root)
  48.         self.raw_text_frame.grid(row=2, column=1, padx=10, pady=10, sticky="nsew")
  49.  
  50.         self.raw_text_scrollbar = ttk.Scrollbar(self.raw_text_frame, orient="vertical")
  51.         self.raw_text_scrollbar.pack(side="right", fill="y")
  52.  
  53.         self.raw_text = tk.Text(self.raw_text_frame, width=50, height=10, yscrollcommand=self.raw_text_scrollbar.set, bg="#ffffff", fg="#333333", font=("Arial", 10))
  54.         self.raw_text.pack(side="left", fill="both", expand=True)
  55.         self.raw_text_scrollbar.config(command=self.raw_text.yview)
  56.  
  57.         # Convert Raw Text Button
  58.         ttk.Button(self.root, text="Convert Raw Text", style="Convert.TButton", command=self.convert_raw_text).grid(
  59.             row=2, column=2, padx=10, pady=10
  60.         )
  61.  
  62.         # Status Label
  63.         self.status_label = ttk.Label(self.root, text="Enter M3U URL, browse for a file, or paste raw text.", background="#f0f0f0", foreground="#333333")
  64.         self.status_label.grid(row=3, column=0, columnspan=3, pady=20)
  65.  
  66.         # Configure grid to make it responsive
  67.         self.root.grid_rowconfigure(2, weight=1)
  68.         self.root.grid_columnconfigure(1, weight=1)
  69.  
  70.     def download_and_transform(self):
  71.         """Download the M3U file from the provided URL and transform it."""
  72.         m3u_url = self.url_entry.get().strip()
  73.         if not m3u_url:
  74.             messagebox.showwarning("Warning", "Please enter a valid M3U URL.")
  75.             return
  76.  
  77.         try:
  78.             # Download the M3U file
  79.             self.status_label.config(text="Downloading M3U file...")
  80.             response = requests.get(m3u_url)
  81.             response.raise_for_status()  # Raise an error for bad responses (4xx, 5xx)
  82.             m3u_content = response.text.splitlines()
  83.  
  84.             # Transform and save the M3U file
  85.             self.transform_and_save(m3u_content, source="URL")
  86.         except requests.exceptions.RequestException as e:
  87.             self.status_label.config(text="Error downloading M3U file.")
  88.             messagebox.showerror("Error", f"Failed to download M3U file: {e}")
  89.         except Exception as e:
  90.             self.status_label.config(text="Error transforming M3U file.")
  91.             messagebox.showerror("Error", f"An error occurred: {e}")
  92.  
  93.     def browse_and_transform(self):
  94.         """Browse for an M3U file on the local PC and transform it."""
  95.         file_path = filedialog.askopenfilename(filetypes=[("M3U Files", "*.m3u")])
  96.         if not file_path:
  97.             return
  98.  
  99.         try:
  100.             # Read the M3U file
  101.             self.status_label.config(text="Reading local M3U file...")
  102.             with open(file_path, "r", encoding="utf-8") as file:
  103.                 m3u_content = file.readlines()
  104.  
  105.             # Transform and save the M3U file
  106.             self.transform_and_save(m3u_content, source="Local File")
  107.         except Exception as e:
  108.             self.status_label.config(text="Error reading M3U file.")
  109.             messagebox.showerror("Error", f"An error occurred: {e}")
  110.  
  111.     def convert_raw_text(self):
  112.         """Convert raw text input into an M3U file."""
  113.         raw_input = self.raw_text.get("1.0", tk.END).strip()
  114.         if not raw_input:
  115.             messagebox.showwarning("Warning", "Please paste raw text containing channel names and URLs.")
  116.             return
  117.  
  118.         try:
  119.             # Parse raw text into channel name and stream URL pairs
  120.             self.status_label.config(text="Parsing raw text...")
  121.             lines = raw_input.splitlines()
  122.             self.channels.clear()
  123.  
  124.             for line in lines:
  125.                 match = re.match(r"^(.+)\s+(https?://.+)$", line.strip())
  126.                 if match:
  127.                     channel_name = match.group(1).strip()
  128.                     stream_url = match.group(2).strip()
  129.  
  130.                     # Construct new EXTINF line in the desired format
  131.                     new_extinf = (
  132.                         f'#EXTINF:-1 tvg-name="{channel_name}" '
  133.                         f'tvg-id="{channel_name.lower().replace(" ", "_")}" '
  134.                         f'tvg-logo="" tvg-subtitle="", {channel_name}'
  135.                     )
  136.                     self.channels.append((new_extinf, stream_url))
  137.  
  138.             # Save the transformed M3U file locally
  139.             output_file = "converted_playlist.m3u"
  140.             with open(output_file, "w", encoding="utf-8") as file:
  141.                 for extinf, url in self.channels:
  142.                     file.write(extinf + "\n")
  143.                     file.write(url + "\n")
  144.  
  145.             self.status_label.config(text=f"Conversion complete! Saved as '{output_file}'")
  146.             messagebox.showinfo("Success", f"Converted M3U file saved as '{output_file}'")
  147.         except Exception as e:
  148.             self.status_label.config(text="Error converting raw text.")
  149.             messagebox.showerror("Error", f"An error occurred: {e}")
  150.  
  151.     def transform_and_save(self, m3u_content, source):
  152.         """Transform the M3U content and save it to a new file."""
  153.         self.channels.clear()
  154.         i = 0
  155.         while i < len(m3u_content):
  156.             line = m3u_content[i].strip()
  157.             if line.startswith("#EXTINF"):
  158.                 # Extract channel name from the EXTINF line
  159.                 match = re.search(r',\s*(.+)', line)
  160.                 if match:
  161.                     channel_name = match.group(1)
  162.                     stream_url = m3u_content[i + 1].strip()
  163.  
  164.                     # Construct new EXTINF line in the desired format
  165.                     new_extinf = (
  166.                         f'#EXTINF:-1 tvg-name="{channel_name}" '
  167.                         f'tvg-id="{channel_name.lower().replace(" ", "_")}" '
  168.                         f'tvg-logo="" tvg-subtitle="", {channel_name}'
  169.                     )
  170.                     self.channels.append((new_extinf, stream_url))
  171.                 i += 2
  172.             else:
  173.                 i += 1
  174.  
  175.         # Save the transformed M3U file locally
  176.         output_file = "transformed_playlist.m3u"
  177.         with open(output_file, "w", encoding="utf-8") as file:
  178.             for extinf, url in self.channels:
  179.                 file.write(extinf + "\n")
  180.                 file.write(url + "\n")
  181.  
  182.         self.status_label.config(text=f"Transformation complete! Saved as '{output_file}'")
  183.         messagebox.showinfo(
  184.             "Success",
  185.             f"Transformed M3U file saved as '{output_file}' (Source: {source})"
  186.         )
  187.  
  188.  
  189. if __name__ == "__main__":
  190.     root = tk.Tk()
  191.     app = M3UTransformer(root)
  192.     root.mainloop()
  193.  
Add Comment
Please, Sign In to add comment