Najeebsk

MP3-LIVE.pyw

May 2nd, 2025
51
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 22.25 KB | None | 0 0
  1. import os
  2. import sqlite3
  3. import tempfile
  4. from tkinter import *
  5. from tkinter import filedialog, messagebox, ttk
  6. import vlc
  7. from PIL import Image, ImageTk, ImageSequence
  8. import sys
  9. import configparser
  10. import hashlib
  11. import base64
  12. #================ADD-IMAGE-ICON=================
  13. import sys
  14.  
  15. def resource_path(relative_path):
  16.     """ Get the absolute path to the resource, works for PyInstaller. """
  17.     if getattr(sys, '_MEIPASS', False):
  18.         return os.path.join(sys._MEIPASS, relative_path)
  19.     return os.path.join(os.path.abspath("."), relative_path)
  20.  
  21. # Use this function to load files:
  22. #splash_image = resource_path("splash-1.png")
  23. icon_path = resource_path("M.ico")
  24. #-------------------------------------------
  25. def resource_path(relative_path):
  26.     """ Get absolute path to resource, works for dev and for PyInstaller """
  27.     try:
  28.         # PyInstaller creates a temp folder and stores path in _MEIPASS
  29.         base_path = sys._MEIPASS
  30.     except Exception:
  31.         base_path = os.path.abspath(".")
  32.  
  33.     return os.path.join(base_path, relative_path)
  34. #================ADD-IMAGE-ICON=================
  35. class Mp3PlayerApp:
  36.     def __init__(self, root):
  37.         self.root = root
  38.         self.root.title("Najeeb Advanced MP3 Player")
  39.         self.root.geometry("1200x700")
  40.         self.root.config(bg="#f0f0f0")
  41.         #self.root.configure(bg="#2c3e50")
  42.         self.root.iconbitmap(icon_path)
  43.  
  44.         # Initialize VLC
  45.         self.vlc_instance = vlc.Instance()
  46.         self.media_player = self.vlc_instance.media_player_new()
  47.  
  48.         # Security
  49.         self.secure_mode = False
  50.         self.password_hash = self.load_password_hash()
  51.  
  52.         # DB Setup
  53.         self.db_path = "M-DATA.db"
  54.         self.connect_database()
  55.  
  56.         # Stream data
  57.         self.streams = {}
  58.         self.current_streams = {}
  59.  
  60.         # Temp files list
  61.         self.temp_files = []
  62.  
  63.         # UI Setup
  64.         self.setup_theme()
  65.         self.create_menu()
  66.         self.create_notebook()
  67.         self.create_media_controls()
  68.         self.create_status_bar()
  69.         self.load_gif("MP3.gif")
  70.         self.load_mp3s_from_database()
  71.  
  72.     def setup_theme(self):
  73.         style = ttk.Style()
  74.         style.theme_use('clam')
  75.         style.configure("TButton", padding=6, relief="flat", background="#4CAF50")
  76.         style.configure("TFrame", background="#f0f0f0")
  77.         style.configure("Treeview", background="#ffffff", fieldbackground="#f0f0f0")
  78.         style.configure("TNotebook.Tab", padding=[10, 5], background="#e0e0e0")
  79.         self.root.option_add("*Font", "Helvetica 10")
  80.         self.root.option_add("*Background", "#f0f0f0")
  81.         self.root.option_add("*Button.Background", "#4CAF50")
  82.         self.root.option_add("*Button.Foreground", "white")
  83.  
  84.     def create_menu(self):
  85.         menubar = Menu(self.root)
  86.        
  87.         filemenu = Menu(menubar, tearoff=0)
  88.         filemenu.add_command(label="Open MP3 Database", command=self.select_database)
  89.         filemenu.add_command(label="Open Streams File", command=self.select_streams_file)
  90.         filemenu.add_separator()
  91.         filemenu.add_command(label="Exit", command=self.root.quit)
  92.         menubar.add_cascade(label="File", menu=filemenu)
  93.        
  94.         securitymenu = Menu(menubar, tearoff=0)
  95.         securitymenu.add_command(label="Set Password", command=self.set_password)
  96.         securitymenu.add_command(label="Lock Application", command=self.lock_application)
  97.         securitymenu.add_checkbutton(label="Secure Mode", variable=IntVar(value=1), command=self.toggle_secure_mode)
  98.         menubar.add_cascade(label="Security", menu=securitymenu)
  99.        
  100.         self.root.config(menu=menubar)
  101.  
  102.     def create_notebook(self):
  103.         self.notebook = ttk.Notebook(self.root)
  104.         self.notebook.pack(fill=BOTH, expand=True, padx=10, pady=10)
  105.  
  106.         # Music Tab
  107.         music_tab = ttk.Frame(self.notebook)
  108.         self.notebook.add(music_tab, text='Music Library')
  109.         self.create_music_ui(music_tab)
  110.  
  111.         # Radio Tab
  112.         radio_tab = ttk.Frame(self.notebook)
  113.         self.notebook.add(radio_tab, text='Live Radio')
  114.         self.create_radio_ui(radio_tab)
  115.  
  116.         # Settings Tab
  117.         settings_tab = ttk.Frame(self.notebook)
  118.         self.notebook.add(settings_tab, text='Settings')
  119.         self.create_settings_ui(settings_tab)
  120.  
  121.     def create_music_ui(self, parent):
  122.         main_frame = ttk.Frame(parent)
  123.         main_frame.pack(fill=BOTH, expand=True, padx=10, pady=10)
  124.         button_frame = ttk.Frame(main_frame)
  125.         button_frame.pack(fill=X, pady=5)
  126.         Button(button_frame, text="Browse Folder", command=self.browse_folder).pack(side=LEFT, padx=5)
  127.         Button(button_frame, text="Add MP3", command=self.add_mp3_file).pack(side=LEFT, padx=5)
  128.         Button(button_frame, text="Delete MP3", command=self.delete_mp3_file).pack(side=LEFT, padx=5)
  129.         Button(button_frame, text="Save MP3", command=self.save_mp3_to_database).pack(side=LEFT, padx=5)
  130.         Button(button_frame, text="Delete Temp", command=self.delete_temp_files).pack(side=LEFT, padx=5)
  131.  
  132.         list_frame = ttk.Frame(main_frame)
  133.         list_frame.pack(fill=BOTH, expand=True)
  134.  
  135.         self.listbox = Listbox(list_frame, width=50, height=10)
  136.         self.listbox.pack(side=LEFT, fill=BOTH, expand=True)
  137.         self.listbox.bind("<<ListboxSelect>>", self.play_selected_song)
  138.  
  139.         scrollbar = Scrollbar(list_frame)
  140.         scrollbar.pack(side=RIGHT, fill=Y)
  141.  
  142.         self.listbox.config(yscrollcommand=scrollbar.set)
  143.         scrollbar.config(command=self.listbox.yview)
  144.  
  145.     def create_radio_ui(self, parent):
  146.         radio_frame = ttk.Frame(parent)
  147.         radio_frame.pack(fill=BOTH, expand=True, padx=10, pady=10)
  148.  
  149.         Label(radio_frame, text="Select Category:", font=("Helvetica", 10, "bold")).pack(anchor=W, pady=5)
  150.         self.category_var = StringVar()
  151.         self.category_combo = ttk.Combobox(radio_frame, textvariable=self.category_var, state="readonly", width=30)
  152.         self.category_combo.pack(pady=5)
  153.         self.category_combo.bind("<<ComboboxSelected>>", self.update_stations)
  154.  
  155.         Label(radio_frame, text="Select Station:", font=("Helvetica", 10, "bold")).pack(anchor=W, pady=5)
  156.         self.station_list = Listbox(radio_frame, width=50, height=10)
  157.         self.station_list.pack(fill=BOTH, expand=True)
  158.         self.station_list.bind("<Double-1>", self.play_selected_station)
  159.  
  160.         self.stream_info = Label(radio_frame, text="Double-click a station to play", fg="blue", cursor="hand2")
  161.         self.stream_info.pack(pady=5)
  162.         self.stream_info.bind("<Button-1>", lambda e: self.open_stream_url())
  163.  
  164.     def create_settings_ui(self, parent):
  165.         settings_frame = ttk.Frame(parent)
  166.         settings_frame.pack(fill=BOTH, expand=True, padx=10, pady=10)
  167.  
  168.         Label(settings_frame, text="Security Settings", font=("Helvetica", 12, "bold")).pack(anchor=W, pady=5)
  169.         self.secure_mode_var = IntVar(value=1)
  170.         Checkbutton(settings_frame, text="Enable Secure Mode", variable=self.secure_mode_var, command=self.toggle_secure_mode).pack(anchor=W)
  171.         Button(settings_frame, text="Set Password", command=self.set_password).pack(anchor=W, pady=5)
  172.         Button(settings_frame, text="Lock Application", command=self.lock_application).pack(anchor=W)
  173.  
  174.         Label(settings_frame, text="About", font=("Helvetica", 12, "bold")).pack(anchor=W, pady=5)
  175.         Label(settings_frame, text="MP3 Player v3.0\nDeveloped by Najeeb Shah Khan").pack(anchor=W)
  176.  
  177.     def create_media_controls(self):
  178.         control_frame = ttk.Frame(self.root)
  179.         control_frame.pack(fill=X, padx=10, pady=10)
  180.  
  181.         Button(control_frame, text="Play", command=self.play_selected_song).pack(side=LEFT, padx=5)
  182.         Button(control_frame, text="Stop", command=self.stop_song).pack(side=LEFT, padx=5)
  183.         Button(control_frame, text="Previous", command=self.play_previous).pack(side=LEFT, padx=5)
  184.         Button(control_frame, text="Next", command=self.play_next).pack(side=LEFT, padx=5)
  185.  
  186.         self.position_slider = Scale(control_frame, from_=0, to=100, orient=HORIZONTAL, length=300, command=self.seek_position)
  187.         self.position_slider.pack(side=LEFT, padx=10)
  188.  
  189.         Label(control_frame, text="Volume").pack(side=LEFT)
  190.         self.volume_slider = Scale(control_frame, from_=0, to=100, orient=HORIZONTAL, length=100, command=self.set_volume)
  191.         self.volume_slider.set(50)
  192.         self.volume_slider.pack(side=LEFT, padx=10)
  193.  
  194.     def create_status_bar(self):
  195.         self.status_bar = Label(self.root, text="Ready", bd=1, relief=SUNKEN, anchor=W)
  196.         self.status_bar.pack(side=BOTTOM, fill=X)
  197.  
  198.     def connect_database(self):
  199.         try:
  200.             self.db_connection = sqlite3.connect(self.db_path)
  201.             self.cursor = self.db_connection.cursor()
  202.             self.cursor.execute('''CREATE TABLE IF NOT EXISTS mp3_files
  203.                                 (id INTEGER PRIMARY KEY, filename TEXT, filedata BLOB)''')
  204.             self.db_connection.commit()
  205.         except Exception as e:
  206.             messagebox.showerror("Database Error", f"Failed to connect to database: {e}")
  207.  
  208.     def select_database(self):
  209.         db_path = filedialog.askopenfilename(
  210.             title="Select MP3 Database",
  211.             filetypes=[("SQLite Database", "*.db"), ("All files", "*.*")]
  212.         )
  213.         if db_path:
  214.             self.db_path = db_path
  215.             self.connect_database()
  216.             self.load_mp3s_from_database()
  217.             self.update_status("Database loaded successfully")
  218.  
  219.     def select_streams_file(self):
  220.         ini_path = filedialog.askopenfilename(
  221.             title="Select Streams File",
  222.             filetypes=[("INI Config", "*.ini"), ("All files", "*.*")]
  223.         )
  224.         if ini_path:
  225.             self.load_streams_file(ini_path)
  226.             self.update_status("Streams file loaded successfully")
  227.  
  228.     def load_streams_file(self, ini_path):
  229.         config = configparser.ConfigParser()
  230.         try:
  231.            
  232.             config.read(ini_path)
  233.             self.streams = {sect: dict(config.items(sect)) for sect in config.sections()}
  234.             if self.streams:
  235.                 self.category_combo['values'] = list(self.streams.keys())
  236.                 self.category_combo.current(0)
  237.                 self.update_stations()
  238.             else:
  239.                 messagebox.showerror("Error", "No valid sections found in stream file.")
  240.         except Exception as e:
  241.             messagebox.showerror("Error", f"Failed to load stream file: {e}")
  242.  
  243.     def update_stations(self, event=None):
  244.         selected_category = self.category_var.get()
  245.         if selected_category in self.streams:
  246.             self.station_list.delete(0, END)
  247.             for station in self.streams[selected_category]:
  248.                 self.station_list.insert(END, station)
  249.             self.current_streams = self.streams[selected_category]
  250.  
  251.     def play_selected_station(self, event=None):
  252.         if self.secure_mode and not self.authenticate():
  253.             return
  254.  
  255.         selected_index = self.station_list.curselection()
  256.         if not selected_index:
  257.             return
  258.  
  259.         station_name = self.station_list.get(selected_index)
  260.         stream_url = self.current_streams.get(station_name)
  261.  
  262.         if not stream_url:
  263.             messagebox.showerror("Error", "Stream URL not found.")
  264.             return
  265.  
  266.         try:
  267.             media = self.vlc_instance.media_new(stream_url)
  268.             self.media_player.set_media(media)
  269.             self.media_player.play()
  270.             self.update_status(f"Playing: {station_name}")
  271.             self.current_stream_url = stream_url
  272.         except Exception as e:
  273.             messagebox.showerror("Error", f"Failed to play stream: {e}")
  274.  
  275.     def open_stream_url(self):
  276.         if hasattr(self, 'current_stream_url'):
  277.             import webbrowser
  278.             webbrowser.open(self.current_stream_url)
  279.  
  280.     def set_password(self):
  281.         if self.secure_mode and not self.authenticate():
  282.             return
  283.  
  284.         new_pass = self.get_password_dialog("Set New Password", "Enter new password:")
  285.         if new_pass:
  286.             self.password_hash = self.hash_password(new_pass)
  287.             self.save_password_hash()
  288.             messagebox.showinfo("Success", "Password set successfully")
  289.             self.update_status("Password set")
  290.  
  291.     def lock_application(self):
  292.         if self.authenticate():
  293.             self.root.withdraw()
  294.             self.root.after(100, self.show_login_dialog)
  295.  
  296.     def toggle_secure_mode(self):
  297.         self.secure_mode = self.secure_mode_var.get() == 1
  298.         status = "enabled" if self.secure_mode else "disabled"
  299.         self.update_status(f"Secure mode {status}")
  300.  
  301.     def authenticate(self):
  302.         if not self.secure_mode or not self.password_hash:
  303.             return True
  304.  
  305.         password = self.get_password_dialog("Authentication Required", "Enter password:")
  306.         if password and self.hash_password(password) == self.password_hash:
  307.             return True
  308.         return False
  309.  
  310.     def get_password_dialog(self, title, prompt):
  311.         dialog = Toplevel(self.root)
  312.         dialog.title(title)
  313.         dialog.geometry("300x120")
  314.         dialog.transient(self.root)
  315.         dialog.grab_set()
  316.  
  317.         Label(dialog, text=prompt).pack(pady=5)
  318.         password_var = StringVar()
  319.         entry = Entry(dialog, textvariable=password_var, show="*")
  320.         entry.pack(pady=5)
  321.  
  322.         result = [None]
  323.  
  324.         def on_ok():
  325.             result[0] = password_var.get()
  326.             dialog.destroy()
  327.  
  328.         Button(dialog, text="OK", command=on_ok).pack(side=LEFT, padx=20)
  329.         Button(dialog, text="Cancel", command=dialog.destroy).pack(side=RIGHT, padx=20)
  330.  
  331.         self.root.wait_window(dialog)
  332.         return result[0]
  333.  
  334.     def hash_password(self, password):
  335.         salt = b'salt_123'
  336.         return base64.b64encode(hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)).decode()
  337.  
  338.     def load_password_hash(self):
  339.         try:
  340.             with open('config.dat', 'r') as f:
  341.                 return f.read().strip()
  342.         except:
  343.             return None
  344.  
  345.     def save_password_hash(self):
  346.         try:
  347.             with open('config.dat', 'w') as f:
  348.                 f.write(self.password_hash or '')
  349.         except Exception as e:
  350.             messagebox.showerror("Error", f"Failed to save password: {e}")
  351.  
  352.     def show_login_dialog(self):
  353.         login_window = Toplevel()
  354.         login_window.title("Login")
  355.         login_window.geometry("300x120")
  356.         login_window.transient(self.root)
  357.  
  358.         Label(login_window, text="Enter password to unlock:").pack(pady=5)
  359.         password_var = StringVar()
  360.         entry = Entry(login_window, textvariable=password_var, show="*")
  361.         entry.pack(pady=5)
  362.  
  363.         def on_login():
  364.             if self.hash_password(password_var.get()) == self.password_hash:
  365.                 login_window.destroy()
  366.                 self.root.deiconify()
  367.             else:
  368.                 messagebox.showerror("Error", "Invalid password")
  369.  
  370.         Button(login_window, text="Login", command=on_login).pack()
  371.  
  372.         login_window.protocol("WM_DELETE_WINDOW", self.root.quit)
  373.  
  374.     def update_status(self, message):
  375.         self.status_bar.config(text=message)
  376.  
  377.     def load_gif(self, gif_path):
  378.         if getattr(sys, 'frozen', False):
  379.             base_path = sys._MEIPASS
  380.         else:
  381.             base_path = os.path.dirname(__file__)
  382.         full_path = os.path.join(base_path, gif_path)
  383.         self.gif_label = Label(self.root)
  384.         self.gif_label.pack(pady=10)
  385.         self.gif = Image.open(full_path)
  386.         self.frames = [ImageTk.PhotoImage(frame) for frame in ImageSequence.Iterator(self.gif)]
  387.         self.gif_index = 0
  388.         self.gif_label.configure(image=self.frames[0])
  389.         self.update_gif()
  390.  
  391.     def update_gif(self):
  392.         self.gif_index = (self.gif_index + 1) % len(self.frames)
  393.         self.gif_label.configure(image=self.frames[self.gif_index])
  394.         self.root.after(100, self.update_gif)
  395.  
  396.     def load_mp3s_from_database(self):
  397.         self.listbox.delete(0, END)
  398.         try:
  399.             self.cursor.execute("SELECT filename FROM mp3_files")
  400.             for row in self.cursor.fetchall():
  401.                 self.listbox.insert(END, row[0])
  402.         except Exception as e:
  403.             messagebox.showerror("Error", f"Failed to load MP3 files: {e}")
  404.  
  405.     def play_selected_song(self, event=None):
  406.         selected_index = self.listbox.curselection()
  407.         if not selected_index:
  408.             return
  409.         song_name = self.listbox.get(selected_index)
  410.         try:
  411.             self.cursor.execute("SELECT filedata FROM mp3_files WHERE filename = ?", (song_name,))
  412.             song_data = self.cursor.fetchone()[0]
  413.             with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_file:
  414.                 temp_file.write(song_data)
  415.                 temp_file_path = temp_file.name
  416.                 self.temp_files.append(temp_file_path)
  417.             self.media_player.set_media(self.vlc_instance.media_new(temp_file_path))
  418.             self.media_player.play()
  419.             self.update_position_slider()
  420.         except Exception as e:
  421.             messagebox.showerror("Error", f"Failed to play song: {e}")
  422.  
  423.     def stop_song(self):
  424.         self.media_player.stop()
  425.         self.position_slider.set(0)
  426.  
  427.     def play_next(self):
  428.         current_index = self.listbox.curselection()[0]
  429.         next_index = (current_index + 1) % self.listbox.size()
  430.         self.listbox.select_clear(current_index)
  431.         self.listbox.select_set(next_index)
  432.         self.play_selected_song()
  433.  
  434.     def play_previous(self):
  435.         current_index = self.listbox.curselection()[0]
  436.         prev_index = (current_index - 1) % self.listbox.size()
  437.         self.listbox.select_clear(current_index)
  438.         self.listbox.select_set(prev_index)
  439.         self.play_selected_song()
  440.  
  441.     def set_volume(self, volume):
  442.         self.media_player.audio_set_volume(int(volume))
  443.  
  444.     def update_position_slider(self):
  445.         if self.media_player.is_playing():
  446.             current_pos = self.media_player.get_time() // 1000
  447.             self.position_slider.set(current_pos)
  448.             self.root.after(1000, self.update_position_slider)
  449.  
  450.     def seek_position(self, position):
  451.         self.media_player.set_time(int(position) * 1000)
  452.  
  453.     def browse_folder(self):
  454.         folder_path = filedialog.askdirectory()
  455.         if not folder_path:
  456.             return
  457.         self.listbox.delete(0, END)
  458.         try:
  459.             self.cursor.execute("DELETE FROM mp3_files")
  460.             self.db_connection.commit()
  461.             for filename in os.listdir(folder_path):
  462.                 if filename.endswith(".mp3"):
  463.                     file_path = os.path.join(folder_path, filename)
  464.                     with open(file_path, 'rb') as file:
  465.                         file_data = file.read()
  466.                         self.cursor.execute("INSERT INTO mp3_files (filename, filedata) VALUES (?, ?)", (filename, file_data))
  467.             self.db_connection.commit()
  468.             self.load_mp3s_from_database()
  469.             messagebox.showinfo("Success", "MP3 files loaded from folder and saved to database!")
  470.         except Exception as e:
  471.             messagebox.showerror("Error", f"Failed to load folder: {e}")
  472.  
  473.     def add_mp3_file(self):
  474.         file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")])
  475.         if not file_path:
  476.             return
  477.         try:
  478.             filename = os.path.basename(file_path)
  479.             with open(file_path, 'rb') as file:
  480.                 file_data = file.read()
  481.                 self.cursor.execute("INSERT INTO mp3_files (filename, filedata) VALUES (?, ?)", (filename, file_data))
  482.             self.db_connection.commit()
  483.             self.listbox.insert(END, filename)
  484.             messagebox.showinfo("Success", "MP3 file added to database!")
  485.         except Exception as e:
  486.             messagebox.showerror("Error", f"Failed to add MP3: {e}")
  487.  
  488.     def delete_mp3_file(self):
  489.         selected_index = self.listbox.curselection()
  490.         if not selected_index:
  491.             return
  492.         try:
  493.             song_name = self.listbox.get(selected_index)
  494.             self.cursor.execute("DELETE FROM mp3_files WHERE filename = ?", (song_name,))
  495.             self.db_connection.commit()
  496.             self.listbox.delete(selected_index)
  497.             messagebox.showinfo("Deleted", "MP3 file deleted from database.")
  498.         except Exception as e:
  499.             messagebox.showerror("Error", f"Failed to delete MP3: {e}")
  500.  
  501.     def save_mp3_to_database(self):
  502.         selected_index = self.listbox.curselection()
  503.         if not selected_index:
  504.             messagebox.showwarning("Warning", "Please select an MP3 file to save.")
  505.             return
  506.         try:
  507.             song_name = self.listbox.get(selected_index)
  508.             folder_path = filedialog.askdirectory()
  509.             if not folder_path:
  510.                 return
  511.             self.cursor.execute("SELECT filedata FROM mp3_files WHERE filename = ?", (song_name,))
  512.             file_data = self.cursor.fetchone()
  513.             if file_data:
  514.                 file_path = os.path.join(folder_path, song_name)
  515.                 with open(file_path, 'wb') as file:
  516.                     file.write(file_data[0])
  517.                 messagebox.showinfo("Success", f"MP3 file '{song_name}' saved to selected folder.")
  518.             else:
  519.                 messagebox.showerror("Error", "File data not found in database.")
  520.         except Exception as e:
  521.             messagebox.showerror("Error", f"Failed to save MP3: {e}")
  522.  
  523.     def delete_temp_files(self):
  524.         self.media_player.stop()
  525.         deleted_files = []
  526.         for temp_file in self.temp_files:
  527.             try:
  528.                 if os.path.exists(temp_file):
  529.                     os.remove(temp_file)
  530.                     deleted_files.append(temp_file)
  531.             except Exception as e:
  532.                 messagebox.showerror("Error", f"Failed to delete {temp_file}: {e}")
  533.         self.temp_files.clear()
  534.         if deleted_files:
  535.             messagebox.showinfo("Success", f"Temporary files deleted: {', '.join(deleted_files)}")
  536.         else:
  537.             messagebox.showinfo("Success", "No temporary files to delete.")
  538.  
  539. if __name__ == "__main__":
  540.     root = Tk()
  541.     app = Mp3PlayerApp(root)
  542.     root.mainloop()
  543.  
Add Comment
Please, Sign In to add comment