import sys
import os
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
import requests
import re
import asyncio
from packaging import version
import qdarktheme

from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit,
    QLabel, QFileDialog, QListWidget, QTextEdit, QTabWidget, QButtonGroup, QRadioButton,
    QAbstractItemView, QProgressBar, QCheckBox, QDialog,
    QDialogButtonBox, QComboBox, QStyledItemDelegate
)
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QUrl, QTimer, QTime, QSettings, QSize
from PyQt6.QtGui import QIcon, QTextCursor, QDesktopServices, QPixmap, QBrush
from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply

from getMetadata import get_filtered_data, parse_uri, SpotifyInvalidUrlException
from tidalDL import TidalDownloader
from deezerDL import DeezerDownloader

@dataclass
class Track:
    external_urls: str
    title: str
    artists: str
    album: str
    track_number: int
    duration_ms: int
    id: str
    isrc: str = ""
    release_date: str = ""

class MetadataFetchWorker(QThread):
    finished = pyqtSignal(dict)
    error = pyqtSignal(str)
    
    def __init__(self, url):
        super().__init__()
        self.url = url
        
    def run(self):
        try:
            metadata = get_filtered_data(self.url)
            if "error" in metadata:
                self.error.emit(metadata["error"])
            else:
                self.finished.emit(metadata)
        except SpotifyInvalidUrlException as e:
            self.error.emit(str(e))
        except Exception as e:
            self.error.emit(f'Failed to fetch metadata: {str(e)}')

class DownloadWorker(QThread):
    finished = pyqtSignal(bool, str, list, list, list)
    progress = pyqtSignal(str, int)
    
    def __init__(self, tracks, outpath, is_single_track=False, is_album=False, is_playlist=False,
                 album_or_playlist_name='', filename_format='title_artist', use_track_numbers=True,
                 use_artist_subfolders=False, use_album_subfolders=False, service="tidal"):
        super().__init__()
        self.tracks = tracks
        self.outpath = outpath
        self.is_single_track = is_single_track
        self.is_album = is_album        
        self.is_playlist = is_playlist
        self.album_or_playlist_name = album_or_playlist_name
        self.filename_format = filename_format
        self.use_track_numbers = use_track_numbers
        self.use_artist_subfolders = use_artist_subfolders
        self.use_album_subfolders = use_album_subfolders
        self.service = service
        self.is_paused = False
        self.is_stopped = False
        self.failed_tracks = []
        self.successful_tracks = []
        self.skipped_tracks = []

    def get_formatted_filename(self, track):
        if self.filename_format == "artist_title":
            filename = f"{track.artists} - {track.title}.flac"
        elif self.filename_format == "title_only":
            filename = f"{track.title}.flac"
        else:
            filename = f"{track.title} - {track.artists}.flac"
        return re.sub(r'[<>:"/\\|?*]', lambda m: "'" if m.group() == '"' else '_', filename)

    def run(self):
        try:
            if self.service == "tidal": 
                downloader = TidalDownloader()            
            elif self.service == "deezer":
                downloader = DeezerDownloader()
            else:
                downloader = TidalDownloader()
            
            def progress_update(current, total):
                if total <= 0:
                    self.progress.emit("Processing metadata...", 0)
            
            downloader.set_progress_callback(progress_update)
            
            total_tracks = len(self.tracks)
            
            for i, track in enumerate(self.tracks):
                while self.is_paused:
                    if self.is_stopped:
                        return
                    self.msleep(100)
                if self.is_stopped:
                    return
                
                self.progress.emit(f"Starting download ({i+1}/{total_tracks}): {track.title} - {track.artists}", 
                                int((i) / total_tracks * 100))
                
                try:
                    if self.is_playlist:
                        track_outpath = self.outpath
                        
                        if self.use_artist_subfolders:
                            artist_name = track.artists.split(', ')[0] if ', ' in track.artists else track.artists
                            artist_folder = re.sub(r'[<>:"/\\|?*]', lambda m: "'" if m.group() == '"' else '_', artist_name)
                            track_outpath = os.path.join(track_outpath, artist_folder)
                        
                        if self.use_album_subfolders:
                            album_folder = re.sub(r'[<>:"/\\|?*]', lambda m: "'" if m.group() == '"' else '_', track.album)
                            track_outpath = os.path.join(track_outpath, album_folder)
                        
                        os.makedirs(track_outpath, exist_ok=True)
                    else:
                        track_outpath = self.outpath
                    
                    if (self.is_album or self.is_playlist) and self.use_track_numbers:
                        new_filename = f"{track.track_number:02d} - {self.get_formatted_filename(track)}"
                    else:
                        new_filename = self.get_formatted_filename(track)
                    
                    new_filename = re.sub(r'[<>:"/\\|?*]', lambda m: "'" if m.group() == '"' else '_', new_filename)
                    new_filepath = os.path.join(track_outpath, new_filename)
                    
                    if os.path.exists(new_filepath) and os.path.getsize(new_filepath) > 0:
                        self.progress.emit(f"File already exists: {new_filename}. Skipping download.", 0)
                        self.progress.emit(f"Skipped: {track.title} - {track.artists}", 
                                    int((i + 1) / total_tracks * 100))
                        self.skipped_tracks.append(track)
                        continue
                    
                    if self.service == "tidal": 
                        if not track.isrc:
                            self.progress.emit(f"No ISRC found for track: {track.title}. Skipping.", 0)
                            self.failed_tracks.append((track.title, track.artists, "No ISRC available"))
                            continue
                        
                        self.progress.emit(f"Searching and downloading from Tidal for ISRC: {track.isrc} - {track.title} - {track.artists}", 0)
                        is_paused_callback = lambda: self.is_paused
                        is_stopped_callback = lambda: self.is_stopped
                        
                        download_result_details = downloader.download(
                            query=f"{track.title} {track.artists}", 
                            isrc=track.isrc,
                            output_dir=track_outpath,
                            quality="LOSSLESS", 
                            is_paused_callback=is_paused_callback,
                            is_stopped_callback=is_stopped_callback
                        )
                        
                        if isinstance(download_result_details, str) and os.path.exists(download_result_details): 
                            downloaded_file = download_result_details
                        elif isinstance(download_result_details, dict) and download_result_details.get("success") == False and download_result_details.get("error") == "Download stopped by user":
                            self.progress.emit(f"Download stopped by user for: {track.title}",0)
                            return 
                        elif isinstance(download_result_details, dict) and download_result_details.get("success") == False:
                            raise Exception(download_result_details.get("error", "Tidal download failed"))                        
                        elif isinstance(download_result_details, dict) and (download_result_details.get("status") == "all_skipped" or download_result_details.get("status") == "skipped_exists"):
                            self.progress.emit(f"File already exists or skipped: {new_filename}",0)
                            downloaded_file = new_filepath
                            self.skipped_tracks.append(track)
                        else: 
                            downloaded_file = None 
                            raise Exception(f"Tidal download failed or returned unexpected result: {download_result_details}")
                    elif self.service == "deezer":
                        if not track.isrc:
                            self.progress.emit(f"No ISRC found for track: {track.title}. Skipping.", 0)
                            self.failed_tracks.append((track.title, track.artists, "No ISRC available"))
                            continue
                        
                        self.progress.emit(f"Downloading from Deezer with ISRC: {track.isrc}", 0)
                        
                        success = asyncio.run(downloader.download_by_isrc(track.isrc, track_outpath))
                        
                        if success:
                            safe_title = "".join(c for c in track.title if c.isalnum() or c in (' ', '-', '_')).rstrip()
                            safe_artist = "".join(c for c in track.artists if c.isalnum() or c in (' ', '-', '_')).rstrip()
                            expected_filename = f"{safe_artist} - {safe_title}.flac"
                            downloaded_file = os.path.join(track_outpath, expected_filename)
                            
                            if not os.path.exists(downloaded_file):
                                import glob
                                flac_files = glob.glob(os.path.join(track_outpath, "*.flac"))
                                if flac_files:
                                    downloaded_file = max(flac_files, key=os.path.getctime)
                                else:
                                    raise Exception("Downloaded file not found")
                        else:
                            raise Exception("Deezer download failed")
                    else: 
                        track_id = track.id
                        self.progress.emit(f"Getting track info for ID: {track_id} from {self.service}", 0)
                        
                        try:
                            loop = asyncio.get_event_loop()
                            if loop.is_closed():
                                loop = asyncio.new_event_loop()
                                asyncio.set_event_loop(loop)
                        except RuntimeError:
                            loop = asyncio.new_event_loop()
                            asyncio.set_event_loop(loop)
                        
                        metadata = loop.run_until_complete(downloader.get_track_info(track_id, self.service))
                        self.progress.emit(f"Track info received, starting download process", 0)
                        
                        is_paused_callback = lambda: self.is_paused
                        is_stopped_callback = lambda: self.is_stopped
                        
                        downloaded_file = downloader.download(
                            metadata, 
                            track_outpath,
                            is_paused_callback=is_paused_callback,
                            is_stopped_callback=is_stopped_callback
                        )
                    if self.is_stopped: 
                        return

                    if downloaded_file and os.path.exists(downloaded_file):
                        if downloaded_file == new_filepath:
                            self.progress.emit(f"File already exists: {new_filename}", 0)
                            self.progress.emit(f"Skipped: {track.title} - {track.artists}", 
                                        int((i + 1) / total_tracks * 100))
                            self.skipped_tracks.append(track)
                            continue
                        
                        if downloaded_file != new_filepath:
                            try:
                                os.rename(downloaded_file, new_filepath)
                                self.progress.emit(f"File renamed to: {new_filename}", 0)
                            except OSError as e:
                                self.progress.emit(f"Warning: Could not rename file {downloaded_file} to {new_filepath}: {str(e)}", 0)
                                pass
                    else:
                        raise Exception(f"Download failed or file not found: {downloaded_file}")
                    
                    self.progress.emit(f"Successfully downloaded: {track.title} - {track.artists}", 
                                    int((i + 1) / total_tracks * 100))
                    self.successful_tracks.append(track)
                except Exception as e:
                    self.failed_tracks.append((track.title, track.artists, str(e)))
                    self.progress.emit(f"Failed to download: {track.title} - {track.artists}\nError: {str(e)}", 
                                    int((i + 1) / total_tracks * 100))
                    continue

            if not self.is_stopped:
                success_message = "Download completed!"
                if self.failed_tracks:
                    success_message += f"\n\nFailed downloads: {len(self.failed_tracks)} tracks"
                if self.successful_tracks:
                    success_message += f"\n\nSuccessful downloads: {len(self.successful_tracks)} tracks"
                if self.skipped_tracks:
                    success_message += f"\n\nSkipped (already exists): {len(self.skipped_tracks)} tracks"
                self.finished.emit(True, success_message, self.failed_tracks, self.successful_tracks, self.skipped_tracks)
                
        except Exception as e:
            self.finished.emit(False, str(e), self.failed_tracks, self.successful_tracks, self.skipped_tracks)

    def pause(self):
        self.is_paused = True
        self.progress.emit("Download process paused.", 0)

    def resume(self):
        self.is_paused = False
        self.progress.emit("Download process resumed.", 0)

    def stop(self): 
        self.is_stopped = True
        self.is_paused = False

class UpdateDialog(QDialog):
    def __init__(self, current_version, new_version, parent=None):
        super().__init__(parent)
        self.setWindowTitle("Update Now")
        self.setFixedWidth(400)
        self.setModal(True)

        layout = QVBoxLayout()

        message = QLabel(f"SpotiFLAC v{new_version} Available!")
        message.setWordWrap(True)
        layout.addWidget(message)

        button_box = QDialogButtonBox()
        self.update_button = QPushButton("Check")
        self.update_button.setCursor(Qt.CursorShape.PointingHandCursor)
        self.cancel_button = QPushButton("Later")
        self.cancel_button.setCursor(Qt.CursorShape.PointingHandCursor)
        
        button_box.addButton(self.update_button, QDialogButtonBox.ButtonRole.AcceptRole)
        button_box.addButton(self.cancel_button, QDialogButtonBox.ButtonRole.RejectRole)
        
        layout.addWidget(button_box)

        self.setLayout(layout)

        self.update_button.clicked.connect(self.accept)
        self.cancel_button.clicked.connect(self.reject)

class TidalStatusChecker(QThread):
    status_updated = pyqtSignal(bool)
    error = pyqtSignal(str)

    def run(self):
        try:
            response = requests.get("https://tidal.401658.xyz", timeout=5)
            is_online = response.status_code == 200 or response.status_code == 429
            self.status_updated.emit(is_online)
        except Exception as e:
            self.error.emit(f"Error checking Tidal (API) status: {str(e)}")
            self.status_updated.emit(False)

class DeezerStatusChecker(QThread):
    status_updated = pyqtSignal(bool)
    error = pyqtSignal(str)

    def run(self):
        try:
            response = requests.get("https://deezmate.com/", timeout=5)
            is_online = response.status_code == 200
            self.status_updated.emit(is_online)
        except Exception as e:
            self.error.emit(f"Error checking Deezer status: {str(e)}")
            self.status_updated.emit(False)

class StatusIndicatorDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        item_data = index.data(Qt.ItemDataRole.UserRole)
        is_online = item_data.get('online', False) if item_data else False
        
        super().paint(painter, option, index)
        
        indicator_color = Qt.GlobalColor.green if is_online else Qt.GlobalColor.red
        
        circle_size = 6
        circle_y = option.rect.center().y() - circle_size // 2
        circle_x = option.rect.right() - circle_size - 5
        
        painter.save()
        painter.setPen(Qt.PenStyle.NoPen)
        painter.setBrush(QBrush(indicator_color))
        painter.drawEllipse(circle_x, circle_y, circle_size, circle_size)
        painter.restore()

class ServiceComboBox(QComboBox):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setIconSize(QSize(16, 16))
        self.services_status = {}
        
        self.setItemDelegate(StatusIndicatorDelegate())
        self.setup_items()
        
        self.tidal_status_checker = TidalStatusChecker()
        self.tidal_status_checker.status_updated.connect(self.update_tidal_service_status) 
        self.tidal_status_checker.error.connect(lambda e: print(f"Tidal status check error: {e}")) 
        self.tidal_status_checker.start()

        self.tidal_status_timer = QTimer(self)
        self.tidal_status_timer.timeout.connect(self.refresh_tidal_status) 
        self.tidal_status_timer.start(60000)  
        
        self.deezer_status_checker = DeezerStatusChecker()
        self.deezer_status_checker.status_updated.connect(self.update_deezer_service_status) 
        self.deezer_status_checker.error.connect(lambda e: print(f"Deezer status check error: {e}")) 
        self.deezer_status_checker.start()

        self.deezer_status_timer = QTimer(self)
        self.deezer_status_timer.timeout.connect(self.refresh_deezer_status) 
        self.deezer_status_timer.start(60000)  
        
    def setup_items(self):
        current_dir = os.path.dirname(os.path.abspath(__file__))
        
        self.services = [
            {'id': 'tidal', 'name': 'Tidal', 'icon': 'tidal.png', 'online': False},
            {'id': 'deezer', 'name': 'Deezer', 'icon': 'deezer.png', 'online': False}
        ]
        
        for service in self.services:
            icon_path = os.path.join(current_dir, service['icon'])
            if not os.path.exists(icon_path):
                self.create_placeholder_icon(icon_path)
            
            icon = QIcon(icon_path)
            
            self.addItem(icon, service['name'])
            item_index = self.count() - 1
            self.setItemData(item_index, service['id'], Qt.ItemDataRole.UserRole + 1)
            self.setItemData(item_index, service, Qt.ItemDataRole.UserRole)
    def create_placeholder_icon(self, path):
        pixmap = QPixmap(16, 16)
        pixmap.fill(Qt.GlobalColor.transparent)
        pixmap.save(path)

    def update_service_status(self, service_id, is_online):
        for i in range(self.count()):
            current_service_id = self.itemData(i, Qt.ItemDataRole.UserRole + 1)
            if current_service_id == service_id:
                service_data = self.itemData(i, Qt.ItemDataRole.UserRole)
                if isinstance(service_data, dict):
                    service_data['online'] = is_online
                    self.setItemData(i, service_data, Qt.ItemDataRole.UserRole)
                break 
        self.update()
        
    def update_tidal_service_status(self, is_online): 
        self.update_service_status('tidal', is_online)
        
    def refresh_tidal_status(self):
        if hasattr(self, 'tidal_status_checker') and self.tidal_status_checker.isRunning():
            self.tidal_status_checker.quit()
            self.tidal_status_checker.wait()
            
        self.tidal_status_checker = TidalStatusChecker() 
        self.tidal_status_checker.status_updated.connect(self.update_tidal_service_status)
        self.tidal_status_checker.error.connect(lambda e: print(f"Tidal status check error: {e}")) 
        self.tidal_status_checker.start()
        
    def update_deezer_service_status(self, is_online): 
        self.update_service_status('deezer', is_online)
        
    def refresh_deezer_status(self):
        if hasattr(self, 'deezer_status_checker') and self.deezer_status_checker.isRunning():
            self.deezer_status_checker.quit()
            self.deezer_status_checker.wait()
            
        self.deezer_status_checker = DeezerStatusChecker() 
        self.deezer_status_checker.status_updated.connect(self.update_deezer_service_status)
        self.deezer_status_checker.error.connect(lambda e: print(f"Deezer status check error: {e}")) 
        self.deezer_status_checker.start()
        
    def currentData(self, role=Qt.ItemDataRole.UserRole + 1):
        return super().currentData(role)

class SpotiFLACGUI(QWidget):
    def __init__(self):
        super().__init__()
        self.current_version = "4.7"
        self.tracks = []
        self.all_tracks = []  
        self.successful_downloads = []
        self.reset_state()
        
        self.settings = QSettings('SpotiFLAC', 'Settings')
        self.last_output_path = self.settings.value('output_path', str(Path.home() / "Music"))
        self.last_url = self.settings.value('spotify_url', '')
        
        self.filename_format = self.settings.value('filename_format', 'title_artist')
        self.use_track_numbers = self.settings.value('use_track_numbers', False, type=bool)
        self.use_artist_subfolders = self.settings.value('use_artist_subfolders', False, type=bool)
        self.use_album_subfolders = self.settings.value('use_album_subfolders', False, type=bool)
        self.service = self.settings.value('service', 'tidal')
        self.check_for_updates = self.settings.value('check_for_updates', True, type=bool)
        self.current_theme_color = self.settings.value('theme_color', '#2196F3')
        self.track_list_format = self.settings.value('track_list_format', 'track_artist_date_duration')
        self.date_format = self.settings.value('date_format', 'dd_mm_yyyy')
        
        self.elapsed_time = QTime(0, 0, 0)
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_timer)
        
        self.network_manager = QNetworkAccessManager()
        self.network_manager.finished.connect(self.on_cover_loaded)
        
        self.initUI()
        
        if self.check_for_updates:
            QTimer.singleShot(0, self.check_updates)

    def set_combobox_value(self, combobox, target_value):
        for i in range(combobox.count()):
            if combobox.itemData(i, Qt.ItemDataRole.UserRole + 1) == target_value:
                combobox.setCurrentIndex(i)
                return True
            if combobox.itemData(i, Qt.ItemDataRole.UserRole) == target_value:
                combobox.setCurrentIndex(i)
                return True
        return False

    def check_updates(self):
        try:
            response = requests.get("https://raw.githubusercontent.com/afkarxyz/SpotiFLAC/refs/heads/main/version.json")
            if response.status_code == 200:
                data = response.json()
                new_version = data.get("version")
                
                if new_version and version.parse(new_version) > version.parse(self.current_version):
                    dialog = UpdateDialog(self.current_version, new_version, self)
                    result = dialog.exec()
                    
                    if result == QDialog.DialogCode.Accepted:
                        QDesktopServices.openUrl(QUrl("https://github.com/afkarxyz/SpotiFLAC/releases"))
                        
        except Exception as e:
            pass

    @staticmethod
    def format_duration(ms):
        minutes = ms // 60000
        seconds = (ms % 60000) // 1000
        return f"{minutes}:{seconds:02d}"
    
    def reset_state(self):
        self.tracks.clear()
        self.all_tracks.clear()
        self.is_album = False
        self.is_playlist = False 
        self.is_single_track = False
        self.album_or_playlist_name = ''

    def reset_ui(self):
        self.track_list.clear()
        self.log_output.clear()
        self.progress_bar.setValue(0)
        self.progress_bar.hide()
        self.stop_btn.hide()
        self.pause_resume_btn.hide()
        self.pause_resume_btn.setText('Pause')
        self.reset_info_widget()
        self.hide_track_buttons()
        if hasattr(self, 'search_input'):
            self.search_input.clear()
        if hasattr(self, 'search_widget'):
            self.search_widget.hide()

    def initUI(self):
        self.setWindowTitle('SpotiFLAC')
        self.setFixedWidth(650)
        self.setMinimumHeight(350)  
        
        icon_path = os.path.join(os.path.dirname(__file__), "icon.svg")
        if os.path.exists(icon_path):
            self.setWindowIcon(QIcon(icon_path))
            
        self.main_layout = QVBoxLayout()
        
        self.setup_spotify_section()
        self.setup_tabs()
        
        self.setLayout(self.main_layout)

    def setup_spotify_section(self):
        spotify_layout = QHBoxLayout()
        spotify_label = QLabel('Spotify URL:')
        spotify_label.setFixedWidth(100)
        
        self.spotify_url = QLineEdit()
        self.spotify_url.setPlaceholderText("Enter Spotify URL")
        self.spotify_url.setClearButtonEnabled(True)
        self.spotify_url.setText(self.last_url)
        self.spotify_url.textChanged.connect(self.save_url)
        
        self.fetch_btn = QPushButton('Fetch')
        self.fetch_btn.setFixedWidth(80)
        self.fetch_btn.setCursor(Qt.CursorShape.PointingHandCursor)
        self.fetch_btn.clicked.connect(self.fetch_tracks)
        
        spotify_layout.addWidget(spotify_label)
        spotify_layout.addWidget(self.spotify_url)
        spotify_layout.addWidget(self.fetch_btn)
        self.main_layout.addLayout(spotify_layout)

    def filter_tracks(self):
        search_text = self.search_input.text().lower().strip()
        
        if not search_text:
            self.tracks = self.all_tracks.copy()
        else:
            self.tracks = [
                track for track in self.all_tracks
                if (search_text in track.title.lower() or 
                    search_text in track.artists.lower() or 
                    search_text in track.album.lower())
            ]
        
        self.update_track_list_display()

    def format_track_date(self, release_date):
        if not release_date:
            return ""
        
        try:
            if len(release_date) == 4:
                date_obj = datetime.strptime(release_date, "%Y")
                if self.date_format == "yyyy":
                    return date_obj.strftime('%Y')
                else:
                    return date_obj.strftime('%Y')
            elif len(release_date) == 7:
                date_obj = datetime.strptime(release_date, "%Y-%m")
                if self.date_format == "dd_mm_yyyy":
                    return date_obj.strftime('%m-%Y')
                elif self.date_format == "yyyy_mm_dd":
                    return date_obj.strftime('%Y-%m')
                else:
                    return date_obj.strftime('%Y')
            else:
                date_obj = datetime.strptime(release_date, "%Y-%m-%d")
                if self.date_format == "dd_mm_yyyy":
                    return date_obj.strftime('%d-%m-%Y')
                elif self.date_format == "yyyy_mm_dd":
                    return date_obj.strftime('%Y-%m-%d')
                else:
                    return date_obj.strftime('%Y')
        except ValueError:
            return release_date

    def update_track_list_display(self):
        self.track_list.clear()
        for i, track in enumerate(self.tracks, 1):
            duration = self.format_duration(track.duration_ms)
            formatted_date = self.format_track_date(track.release_date)
            
            if self.track_list_format == "artist_track_date_duration":
                display_parts = [f"{i}. {track.artists} - {track.title}"]
                if formatted_date:
                    display_parts.append(formatted_date)
                display_parts.append(duration)
                display_text = " • ".join(display_parts)
            elif self.track_list_format == "track_artist_date":
                display_parts = [f"{i}. {track.title} - {track.artists}"]
                if formatted_date:
                    display_parts.append(formatted_date)
                display_text = " • ".join(display_parts)
            elif self.track_list_format == "artist_track_date":
                display_parts = [f"{i}. {track.artists} - {track.title}"]
                if formatted_date:
                    display_parts.append(formatted_date)
                display_text = " • ".join(display_parts)
            elif self.track_list_format == "track_artist_duration":
                display_text = f"{i}. {track.title} - {track.artists} • {duration}"
            elif self.track_list_format == "artist_track_duration":
                display_text = f"{i}. {track.artists} - {track.title} • {duration}"
            elif self.track_list_format == "track_artist":
                display_text = f"{i}. {track.title} - {track.artists}"
            elif self.track_list_format == "artist_track":
                display_text = f"{i}. {track.artists} - {track.title}"
            else:
                display_parts = [f"{i}. {track.title} - {track.artists}"]
                if formatted_date:
                    display_parts.append(formatted_date)
                display_parts.append(duration)
                display_text = " • ".join(display_parts)
            
            self.track_list.addItem(display_text)

    def browse_output(self):
        directory = QFileDialog.getExistingDirectory(self, "Select Output Directory")
        if directory:
            self.output_dir.setText(directory)
            self.save_settings()

    def setup_tabs(self):
        self.tab_widget = QTabWidget()
        self.main_layout.addWidget(self.tab_widget)

        self.setup_dashboard_tab()
        self.setup_process_tab()
        self.setup_settings_tab()
        self.setup_theme_tab()
        self.setup_about_tab()

    def setup_dashboard_tab(self):
        dashboard_tab = QWidget()
        dashboard_layout = QVBoxLayout()

        self.setup_info_widget()
        dashboard_layout.addWidget(self.info_widget)

        self.track_list = QListWidget()
        self.track_list.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
        dashboard_layout.addWidget(self.track_list)
        
        self.setup_track_buttons()
        dashboard_layout.addLayout(self.btn_layout)
        dashboard_layout.addWidget(self.single_track_container)

        dashboard_tab.setLayout(dashboard_layout)
        self.tab_widget.addTab(dashboard_tab, "Dashboard")

        self.hide_track_buttons()

    def setup_info_widget(self):
        self.info_widget = QWidget()
        info_layout = QHBoxLayout()
        self.cover_label = QLabel()
        self.cover_label.setFixedSize(80, 80)
        self.cover_label.setScaledContents(True)
        info_layout.addWidget(self.cover_label)

        text_info_layout = QVBoxLayout()
        
        self.title_label = QLabel()
        self.title_label.setStyleSheet("font-size: 14px; font-weight: bold;")
        self.title_label.setWordWrap(True)
        
        self.artists_label = QLabel()
        self.artists_label.setWordWrap(True)

        self.followers_label = QLabel()
        self.followers_label.setWordWrap(True)
        
        self.release_date_label = QLabel()
        self.release_date_label.setWordWrap(True)
        
        self.type_label = QLabel()
        self.type_label.setStyleSheet("font-size: 12px;")
        
        text_info_layout.addWidget(self.title_label)
        text_info_layout.addWidget(self.artists_label)
        text_info_layout.addWidget(self.followers_label)
        text_info_layout.addWidget(self.release_date_label)
        text_info_layout.addWidget(self.type_label)
        text_info_layout.addStretch()

        info_layout.addLayout(text_info_layout, 1)
        
        self.setup_search_widget()
        info_layout.addWidget(self.search_widget)
        
        self.info_widget.setLayout(info_layout)
        self.info_widget.setFixedHeight(100)
        self.info_widget.hide()

    def setup_search_widget(self):
        self.search_widget = QWidget()
        search_layout = QVBoxLayout()
        search_layout.setContentsMargins(10, 0, 0, 0)
        
        search_layout.addStretch()
        
        search_input_layout = QHBoxLayout()
        search_input_layout.addStretch()  
        
        self.search_input = QLineEdit()
        self.search_input.setPlaceholderText("Search...")
        self.search_input.setClearButtonEnabled(True)
        self.search_input.textChanged.connect(self.filter_tracks)
        self.search_input.setFixedWidth(250)  
        
        search_input_layout.addWidget(self.search_input)
        search_layout.addLayout(search_input_layout)
        
        self.search_widget.setLayout(search_layout)
        self.search_widget.hide()

    def setup_track_buttons(self):
        self.btn_layout = QHBoxLayout()
        self.download_selected_btn = QPushButton('Download Selected')
        self.download_all_btn = QPushButton('Download All')
        self.remove_btn = QPushButton('Remove Selected')
        self.clear_btn = QPushButton('Clear')
        
        for btn in [self.download_selected_btn, self.download_all_btn, self.remove_btn, self.clear_btn]:
            btn.setMinimumWidth(120)
            btn.setCursor(Qt.CursorShape.PointingHandCursor)
            
        self.download_selected_btn.clicked.connect(self.download_selected)
        self.download_all_btn.clicked.connect(self.download_all)
        self.remove_btn.clicked.connect(self.remove_selected_tracks)
        self.clear_btn.clicked.connect(self.clear_tracks)
        
        self.btn_layout.addStretch()
        for btn in [self.download_selected_btn, self.download_all_btn, self.remove_btn, self.clear_btn]:
            self.btn_layout.addWidget(btn, 1)
        self.btn_layout.addStretch()
        
        self.single_track_container = QWidget()
        single_track_layout = QHBoxLayout(self.single_track_container)
        single_track_layout.setContentsMargins(0, 0, 0, 0)
        
        self.single_download_btn = QPushButton('Download')
        self.single_clear_btn = QPushButton('Clear')
        
        for btn in [self.single_download_btn, self.single_clear_btn]:
            btn.setFixedWidth(120)
            btn.setCursor(Qt.CursorShape.PointingHandCursor)
            
        self.single_download_btn.clicked.connect(self.download_all)
        self.single_clear_btn.clicked.connect(self.clear_tracks)
        
        single_track_layout.addStretch()
        single_track_layout.addWidget(self.single_download_btn)
        single_track_layout.addWidget(self.single_clear_btn)
        single_track_layout.addStretch()
        
        self.single_track_container.hide()

    def setup_process_tab(self):
        self.process_tab = QWidget()
        process_layout = QVBoxLayout()
        process_layout.setSpacing(5)
        
        self.log_output = QTextEdit()
        self.log_output.setReadOnly(True)
        process_layout.addWidget(self.log_output)
        
        progress_time_layout = QVBoxLayout()
        progress_time_layout.setSpacing(2)
        
        self.progress_bar = QProgressBar()
        progress_time_layout.addWidget(self.progress_bar)
        
        self.time_label = QLabel("00:00:00")
        self.time_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        progress_time_layout.addWidget(self.time_label)
        
        process_layout.addLayout(progress_time_layout)
        
        control_layout = QHBoxLayout()
        self.stop_btn = QPushButton('Stop')
        self.pause_resume_btn = QPushButton('Pause')
        
        self.stop_btn.setFixedWidth(120)
        self.pause_resume_btn.setFixedWidth(120)
        
        self.stop_btn.setCursor(Qt.CursorShape.PointingHandCursor)
        self.pause_resume_btn.setCursor(Qt.CursorShape.PointingHandCursor)
        
        self.stop_btn.clicked.connect(self.stop_download)
        self.pause_resume_btn.clicked.connect(self.toggle_pause_resume)
        
        self.remove_successful_btn = QPushButton('Remove Finished Songs')
        self.remove_successful_btn.setFixedWidth(200)
        self.remove_successful_btn.setCursor(Qt.CursorShape.PointingHandCursor)
        self.remove_successful_btn.clicked.connect(self.remove_successful_downloads)
        
        control_layout.addStretch()
        control_layout.addWidget(self.stop_btn)
        control_layout.addWidget(self.pause_resume_btn)
        control_layout.addWidget(self.remove_successful_btn)
        control_layout.addStretch()
        
        process_layout.addLayout(control_layout)
        
        self.process_tab.setLayout(process_layout)
        
        self.tab_widget.addTab(self.process_tab, "Process")
        
        self.progress_bar.hide()
        self.time_label.hide()
        self.stop_btn.hide()
        self.pause_resume_btn.hide()
        self.remove_successful_btn.hide()

    def setup_settings_tab(self):
        settings_tab = QWidget()
        settings_layout = QVBoxLayout()
        settings_layout.setSpacing(4)
        settings_layout.setContentsMargins(10, 10, 10, 10)

        output_group = QWidget()
        output_layout = QVBoxLayout(output_group)
        output_layout.setSpacing(2)
        output_layout.setContentsMargins(0, 0, 0, 0)
        
        output_label = QLabel('Output Directory')
        output_label.setStyleSheet("font-weight: bold; margin-top: 0px; margin-bottom: 5px;")
        output_layout.addWidget(output_label)
        
        output_dir_layout = QHBoxLayout()
        self.output_dir = QLineEdit()
        self.output_dir.setText(self.last_output_path)
        self.output_dir.textChanged.connect(self.save_settings)
        
        self.output_browse = QPushButton('Browse')
        self.output_browse.setFixedWidth(80)
        self.output_browse.setCursor(Qt.CursorShape.PointingHandCursor)
        self.output_browse.clicked.connect(self.browse_output)
        
        output_dir_layout.addWidget(self.output_dir)
        output_dir_layout.addSpacing(5)
        output_dir_layout.addWidget(self.output_browse)
        
        output_layout.addLayout(output_dir_layout)
        
        settings_layout.addWidget(output_group)

        dashboard_group = QWidget()
        dashboard_layout = QVBoxLayout(dashboard_group)
        dashboard_layout.setSpacing(3)
        dashboard_layout.setContentsMargins(0, 0, 0, 0)
        
        dashboard_label = QLabel('Dashboard Settings')
        dashboard_label.setStyleSheet("font-weight: bold; margin-top: 8px; margin-bottom: 5px;")
        dashboard_layout.addWidget(dashboard_label)
        
        dashboard_controls_layout = QHBoxLayout()
        
        list_format_label = QLabel('Track List View:')
        list_format_label.setFixedWidth(90)
        
        self.track_list_format_dropdown = QComboBox()
        self.track_list_format_dropdown.addItem("Track - Artist - Date - Duration", "track_artist_date_duration")
        self.track_list_format_dropdown.addItem("Artist - Track - Date - Duration", "artist_track_date_duration")
        self.track_list_format_dropdown.addItem("Track - Artist - Date", "track_artist_date")
        self.track_list_format_dropdown.addItem("Artist - Track - Date", "artist_track_date")
        self.track_list_format_dropdown.addItem("Track - Artist - Duration", "track_artist_duration")
        self.track_list_format_dropdown.addItem("Artist - Track - Duration", "artist_track_duration")
        self.track_list_format_dropdown.addItem("Track - Artist", "track_artist")
        self.track_list_format_dropdown.addItem("Artist - Track", "artist_track")
        self.track_list_format_dropdown.currentIndexChanged.connect(self.save_track_list_format)
        
        dashboard_controls_layout.addWidget(list_format_label)
        dashboard_controls_layout.addWidget(self.track_list_format_dropdown)
        
        dashboard_controls_layout.addSpacing(15)
        
        date_format_label = QLabel('Date Format:')
        date_format_label.setFixedWidth(80)
        
        self.date_format_dropdown = QComboBox()
        self.date_format_dropdown.addItem("DD-MM-YYYY", "dd_mm_yyyy")
        self.date_format_dropdown.addItem("YYYY-MM-DD", "yyyy_mm_dd")
        self.date_format_dropdown.addItem("YYYY", "yyyy")
        self.date_format_dropdown.currentIndexChanged.connect(self.save_date_format)
        
        dashboard_controls_layout.addWidget(date_format_label)
        dashboard_controls_layout.addWidget(self.date_format_dropdown)
        dashboard_controls_layout.addStretch()
        
        dashboard_layout.addLayout(dashboard_controls_layout)
        
        settings_layout.addWidget(dashboard_group)

        file_group = QWidget()
        file_layout = QVBoxLayout(file_group)
        file_layout.setSpacing(2)
        file_layout.setContentsMargins(0, 0, 0, 0)
        
        file_label = QLabel('File Settings')
        file_label.setStyleSheet("font-weight: bold; margin-top: 8px; margin-bottom: 5px;")
        file_layout.addWidget(file_label)
        
        format_layout = QHBoxLayout()
        format_label = QLabel('Filename Format:')
        self.format_group = QButtonGroup(self)
        self.title_artist_radio = QRadioButton('Title - Artist')
        self.title_artist_radio.setCursor(Qt.CursorShape.PointingHandCursor)
        self.title_artist_radio.toggled.connect(self.save_filename_format)
        
        self.artist_title_radio = QRadioButton('Artist - Title')
        self.artist_title_radio.setCursor(Qt.CursorShape.PointingHandCursor)
        self.artist_title_radio.toggled.connect(self.save_filename_format)
        
        self.title_only_radio = QRadioButton('Title')
        self.title_only_radio.setCursor(Qt.CursorShape.PointingHandCursor)
        self.title_only_radio.toggled.connect(self.save_filename_format)
        
        if hasattr(self, 'filename_format') and self.filename_format == "artist_title":
            self.artist_title_radio.setChecked(True)
        elif hasattr(self, 'filename_format') and self.filename_format == "title_only":
            self.title_only_radio.setChecked(True)
        else:
            self.title_artist_radio.setChecked(True)
        
        self.format_group.addButton(self.title_artist_radio)
        self.format_group.addButton(self.artist_title_radio)
        self.format_group.addButton(self.title_only_radio)
        
        format_layout.addWidget(format_label)
        format_layout.addWidget(self.title_artist_radio)
        format_layout.addSpacing(10)
        format_layout.addWidget(self.artist_title_radio)
        format_layout.addSpacing(10)
        format_layout.addWidget(self.title_only_radio)
        format_layout.addStretch()
        file_layout.addLayout(format_layout)

        checkbox_layout = QHBoxLayout()
        
        self.artist_subfolder_checkbox = QCheckBox('Artist Subfolder (Playlist)')
        self.artist_subfolder_checkbox.setCursor(Qt.CursorShape.PointingHandCursor)
        self.artist_subfolder_checkbox.setChecked(self.use_artist_subfolders)
        self.artist_subfolder_checkbox.toggled.connect(self.save_artist_subfolder_setting)
        checkbox_layout.addWidget(self.artist_subfolder_checkbox)
        checkbox_layout.addSpacing(10)
        
        self.album_subfolder_checkbox = QCheckBox('Album Subfolder (Playlist)')
        self.album_subfolder_checkbox.setCursor(Qt.CursorShape.PointingHandCursor)
        self.album_subfolder_checkbox.setChecked(self.use_album_subfolders)
        self.album_subfolder_checkbox.toggled.connect(self.save_album_subfolder_setting)
        checkbox_layout.addWidget(self.album_subfolder_checkbox)
        checkbox_layout.addSpacing(10)
        
        self.track_number_checkbox = QCheckBox('Track Number')
        self.track_number_checkbox.setCursor(Qt.CursorShape.PointingHandCursor)
        self.track_number_checkbox.setChecked(self.use_track_numbers)
        self.track_number_checkbox.toggled.connect(self.save_track_numbering)
        checkbox_layout.addWidget(self.track_number_checkbox)
        
        checkbox_layout.addStretch()
        file_layout.addLayout(checkbox_layout)
        
        settings_layout.addWidget(file_group)

        auth_group = QWidget()
        auth_layout = QVBoxLayout(auth_group)
        auth_layout.setSpacing(2)
        auth_layout.setContentsMargins(0, 0, 0, 0)
        
        auth_label = QLabel('Service Settings')
        auth_label.setStyleSheet("font-weight: bold; margin-top: 8px; margin-bottom: 5px;")
        auth_layout.addWidget(auth_label)

        service_fallback_layout = QHBoxLayout()

        service_label = QLabel('Service:')
        
        self.service_dropdown = ServiceComboBox()
        self.service_dropdown.currentIndexChanged.connect(self.on_service_changed)
        service_fallback_layout.addWidget(service_label)
        service_fallback_layout.addWidget(self.service_dropdown)
        
        service_fallback_layout.addStretch()
        auth_layout.addLayout(service_fallback_layout)
        
        settings_layout.addWidget(auth_group)
        settings_layout.addStretch()
        settings_tab.setLayout(settings_layout)
        self.tab_widget.addTab(settings_tab, "Settings")
        self.set_combobox_value(self.service_dropdown, self.service)
        self.set_combobox_value(self.track_list_format_dropdown, self.track_list_format)
        self.set_combobox_value(self.date_format_dropdown, self.date_format)
        
    def setup_theme_tab(self):
        theme_tab = QWidget()
        theme_layout = QVBoxLayout()
        theme_layout.setSpacing(8)
        theme_layout.setContentsMargins(8, 15, 15, 15)

        grid_layout = QVBoxLayout()
        
        self.color_buttons = {}
        
        first_row_palettes = [
            ("Red", [
                ("#FFCDD2", "100"), ("#EF9A9A", "200"), ("#E57373", "300"), ("#EF5350", "400"), ("#F44336", "500"), ("#E53935", "600"), ("#D32F2F", "700"), ("#C62828", "800"), ("#B71C1C", "900"), ("#FF8A80", "A100"), ("#FF5252", "A200"), ("#FF1744", "A400"), ("#D50000", "A700")
            ]),
            ("Pink", [
                ("#F8BBD0", "100"), ("#F48FB1", "200"), ("#F06292", "300"), ("#EC407A", "400"), ("#E91E63", "500"), ("#D81B60", "600"), ("#C2185B", "700"), ("#AD1457", "800"), ("#880E4F", "900"), ("#FF80AB", "A100"), ("#FF4081", "A200"), ("#F50057", "A400"), ("#C51162", "A700")
            ]),
            ("Purple", [
                ("#E1BEE7", "100"), ("#CE93D8", "200"), ("#BA68C8", "300"), ("#AB47BC", "400"), ("#9C27B0", "500"), ("#8E24AA", "600"), ("#7B1FA2", "700"), ("#6A1B9A", "800"), ("#4A148C", "900"), ("#EA80FC", "A100"), ("#E040FB", "A200"), ("#D500F9", "A400"), ("#AA00FF", "A700")
            ])
        ]
        
        second_row_palettes = [
            ("Deep Purple", [
                ("#D1C4E9", "100"), ("#B39DDB", "200"), ("#9575CD", "300"), ("#7E57C2", "400"), ("#673AB7", "500"), ("#5E35B1", "600"), ("#512DA8", "700"), ("#4527A0", "800"), ("#311B92", "900"), ("#B388FF", "A100"), ("#7C4DFF", "A200"), ("#651FFF", "A400"), ("#6200EA", "A700")
            ]),
            ("Indigo", [
                ("#C5CAE9", "100"), ("#9FA8DA", "200"), ("#7986CB", "300"), ("#5C6BC0", "400"), ("#3F51B5", "500"), ("#3949AB", "600"), ("#303F9F", "700"), ("#283593", "800"), ("#1A237E", "900"), ("#8C9EFF", "A100"), ("#536DFE", "A200"), ("#3D5AFE", "A400"), ("#304FFE", "A700")
            ]),
            ("Blue", [
                ("#BBDEFB", "100"), ("#90CAF9", "200"), ("#64B5F6", "300"), ("#42A5F5", "400"), ("#2196F3", "500"), ("#1E88E5", "600"), ("#1976D2", "700"), ("#1565C0", "800"), ("#0D47A1", "900"), ("#82B1FF", "A100"), ("#448AFF", "A200"), ("#2979FF", "A400"), ("#2962FF", "A700")
            ])
        ]
        
        third_row_palettes = [
            ("Light Blue", [
                ("#B3E5FC", "100"), ("#81D4FA", "200"), ("#4FC3F7", "300"), ("#29B6F6", "400"), ("#03A9F4", "500"), ("#039BE5", "600"), ("#0288D1", "700"), ("#0277BD", "800"), ("#01579B", "900"), ("#80D8FF", "A100"), ("#40C4FF", "A200"), ("#00B0FF", "A400"), ("#0091EA", "A700")
            ]),
            ("Cyan", [
                ("#B2EBF2", "100"), ("#80DEEA", "200"), ("#4DD0E1", "300"), ("#26C6DA", "400"), ("#00BCD4", "500"), ("#00ACC1", "600"), ("#0097A7", "700"), ("#00838F", "800"), ("#006064", "900"), ("#84FFFF", "A100"), ("#18FFFF", "A200"), ("#00E5FF", "A400"), ("#00B8D4", "A700")
            ]),
            ("Teal", [
                ("#B2DFDB", "100"), ("#80CBC4", "200"), ("#4DB6AC", "300"), ("#26A69A", "400"), ("#009688", "500"), ("#00897B", "600"), ("#00796B", "700"), ("#00695C", "800"), ("#004D40", "900"), ("#A7FFEB", "A100"), ("#64FFDA", "A200"), ("#1DE9B6", "A400"), ("#00BFA5", "A700")
            ])
        ]
        
        fourth_row_palettes = [
            ("Green", [
                ("#C8E6C9", "100"), ("#A5D6A7", "200"), ("#81C784", "300"), ("#66BB6A", "400"), ("#4CAF50", "500"), ("#43A047", "600"), ("#388E3C", "700"), ("#2E7D32", "800"), ("#1B5E20", "900"), ("#B9F6CA", "A100"), ("#69F0AE", "A200"), ("#00E676", "A400"), ("#00C853", "A700")
            ]),
            ("Light Green", [
                ("#DCEDC8", "100"), ("#C5E1A5", "200"), ("#AED581", "300"), ("#9CCC65", "400"), ("#8BC34A", "500"), ("#7CB342", "600"), ("#689F38", "700"), ("#558B2F", "800"), ("#33691E", "900"), ("#CCFF90", "A100"), ("#B2FF59", "A200"), ("#76FF03", "A400"), ("#64DD17", "A700")
            ]),
            ("Lime", [
                ("#F0F4C3", "100"), ("#E6EE9C", "200"), ("#DCE775", "300"), ("#D4E157", "400"), ("#CDDC39", "500"), ("#C0CA33", "600"), ("#AFB42B", "700"), ("#9E9D24", "800"), ("#827717", "900"), ("#F4FF81", "A100"), ("#EEFF41", "A200"), ("#C6FF00", "A400"), ("#AEEA00", "A700")
            ])
        ]
        
        fifth_row_palettes = [
            ("Yellow", [
                ("#FFF9C4", "100"), ("#FFF59D", "200"), ("#FFF176", "300"), ("#FFEE58", "400"), ("#FFEB3B", "500"), ("#FDD835", "600"), ("#FBC02D", "700"), ("#F9A825", "800"), ("#F57F17", "900"), ("#FFFF8D", "A100"), ("#FFFF00", "A200"), ("#FFEA00", "A400"), ("#FFD600", "A700")
            ]),
            ("Amber", [
                ("#FFECB3", "100"), ("#FFE082", "200"), ("#FFD54F", "300"), ("#FFCA28", "400"), ("#FFC107", "500"), ("#FFB300", "600"), ("#FFA000", "700"), ("#FF8F00", "800"), ("#FF6F00", "900"), ("#FFE57F", "A100"), ("#FFD740", "A200"), ("#FFC400", "A400"), ("#FFAB00", "A700")
            ]),
            ("Orange", [
                ("#FFE0B2", "100"), ("#FFCC80", "200"), ("#FFB74D", "300"), ("#FFA726", "400"), ("#FF9800", "500"), ("#FB8C00", "600"), ("#F57C00", "700"), ("#EF6C00", "800"), ("#E65100", "900"), ("#FFD180", "A100"), ("#FFAB40", "A200"), ("#FF9100", "A400"), ("#FF6D00", "A700")
            ])
        ]
        
        for row_palettes in [first_row_palettes, second_row_palettes, third_row_palettes, fourth_row_palettes, fifth_row_palettes]:
            row_layout = QHBoxLayout()
            row_layout.setSpacing(15)
            
            for palette_name, colors in row_palettes:
                column_layout = QVBoxLayout()
                column_layout.setSpacing(3)
                
                palette_label = QLabel(palette_name)
                palette_label.setStyleSheet("margin-bottom: 2px;")
                palette_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
                column_layout.addWidget(palette_label)
                
                color_buttons_layout = QHBoxLayout()
                color_buttons_layout.setSpacing(3)
                
                for color_hex, color_name in colors:
                    color_btn = QPushButton()
                    color_btn.setFixedSize(18, 18)
                    
                    is_current = color_hex == self.current_theme_color
                    border_style = "2px solid #fff" if is_current else "none"
                    
                    color_btn.setStyleSheet(f"""
                        QPushButton {{
                            background-color: {color_hex};
                            border: {border_style};
                            border-radius: 9px;
                        }}
                        QPushButton:hover {{
                            border: 2px solid #fff;
                        }}
                        QPushButton:pressed {{
                            border: 2px solid #fff;
                        }}
                    """)
                    color_btn.setCursor(Qt.CursorShape.PointingHandCursor)
                    color_btn.setToolTip(f"{palette_name} {color_name}\n{color_hex}")
                    color_btn.clicked.connect(lambda checked, color=color_hex, btn=color_btn: self.change_theme_color(color, btn))
                    
                    self.color_buttons[color_hex] = color_btn
                    
                    color_buttons_layout.addWidget(color_btn)
                
                column_layout.addLayout(color_buttons_layout)
                row_layout.addLayout(column_layout)
            
            grid_layout.addLayout(row_layout)

        theme_layout.addLayout(grid_layout)
        theme_layout.addStretch()

        theme_tab.setLayout(theme_layout)
        self.tab_widget.addTab(theme_tab, "Theme")

    def change_theme_color(self, color, clicked_btn=None):
        if hasattr(self, 'color_buttons'):
            for color_hex, btn in self.color_buttons.items():
                if color_hex == self.current_theme_color:
                    btn.setStyleSheet(f"""
                        QPushButton {{
                            background-color: {color_hex};
                            border: none;
                            border-radius: 9px;
                        }}
                        QPushButton:hover {{
                            border: 2px solid #fff;
                        }}
                        QPushButton:pressed {{
                            border: 2px solid #fff;
                        }}
                    """)
                    break
        
        self.current_theme_color = color
        self.settings.setValue('theme_color', color)
        self.settings.sync()
        
        if clicked_btn:
            clicked_btn.setStyleSheet(f"""
                QPushButton {{
                    background-color: {color};
                    border: 2px solid #fff;
                    border-radius: 9px;
                }}
                QPushButton:hover {{
                    border: 2px solid #fff;
                }}
                QPushButton:pressed {{
                    border: 2px solid #fff;
                }}
            """)
        
        qdarktheme.setup_theme(
            custom_colors={
                "[dark]": {
                    "primary": color,
                }
            }
        )
        
    def setup_about_tab(self):
        about_tab = QWidget()
        about_layout = QVBoxLayout()
        about_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
        about_layout.setSpacing(15)

        sections = [
            ("Check for Updates", "Check", "https://github.com/afkarxyz/SpotiFLAC/releases"),
            ("Report an Issue", "Report", "https://github.com/afkarxyz/SpotiFLAC/issues")
        ]

        for title, button_text, url in sections:
            section_widget = QWidget()
            section_layout = QVBoxLayout(section_widget)
            section_layout.setSpacing(10)
            section_layout.setContentsMargins(0, 0, 0, 0)

            label = QLabel(title)
            label.setStyleSheet("color: palette(text); font-weight: bold;")
            label.setAlignment(Qt.AlignmentFlag.AlignCenter)
            section_layout.addWidget(label)

            button = QPushButton(button_text)
            button.setFixedSize(120, 25)
            button.setCursor(Qt.CursorShape.PointingHandCursor)
            button.clicked.connect(lambda _, url=url: QDesktopServices.openUrl(QUrl(url if url.startswith(('http://', 'https://')) else f'https://{url}')))
            section_layout.addWidget(button, alignment=Qt.AlignmentFlag.AlignCenter)

            about_layout.addWidget(section_widget)

        footer_label = QLabel(f"v{self.current_version} | October 2025")
        about_layout.addWidget(footer_label, alignment=Qt.AlignmentFlag.AlignCenter)

        about_tab.setLayout(about_layout)
        self.tab_widget.addTab(about_tab, "About")
            
    def on_service_changed(self, index):
        service = self.service_dropdown.currentData()
        self.service = service
        self.settings.setValue('service', service)
        self.settings.sync()
        self.log_output.append(f"Service changed to: {self.service_dropdown.currentText()}")

    def save_url(self):
        self.settings.setValue('spotify_url', self.spotify_url.text().strip())
        self.settings.sync()
        
    def save_filename_format(self):
        if self.artist_title_radio.isChecked():
            self.filename_format = "artist_title"
        elif self.title_only_radio.isChecked():
            self.filename_format = "title_only"
        else:
            self.filename_format = "title_artist"
        self.settings.setValue('filename_format', self.filename_format)
        self.settings.sync()
        
    def save_track_numbering(self):
        self.use_track_numbers = self.track_number_checkbox.isChecked()
        self.settings.setValue('use_track_numbers', self.use_track_numbers)
        self.settings.sync()
    
    def save_artist_subfolder_setting(self):
        self.use_artist_subfolders = self.artist_subfolder_checkbox.isChecked()
        self.settings.setValue('use_artist_subfolders', self.use_artist_subfolders)
        self.settings.sync()
    
    def save_album_subfolder_setting(self):
        self.use_album_subfolders = self.album_subfolder_checkbox.isChecked()
        self.settings.setValue('use_album_subfolders', self.use_album_subfolders)
        self.settings.sync()
    
    def save_track_list_format(self):
        format_value = self.track_list_format_dropdown.currentData()
        self.track_list_format = format_value
        self.settings.setValue('track_list_format', format_value)
        self.settings.sync()
        if self.tracks:
            self.update_track_list_display()
    
    def save_date_format(self):
        format_value = self.date_format_dropdown.currentData()
        self.date_format = format_value
        self.settings.setValue('date_format', format_value)
        self.settings.sync()
        if self.tracks:
            self.update_track_list_display()
    
    def save_settings(self):
        self.settings.setValue('output_path', self.output_dir.text().strip())
        self.settings.sync()
        self.log_output.append("Settings saved successfully!")

    def update_timer(self):
        self.elapsed_time = self.elapsed_time.addSecs(1)
        self.time_label.setText(self.elapsed_time.toString("hh:mm:ss"))
                        
    def fetch_tracks(self):
        url = self.spotify_url.text().strip()
        
        if not url:
            self.log_output.append('Warning: Please enter a Spotify URL.')
            return

        try:
            self.reset_state()
            self.reset_ui()
            
            self.log_output.append('Just a moment. Fetching metadata...')
            self.tab_widget.setCurrentWidget(self.process_tab)
            
            self.metadata_worker = MetadataFetchWorker(url)
            self.metadata_worker.finished.connect(self.on_metadata_fetched)
            self.metadata_worker.error.connect(self.on_metadata_error)
            self.metadata_worker.start()
            
        except Exception as e:
            self.log_output.append(f'Error: Failed to start metadata fetch: {str(e)}')
    
    def on_metadata_fetched(self, metadata):
        try:
            url_info = parse_uri(self.spotify_url.text().strip())
            
            if url_info["type"] == "track":
                self.handle_track_metadata(metadata["track"])
            elif url_info["type"] == "album":
                self.handle_album_metadata(metadata)
            elif url_info["type"] == "playlist":
                self.handle_playlist_metadata(metadata)
            elif url_info["type"] == "artist_discography":
                self.handle_discography_metadata(metadata)
            elif url_info["type"] == "artist":
                self.handle_artist_metadata(metadata)
                
            self.update_button_states()
            self.tab_widget.setCurrentIndex(0)
        except Exception as e:
            self.log_output.append(f'Error: {str(e)}')
    
    def on_metadata_error(self, error_message):
        self.log_output.append(f'Error: {error_message}')

    def handle_track_metadata(self, track_data):
        track_id = track_data["external_urls"].split("/")[-1]
        
        track = Track(
            external_urls=track_data["external_urls"],
            title=track_data["name"],
            artists=track_data["artists"],
            album=track_data["album_name"],
            track_number=1,
            duration_ms=track_data.get("duration_ms", 0),
            id=track_id,
            isrc=track_data.get("isrc", ""),
            release_date=track_data.get("release_date", "")
        )
        
        self.tracks = [track]
        self.all_tracks = [track]
        self.is_single_track = True
        self.is_album = self.is_playlist = False
        self.album_or_playlist_name = f"{self.tracks[0].title} - {self.tracks[0].artists}"
        
        metadata = {
            'title': track_data["name"],
            'artists': track_data["artists"],
            'releaseDate': track_data["release_date"],
            'cover': track_data["images"],
            'duration_ms': track_data.get("duration_ms", 0)
        }
        self.update_display_after_fetch(metadata)

    def handle_album_metadata(self, album_data):
        self.album_or_playlist_name = album_data["album_info"]["name"]
        self.tracks = []
        
        for track in album_data["track_list"]:
            track_id = track["external_urls"].split("/")[-1]
            
            self.tracks.append(Track(
                external_urls=track["external_urls"],
                title=track["name"],
                artists=track["artists"],
                album=self.album_or_playlist_name,
                track_number=track["track_number"],
                duration_ms=track.get("duration_ms", 0),
                id=track_id,
                isrc=track.get("isrc", ""),
                release_date=track.get("release_date", "")
            ))
        
        self.all_tracks = self.tracks.copy()
        self.is_album = True
        self.is_playlist = self.is_single_track = False
        
        metadata = {
            'title': album_data["album_info"]["name"],
            'artists': album_data["album_info"]["artists"],
            'releaseDate': album_data["album_info"]["release_date"],
            'cover': album_data["album_info"]["images"],
            'total_tracks': album_data["album_info"]["total_tracks"]
        }
        self.update_display_after_fetch(metadata)

    def handle_playlist_metadata(self, playlist_data):
        self.album_or_playlist_name = playlist_data["playlist_info"]["owner"]["name"]
        self.tracks = []
        
        for track in playlist_data["track_list"]:
            track_id = track["external_urls"].split("/")[-1]
            
            self.tracks.append(Track(
                external_urls=track["external_urls"],
                title=track["name"],
                artists=track["artists"],
                album=track["album_name"],
                track_number=track.get("track_number", len(self.tracks) + 1),
                duration_ms=track.get("duration_ms", 0),
                id=track_id,
                isrc=track.get("isrc", ""),
                release_date=track.get("release_date", "")
            ))
        
        self.all_tracks = self.tracks.copy()
        self.is_playlist = True
        self.is_album = self.is_single_track = False
        
        metadata = {
            'title': playlist_data["playlist_info"]["owner"]["name"],
            'artists': playlist_data["playlist_info"]["owner"]["display_name"],
            'cover': playlist_data["playlist_info"]["owner"]["images"],
            'followers': playlist_data["playlist_info"]["followers"]["total"],
            'total_tracks': playlist_data["playlist_info"]["tracks"]["total"]
        }
        self.update_display_after_fetch(metadata)

    def handle_discography_metadata(self, discography_data):
        artist_info = discography_data["artist_info"]
        self.album_or_playlist_name = f"{artist_info['name']} - Discography ({artist_info['discography_type'].title()})"
        self.tracks = []
        
        for track in discography_data["track_list"]:
            track_id = track["external_urls"].split("/")[-1] if track.get("external_urls") else ""
            
            self.tracks.append(Track(
                external_urls=track.get("external_urls", ""),
                title=track["name"],
                artists=track["artists"],
                album=track["album_name"],
                track_number=track.get("track_number", len(self.tracks) + 1),
                duration_ms=track.get("duration_ms", 0),
                id=track_id,
                isrc=track.get("isrc", ""),
                release_date=track.get("release_date", "")
            ))
        
        self.all_tracks = self.tracks.copy()
        self.is_playlist = True
        self.is_album = self.is_single_track = False
        
        metadata = {
            'title': f"{artist_info['name']} - Discography",
            'artists': f"{artist_info['discography_type'].title()} • {artist_info['total_albums']} albums",
            'cover': artist_info["images"],
            'followers': artist_info.get("followers", 0),
            'total_tracks': len(self.tracks),
            'discography_type': artist_info['discography_type']
        }
        self.update_display_after_fetch(metadata)

    def handle_artist_metadata(self, artist_data):
        self.reset_state()
        
        metadata = {
            'title': artist_data["artist"]["name"],
            'artists': f"Followers: {artist_data['artist']['followers']:,}",
            'cover': artist_data["artist"]["images"],
            'followers': artist_data["artist"]["followers"],
            'genres': artist_data["artist"].get("genres", [])
        }
        
        self.update_info_widget_artist_only(metadata)

    def update_display_after_fetch(self, metadata):
        self.track_list.setVisible(not self.is_single_track)
        
        if not self.is_single_track:
            self.search_widget.show()
            self.update_track_list_display()
        else:
            self.search_widget.hide()
        
        self.update_info_widget(metadata)

    def update_info_widget(self, metadata):
        self.title_label.setText(metadata['title'])
        
        if self.is_single_track or self.is_album:
            artists = metadata['artists'] if isinstance(metadata['artists'], list) else metadata['artists'].split(", ")
            label_text = "Artists" if len(artists) > 1 else "Artist"
            artists_text = ", ".join(artists)
            self.artists_label.setText(f"<b>{label_text}</b> {artists_text}")
        else:
            self.artists_label.setText(f"<b>Owner</b> {metadata['artists']}")
        
        if self.is_playlist and 'followers' in metadata:
            self.followers_label.setText(f"<b>Followers</b> {metadata['followers']:,}")
            self.followers_label.show()
        else:
            self.followers_label.hide()
        
        if metadata.get('releaseDate'):
            try:
                release_date = metadata['releaseDate']
                if len(release_date) == 4:
                    date_obj = datetime.strptime(release_date, "%Y")
                elif len(release_date) == 7:
                    date_obj = datetime.strptime(release_date, "%Y-%m")
                else:
                    date_obj = datetime.strptime(release_date, "%Y-%m-%d")
                
                formatted_date = date_obj.strftime("%d-%m-%Y")
                self.release_date_label.setText(f"<b>Released</b> {formatted_date}")
                self.release_date_label.show()
            except ValueError:
                self.release_date_label.setText(f"<b>Released</b> {metadata['releaseDate']}")
                self.release_date_label.show()
        else:
            self.release_date_label.hide()
        
        if self.is_single_track:
            duration = self.format_duration(metadata.get('duration_ms', 0))
            self.type_label.setText(f"<b>Duration</b> {duration}")
        elif self.is_album:
            total_tracks = metadata.get('total_tracks', 0)
            self.type_label.setText(f"<b>Album</b> • {total_tracks} tracks")
        elif self.is_playlist:
            total_tracks = metadata.get('total_tracks', 0)
            if metadata.get('discography_type'):
                discography_type = metadata['discography_type'].title()
                self.type_label.setText(f"<b>Discography ({discography_type})</b> • {total_tracks} tracks")
            else:
                self.type_label.setText(f"<b>Playlist</b> • {total_tracks} tracks")
        
        self.network_manager.get(QNetworkRequest(QUrl(metadata['cover'])))
        
        self.info_widget.show()

    def update_info_widget_artist_only(self, metadata):
        self.title_label.setText(metadata['title'])
        self.artists_label.setText(f"<b>Followers</b> {metadata['followers']:,}")
        
        if metadata.get('genres'):
            genres_text = ", ".join(metadata['genres'][:3])
            if len(metadata['genres']) > 3:
                genres_text += f" (+{len(metadata['genres']) - 3} more)"
            self.followers_label.setText(f"<b>Genres</b> {genres_text}")
            self.followers_label.show()
        else:
            self.followers_label.hide()
        
        self.release_date_label.hide()
        self.type_label.setText("<b>Artist Profile</b> • No tracks available for download")
        
        self.network_manager.get(QNetworkRequest(QUrl(metadata['cover'])))
        
        self.track_list.hide()
        self.search_widget.hide()
        self.hide_track_buttons()
        
        self.info_widget.show()

    def reset_info_widget(self):
        self.title_label.clear()
        self.artists_label.clear()
        self.followers_label.clear()
        self.release_date_label.clear()
        self.type_label.clear()
        self.cover_label.clear()
        self.info_widget.hide()

    def on_cover_loaded(self, reply):
        if reply.error() == QNetworkReply.NetworkError.NoError:
            data = reply.readAll()
            pixmap = QPixmap()
            pixmap.loadFromData(data)
            self.cover_label.setPixmap(pixmap)

    def update_button_states(self):
        if self.is_single_track:
            for btn in [self.download_selected_btn, self.download_all_btn, self.remove_btn, self.clear_btn]:
                btn.hide()
            
            self.single_track_container.show()
            
            self.single_download_btn.setEnabled(True)
            self.single_clear_btn.setEnabled(True)
            
        else:
            self.single_track_container.hide()
            
            self.download_selected_btn.show()
            self.download_all_btn.show()
            self.remove_btn.show()
            self.clear_btn.show()
            
            self.download_all_btn.setText('Download All')
            self.clear_btn.setText('Clear')
            
            self.download_all_btn.setMinimumWidth(120)
            self.clear_btn.setMinimumWidth(120)
            
            self.download_selected_btn.setEnabled(True)
            self.download_all_btn.setEnabled(True)

    def hide_track_buttons(self):
        buttons = [
            self.download_selected_btn,
            self.download_all_btn,
            self.remove_btn,
            self.clear_btn
        ]
        for btn in buttons:
            btn.hide()
        
        if hasattr(self, 'single_track_container'):
            self.single_track_container.hide()

    def download_selected(self):
        if self.is_single_track:
            self.download_all()
        else:
            selected_items = self.track_list.selectedItems()            
            if not selected_items:
                self.log_output.append('Warning: Please select tracks to download.')
                return
            selected_indices = [self.track_list.row(item) for item in selected_items]
            self.download_tracks(selected_indices)

    def download_all(self):
        if self.is_single_track:
            self.download_tracks([0])
        else:
            self.download_tracks(range(len(self.tracks)))

    def download_tracks(self, indices):
        self.log_output.clear()
        raw_outpath = self.output_dir.text().strip()
        outpath = os.path.normpath(raw_outpath)
        if not os.path.exists(outpath):
            self.log_output.append('Warning: Invalid output directory.')
            return

        tracks_to_download = self.tracks if self.is_single_track else [self.tracks[i] for i in indices]

        if self.is_album or self.is_playlist:
            name = self.album_or_playlist_name.strip()
            folder_name = re.sub(r'[<>:"/\\|?*]', '_', name)
            outpath = os.path.join(outpath, folder_name)
            os.makedirs(outpath, exist_ok=True)

        try:
            self.start_download_worker(tracks_to_download, outpath)
        except Exception as e:
            self.log_output.append(f"Error: An error occurred while starting the download: {str(e)}")
    
    def start_download_worker(self, tracks_to_download, outpath):
        service = self.service_dropdown.currentData()
        
        self.worker = DownloadWorker(
            tracks_to_download, 
            outpath,
            self.is_single_track, 
            self.is_album, 
            self.is_playlist, 
            self.album_or_playlist_name,
            self.filename_format,
            self.use_track_numbers,
            self.use_artist_subfolders,
            self.use_album_subfolders,
            service
        )
        self.worker.finished.connect(lambda success, message, failed_tracks, successful_tracks, skipped_tracks: self.on_download_finished(success, message, failed_tracks, successful_tracks, skipped_tracks))
        self.worker.progress.connect(self.update_progress)
        self.worker.start()
        self.start_timer()
        self.update_ui_for_download_start()

    def update_ui_for_download_start(self):
        self.download_selected_btn.setEnabled(False)
        self.download_all_btn.setEnabled(False)
        
        if hasattr(self, 'single_download_btn'):
            self.single_download_btn.setEnabled(False)
        if hasattr(self, 'single_clear_btn'):
            self.single_clear_btn.setEnabled(False)
            
        self.stop_btn.show()
        self.pause_resume_btn.show()
        self.progress_bar.show()
        self.progress_bar.setValue(0)
        
        self.tab_widget.setCurrentWidget(self.process_tab)

    def update_progress(self, message, percentage):
        self.log_output.append(message)
        self.log_output.moveCursor(QTextCursor.MoveOperation.End)
        if percentage > 0:
            self.progress_bar.setValue(percentage)

    def stop_download(self):
        if hasattr(self, 'worker'):
            self.worker.stop()
        self.stop_timer()
        self.on_download_finished(True, "Download stopped by user.", [], [], [])
        
    def on_download_finished(self, success, message, failed_tracks, successful_tracks=None, skipped_tracks=None):
        self.progress_bar.hide()
        self.stop_btn.hide()
        self.pause_resume_btn.hide()
        self.pause_resume_btn.setText('Pause')
        self.stop_timer()
        
        if successful_tracks is not None:
            self.successful_downloads = successful_tracks
        if skipped_tracks is not None:
            self.skipped_downloads = skipped_tracks
        
        if (hasattr(self, 'successful_downloads') and self.successful_downloads) or (hasattr(self, 'skipped_downloads') and self.skipped_downloads):
            self.remove_successful_btn.show()
        else:
            self.remove_successful_btn.hide()
        
        self.download_selected_btn.setEnabled(True)
        self.download_all_btn.setEnabled(True)
        
        if hasattr(self, 'single_download_btn'):
            self.single_download_btn.setEnabled(True)
        if hasattr(self, 'single_clear_btn'):
            self.single_clear_btn.setEnabled(True)
        
        if success:
            self.log_output.append(f"\nStatus: {message}")
            if failed_tracks:
                self.log_output.append("\nFailed downloads:")
                for title, artists, error in failed_tracks:
                    self.log_output.append(f"• {title} - {artists}")
                    self.log_output.append(f"  Error: {error}\n")
        else:
            self.log_output.append(f"Error: {message}")

        self.tab_widget.setCurrentWidget(self.process_tab)
    
    def toggle_pause_resume(self):
        if hasattr(self, 'worker'):
            if self.worker.is_paused:
                self.worker.resume()
                self.pause_resume_btn.setText('Pause')
                self.timer.start(1000)
            else:
                self.worker.pause()
                self.pause_resume_btn.setText('Resume')

    def remove_successful_downloads(self):
        successful_tracks = getattr(self, 'successful_downloads', [])
        skipped_tracks = getattr(self, 'skipped_downloads', [])
        
        if not successful_tracks and not skipped_tracks:
            self.log_output.append("No downloaded or skipped tracks to remove.")
            return
        
        tracks_to_remove = []
        
        for track in self.tracks:
            for successful_track in successful_tracks:
                if (track.title == successful_track.title and 
                    track.artists == successful_track.artists and
                    track.album == successful_track.album):
                    tracks_to_remove.append(track)
                    break
        
        for track in self.tracks:
            for skipped_track in skipped_tracks:
                if (track.title == skipped_track.title and 
                    track.artists == skipped_track.artists and
                    track.album == skipped_track.album):
                    if track not in tracks_to_remove:
                        tracks_to_remove.append(track)
                    break
        
        if tracks_to_remove:
            for track in tracks_to_remove:
                if track in self.tracks:
                    self.tracks.remove(track)
                if track in self.all_tracks:
                    self.all_tracks.remove(track)
            
            self.update_track_list_display()
            successful_count = len([t for t in tracks_to_remove if t in successful_tracks])
            skipped_count = len([t for t in tracks_to_remove if t in skipped_tracks])
            
            message = f"Removed {len(tracks_to_remove)} tracks from the list"
            if successful_count > 0:
                message += f" ({successful_count} downloaded"
            if skipped_count > 0:
                message += f", {skipped_count} already existed" if successful_count > 0 else f" ({skipped_count} already existed"
            if successful_count > 0 or skipped_count > 0:
                message += ")"
            
            self.log_output.append(message + ".")
            self.tab_widget.setCurrentIndex(0)
        else:
            self.log_output.append("No matching tracks found in the current list.")
        
        self.remove_successful_btn.hide()

    def remove_selected_tracks(self):
        if not self.is_single_track:
            selected_items = self.track_list.selectedItems()
            selected_indices = [self.track_list.row(item) for item in selected_items]
            
            tracks_to_remove = [self.tracks[i] for i in selected_indices]
            
            for track in tracks_to_remove:
                if track in self.tracks:
                    self.tracks.remove(track)
                if track in self.all_tracks:
                    self.all_tracks.remove(track)
            

            
            self.update_track_list_display()

    def clear_tracks(self):
        self.reset_state()
        self.reset_ui()
        self.tab_widget.setCurrentIndex(0)

    def start_timer(self):
        self.elapsed_time = QTime(0, 0, 0)
        self.time_label.setText("00:00:00")
        self.time_label.show()
        self.timer.start(1000)
    
    def stop_timer(self):
        self.timer.stop()
        self.time_label.hide()

    def closeEvent(self, event):
        if hasattr(self, 'timer'):
            self.timer.stop()
        
        if hasattr(self, 'service_dropdown'):
            for attr_name in ['tidal_status_checker', 'deezer_status_checker']:
                if hasattr(self.service_dropdown, attr_name):
                    checker = getattr(self.service_dropdown, attr_name)
                    if checker.isRunning():
                        checker.quit()
                        checker.wait()
        
        if hasattr(self, 'worker') and self.worker and self.worker.isRunning():
            self.worker.stop()
            self.worker.quit()
            self.worker.wait()
        
        event.accept()

if __name__ == '__main__':
    try:
        if sys.platform == "win32":
            import io
            sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
            sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
    except Exception as e:
        pass
        
    app = QApplication(sys.argv)
    
    settings = QSettings('SpotiFLAC', 'Settings')
    theme_color = settings.value('theme_color', '#2196F3')
    
    qdarktheme.setup_theme(
        custom_colors={
            "[dark]": {
                "primary": theme_color,
            }
        }
    )
    ex = SpotiFLACGUI()
    ex.show()
    sys.exit(app.exec())