import os
import sqlite3
import tempfile
from tkinter import *
from tkinter import filedialog, messagebox, ttk
import vlc
from PIL import Image, ImageTk, ImageSequence
import sys
import configparser
import hashlib
import base64
#================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("splash-1.png")
icon_path = resource_path("M.ico")
#-------------------------------------------
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
#================ADD-IMAGE-ICON=================
class Mp3PlayerApp:
def __init__(self, root):
self.root = root
self.root.title("Najeeb Advanced MP3 Player")
self.root.geometry("1200x700")
self.root.config(bg="#f0f0f0")
#self.root.configure(bg="#2c3e50")
self.root.iconbitmap(icon_path)
# Initialize VLC
self.vlc_instance = vlc.Instance()
self.media_player = self.vlc_instance.media_player_new()
# Security
self.secure_mode = False
self.password_hash = self.load_password_hash()
# DB Setup
self.db_path = "M-DATA.db"
self.connect_database()
# Stream data
self.streams = {}
self.current_streams = {}
# Temp files list
self.temp_files = []
# UI Setup
self.setup_theme()
self.create_menu()
self.create_notebook()
self.create_media_controls()
self.create_status_bar()
self.load_gif("MP3.gif")
self.load_mp3s_from_database()
def setup_theme(self):
style = ttk.Style()
style.theme_use('clam')
style.configure("TButton", padding=6, relief="flat", background="#4CAF50")
style.configure("TFrame", background="#f0f0f0")
style.configure("Treeview", background="#ffffff", fieldbackground="#f0f0f0")
style.configure("TNotebook.Tab", padding=[10, 5], background="#e0e0e0")
self.root.option_add("*Font", "Helvetica 10")
self.root.option_add("*Background", "#f0f0f0")
self.root.option_add("*Button.Background", "#4CAF50")
self.root.option_add("*Button.Foreground", "white")
def create_menu(self):
menubar = Menu(self.root)
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="Open MP3 Database", command=self.select_database)
filemenu.add_command(label="Open Streams File", command=self.select_streams_file)
filemenu.add_separator()
filemenu.add_command(label="Exit", command=self.root.quit)
menubar.add_cascade(label="File", menu=filemenu)
securitymenu = Menu(menubar, tearoff=0)
securitymenu.add_command(label="Set Password", command=self.set_password)
securitymenu.add_command(label="Lock Application", command=self.lock_application)
securitymenu.add_checkbutton(label="Secure Mode", variable=IntVar(value=1), command=self.toggle_secure_mode)
menubar.add_cascade(label="Security", menu=securitymenu)
self.root.config(menu=menubar)
def create_notebook(self):
self.notebook = ttk.Notebook(self.root)
self.notebook.pack(fill=BOTH, expand=True, padx=10, pady=10)
# Music Tab
music_tab = ttk.Frame(self.notebook)
self.notebook.add(music_tab, text='Music Library')
self.create_music_ui(music_tab)
# Radio Tab
radio_tab = ttk.Frame(self.notebook)
self.notebook.add(radio_tab, text='Live Radio')
self.create_radio_ui(radio_tab)
# Settings Tab
settings_tab = ttk.Frame(self.notebook)
self.notebook.add(settings_tab, text='Settings')
self.create_settings_ui(settings_tab)
def create_music_ui(self, parent):
main_frame = ttk.Frame(parent)
main_frame.pack(fill=BOTH, expand=True, padx=10, pady=10)
button_frame = ttk.Frame(main_frame)
button_frame.pack(fill=X, pady=5)
Button(button_frame, text="Browse Folder", command=self.browse_folder).pack(side=LEFT, padx=5)
Button(button_frame, text="Add MP3", command=self.add_mp3_file).pack(side=LEFT, padx=5)
Button(button_frame, text="Delete MP3", command=self.delete_mp3_file).pack(side=LEFT, padx=5)
Button(button_frame, text="Save MP3", command=self.save_mp3_to_database).pack(side=LEFT, padx=5)
Button(button_frame, text="Delete Temp", command=self.delete_temp_files).pack(side=LEFT, padx=5)
list_frame = ttk.Frame(main_frame)
list_frame.pack(fill=BOTH, expand=True)
self.listbox = Listbox(list_frame, width=50, height=10)
self.listbox.pack(side=LEFT, fill=BOTH, expand=True)
self.listbox.bind("<<ListboxSelect>>", self.play_selected_song)
scrollbar = Scrollbar(list_frame)
scrollbar.pack(side=RIGHT, fill=Y)
self.listbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=self.listbox.yview)
def create_radio_ui(self, parent):
radio_frame = ttk.Frame(parent)
radio_frame.pack(fill=BOTH, expand=True, padx=10, pady=10)
Label(radio_frame, text="Select Category:", font=("Helvetica", 10, "bold")).pack(anchor=W, pady=5)
self.category_var = StringVar()
self.category_combo = ttk.Combobox(radio_frame, textvariable=self.category_var, state="readonly", width=30)
self.category_combo.pack(pady=5)
self.category_combo.bind("<<ComboboxSelected>>", self.update_stations)
Label(radio_frame, text="Select Station:", font=("Helvetica", 10, "bold")).pack(anchor=W, pady=5)
self.station_list = Listbox(radio_frame, width=50, height=10)
self.station_list.pack(fill=BOTH, expand=True)
self.station_list.bind("<Double-1>", self.play_selected_station)
self.stream_info = Label(radio_frame, text="Double-click a station to play", fg="blue", cursor="hand2")
self.stream_info.pack(pady=5)
self.stream_info.bind("<Button-1>", lambda e: self.open_stream_url())
def create_settings_ui(self, parent):
settings_frame = ttk.Frame(parent)
settings_frame.pack(fill=BOTH, expand=True, padx=10, pady=10)
Label(settings_frame, text="Security Settings", font=("Helvetica", 12, "bold")).pack(anchor=W, pady=5)
self.secure_mode_var = IntVar(value=1)
Checkbutton(settings_frame, text="Enable Secure Mode", variable=self.secure_mode_var, command=self.toggle_secure_mode).pack(anchor=W)
Button(settings_frame, text="Set Password", command=self.set_password).pack(anchor=W, pady=5)
Button(settings_frame, text="Lock Application", command=self.lock_application).pack(anchor=W)
Label(settings_frame, text="About", font=("Helvetica", 12, "bold")).pack(anchor=W, pady=5)
Label(settings_frame, text="MP3 Player v3.0\nDeveloped by Najeeb Shah Khan").pack(anchor=W)
def create_media_controls(self):
control_frame = ttk.Frame(self.root)
control_frame.pack(fill=X, padx=10, pady=10)
Button(control_frame, text="Play", command=self.play_selected_song).pack(side=LEFT, padx=5)
Button(control_frame, text="Stop", command=self.stop_song).pack(side=LEFT, padx=5)
Button(control_frame, text="Previous", command=self.play_previous).pack(side=LEFT, padx=5)
Button(control_frame, text="Next", command=self.play_next).pack(side=LEFT, padx=5)
self.position_slider = Scale(control_frame, from_=0, to=100, orient=HORIZONTAL, length=300, command=self.seek_position)
self.position_slider.pack(side=LEFT, padx=10)
Label(control_frame, text="Volume").pack(side=LEFT)
self.volume_slider = Scale(control_frame, from_=0, to=100, orient=HORIZONTAL, length=100, command=self.set_volume)
self.volume_slider.set(50)
self.volume_slider.pack(side=LEFT, padx=10)
def create_status_bar(self):
self.status_bar = Label(self.root, text="Ready", bd=1, relief=SUNKEN, anchor=W)
self.status_bar.pack(side=BOTTOM, fill=X)
def connect_database(self):
try:
self.db_connection = sqlite3.connect(self.db_path)
self.cursor = self.db_connection.cursor()
self.cursor.execute('''CREATE TABLE IF NOT EXISTS mp3_files
(id INTEGER PRIMARY KEY, filename TEXT, filedata BLOB)''')
self.db_connection.commit()
except Exception as e:
messagebox.showerror("Database Error", f"Failed to connect to database: {e}")
def select_database(self):
db_path = filedialog.askopenfilename(
title="Select MP3 Database",
filetypes=[("SQLite Database", "*.db"), ("All files", "*.*")]
)
if db_path:
self.db_path = db_path
self.connect_database()
self.load_mp3s_from_database()
self.update_status("Database loaded successfully")
def select_streams_file(self):
ini_path = filedialog.askopenfilename(
title="Select Streams File",
filetypes=[("INI Config", "*.ini"), ("All files", "*.*")]
)
if ini_path:
self.load_streams_file(ini_path)
self.update_status("Streams file loaded successfully")
def load_streams_file(self, ini_path):
config = configparser.ConfigParser()
try:
config.read(ini_path)
self.streams = {sect: dict(config.items(sect)) for sect in config.sections()}
if self.streams:
self.category_combo['values'] = list(self.streams.keys())
self.category_combo.current(0)
self.update_stations()
else:
messagebox.showerror("Error", "No valid sections found in stream file.")
except Exception as e:
messagebox.showerror("Error", f"Failed to load stream file: {e}")
def update_stations(self, event=None):
selected_category = self.category_var.get()
if selected_category in self.streams:
self.station_list.delete(0, END)
for station in self.streams[selected_category]:
self.station_list.insert(END, station)
self.current_streams = self.streams[selected_category]
def play_selected_station(self, event=None):
if self.secure_mode and not self.authenticate():
return
selected_index = self.station_list.curselection()
if not selected_index:
return
station_name = self.station_list.get(selected_index)
stream_url = self.current_streams.get(station_name)
if not stream_url:
messagebox.showerror("Error", "Stream URL not found.")
return
try:
media = self.vlc_instance.media_new(stream_url)
self.media_player.set_media(media)
self.media_player.play()
self.update_status(f"Playing: {station_name}")
self.current_stream_url = stream_url
except Exception as e:
messagebox.showerror("Error", f"Failed to play stream: {e}")
def open_stream_url(self):
if hasattr(self, 'current_stream_url'):
import webbrowser
webbrowser.open(self.current_stream_url)
def set_password(self):
if self.secure_mode and not self.authenticate():
return
new_pass = self.get_password_dialog("Set New Password", "Enter new password:")
if new_pass:
self.password_hash = self.hash_password(new_pass)
self.save_password_hash()
messagebox.showinfo("Success", "Password set successfully")
self.update_status("Password set")
def lock_application(self):
if self.authenticate():
self.root.withdraw()
self.root.after(100, self.show_login_dialog)
def toggle_secure_mode(self):
self.secure_mode = self.secure_mode_var.get() == 1
status = "enabled" if self.secure_mode else "disabled"
self.update_status(f"Secure mode {status}")
def authenticate(self):
if not self.secure_mode or not self.password_hash:
return True
password = self.get_password_dialog("Authentication Required", "Enter password:")
if password and self.hash_password(password) == self.password_hash:
return True
return False
def get_password_dialog(self, title, prompt):
dialog = Toplevel(self.root)
dialog.title(title)
dialog.geometry("300x120")
dialog.transient(self.root)
dialog.grab_set()
Label(dialog, text=prompt).pack(pady=5)
password_var = StringVar()
entry = Entry(dialog, textvariable=password_var, show="*")
entry.pack(pady=5)
result = [None]
def on_ok():
result[0] = password_var.get()
dialog.destroy()
Button(dialog, text="OK", command=on_ok).pack(side=LEFT, padx=20)
Button(dialog, text="Cancel", command=dialog.destroy).pack(side=RIGHT, padx=20)
self.root.wait_window(dialog)
return result[0]
def hash_password(self, password):
salt = b'salt_123'
return base64.b64encode(hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)).decode()
def load_password_hash(self):
try:
with open('config.dat', 'r') as f:
return f.read().strip()
except:
return None
def save_password_hash(self):
try:
with open('config.dat', 'w') as f:
f.write(self.password_hash or '')
except Exception as e:
messagebox.showerror("Error", f"Failed to save password: {e}")
def show_login_dialog(self):
login_window = Toplevel()
login_window.title("Login")
login_window.geometry("300x120")
login_window.transient(self.root)
Label(login_window, text="Enter password to unlock:").pack(pady=5)
password_var = StringVar()
entry = Entry(login_window, textvariable=password_var, show="*")
entry.pack(pady=5)
def on_login():
if self.hash_password(password_var.get()) == self.password_hash:
login_window.destroy()
self.root.deiconify()
else:
messagebox.showerror("Error", "Invalid password")
Button(login_window, text="Login", command=on_login).pack()
login_window.protocol("WM_DELETE_WINDOW", self.root.quit)
def update_status(self, message):
self.status_bar.config(text=message)
def load_gif(self, gif_path):
if getattr(sys, 'frozen', False):
base_path = sys._MEIPASS
else:
base_path = os.path.dirname(__file__)
full_path = os.path.join(base_path, gif_path)
self.gif_label = Label(self.root)
self.gif_label.pack(pady=10)
self.gif = Image.open(full_path)
self.frames = [ImageTk.PhotoImage(frame) for frame in ImageSequence.Iterator(self.gif)]
self.gif_index = 0
self.gif_label.configure(image=self.frames[0])
self.update_gif()
def update_gif(self):
self.gif_index = (self.gif_index + 1) % len(self.frames)
self.gif_label.configure(image=self.frames[self.gif_index])
self.root.after(100, self.update_gif)
def load_mp3s_from_database(self):
self.listbox.delete(0, END)
try:
self.cursor.execute("SELECT filename FROM mp3_files")
for row in self.cursor.fetchall():
self.listbox.insert(END, row[0])
except Exception as e:
messagebox.showerror("Error", f"Failed to load MP3 files: {e}")
def play_selected_song(self, event=None):
selected_index = self.listbox.curselection()
if not selected_index:
return
song_name = self.listbox.get(selected_index)
try:
self.cursor.execute("SELECT filedata FROM mp3_files WHERE filename = ?", (song_name,))
song_data = self.cursor.fetchone()[0]
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_file:
temp_file.write(song_data)
temp_file_path = temp_file.name
self.temp_files.append(temp_file_path)
self.media_player.set_media(self.vlc_instance.media_new(temp_file_path))
self.media_player.play()
self.update_position_slider()
except Exception as e:
messagebox.showerror("Error", f"Failed to play song: {e}")
def stop_song(self):
self.media_player.stop()
self.position_slider.set(0)
def play_next(self):
current_index = self.listbox.curselection()[0]
next_index = (current_index + 1) % self.listbox.size()
self.listbox.select_clear(current_index)
self.listbox.select_set(next_index)
self.play_selected_song()
def play_previous(self):
current_index = self.listbox.curselection()[0]
prev_index = (current_index - 1) % self.listbox.size()
self.listbox.select_clear(current_index)
self.listbox.select_set(prev_index)
self.play_selected_song()
def set_volume(self, volume):
self.media_player.audio_set_volume(int(volume))
def update_position_slider(self):
if self.media_player.is_playing():
current_pos = self.media_player.get_time() // 1000
self.position_slider.set(current_pos)
self.root.after(1000, self.update_position_slider)
def seek_position(self, position):
self.media_player.set_time(int(position) * 1000)
def browse_folder(self):
folder_path = filedialog.askdirectory()
if not folder_path:
return
self.listbox.delete(0, END)
try:
self.cursor.execute("DELETE FROM mp3_files")
self.db_connection.commit()
for filename in os.listdir(folder_path):
if filename.endswith(".mp3"):
file_path = os.path.join(folder_path, filename)
with open(file_path, 'rb') as file:
file_data = file.read()
self.cursor.execute("INSERT INTO mp3_files (filename, filedata) VALUES (?, ?)", (filename, file_data))
self.db_connection.commit()
self.load_mp3s_from_database()
messagebox.showinfo("Success", "MP3 files loaded from folder and saved to database!")
except Exception as e:
messagebox.showerror("Error", f"Failed to load folder: {e}")
def add_mp3_file(self):
file_path = filedialog.askopenfilename(filetypes=[("MP3 files", "*.mp3")])
if not file_path:
return
try:
filename = os.path.basename(file_path)
with open(file_path, 'rb') as file:
file_data = file.read()
self.cursor.execute("INSERT INTO mp3_files (filename, filedata) VALUES (?, ?)", (filename, file_data))
self.db_connection.commit()
self.listbox.insert(END, filename)
messagebox.showinfo("Success", "MP3 file added to database!")
except Exception as e:
messagebox.showerror("Error", f"Failed to add MP3: {e}")
def delete_mp3_file(self):
selected_index = self.listbox.curselection()
if not selected_index:
return
try:
song_name = self.listbox.get(selected_index)
self.cursor.execute("DELETE FROM mp3_files WHERE filename = ?", (song_name,))
self.db_connection.commit()
self.listbox.delete(selected_index)
messagebox.showinfo("Deleted", "MP3 file deleted from database.")
except Exception as e:
messagebox.showerror("Error", f"Failed to delete MP3: {e}")
def save_mp3_to_database(self):
selected_index = self.listbox.curselection()
if not selected_index:
messagebox.showwarning("Warning", "Please select an MP3 file to save.")
return
try:
song_name = self.listbox.get(selected_index)
folder_path = filedialog.askdirectory()
if not folder_path:
return
self.cursor.execute("SELECT filedata FROM mp3_files WHERE filename = ?", (song_name,))
file_data = self.cursor.fetchone()
if file_data:
file_path = os.path.join(folder_path, song_name)
with open(file_path, 'wb') as file:
file.write(file_data[0])
messagebox.showinfo("Success", f"MP3 file '{song_name}' saved to selected folder.")
else:
messagebox.showerror("Error", "File data not found in database.")
except Exception as e:
messagebox.showerror("Error", f"Failed to save MP3: {e}")
def delete_temp_files(self):
self.media_player.stop()
deleted_files = []
for temp_file in self.temp_files:
try:
if os.path.exists(temp_file):
os.remove(temp_file)
deleted_files.append(temp_file)
except Exception as e:
messagebox.showerror("Error", f"Failed to delete {temp_file}: {e}")
self.temp_files.clear()
if deleted_files:
messagebox.showinfo("Success", f"Temporary files deleted: {', '.join(deleted_files)}")
else:
messagebox.showinfo("Success", "No temporary files to delete.")
if __name__ == "__main__":
root = Tk()
app = Mp3PlayerApp(root)
root.mainloop()