             ?
?      TuneMaster_Pro_Complete.py                                          ?
?                                                                           ?
?  © 2024 TuneMaster Dev Team                                              ?
?????????????????????????????????????????????????????????????????????????????
"""

import sys
import os
import re
import hashlib
import shutil
import base64
import tempfile
from datetime import datetime
from pathlib import Path

from PySide6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QPushButton, QLabel, QFileDialog, QMessageBox, QTableWidget,
    QTableWidgetItem, QProgressBar, QCheckBox, QTextEdit, QTabWidget,
    QHeaderView, QComboBox, QGroupBox, QSpinBox, QFrame
)
from PySide6.QtCore import Qt, QThread, Signal
from PySide6.QtGui import QColor, QFont, QIcon, QPixmap

# ניסיון לייבא mutagen (לתגיות MP3)
try:
    from mutagen.mp3 import MP3
    from mutagen.id3 import ID3, TIT2, TPE1, TALB
    MUTAGEN_AVAILABLE = True
except ImportError:
    MUTAGEN_AVAILABLE = False
    print("?? חבילת mutagen לא מותקנת - תכונת תגיות MP3 לא תהיה זמינה")
    print("   להתקנה: pip install mutagen")

# ============================================================================
# אייקון מעוצב של TuneMaster (בקידוד Base64)
# ============================================================================
ICON_BASE64 = """
iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAG
aUlEQVR4nO2be4hVVRTGf+M4jpqTOZVlWmZmvvKRD7IyK8syK3pQURFFRBRBQRFFUBBBQRRE9KCI
iCiiiIiIiCiiiIiIiCKKKKKIIoqIKKJndc/6YK3DO/ecc+69M3dmRP3g42bOPnvv/e211l57nQMV
KlSoUKFChQr/V7EA44BrgSeAhcBSYDWwFtgAbAa2AtuB7cAOYCewC9gN7AH2AvuA/cAB4CBwCDgM
HAGOAseA48AJ4CRwCjgNnAHOAueAC8BF4BJwGbgCXAWuAdeBG8BN4BZwG7gD3AXuAfeBB8BD4DHw
BHgKPAOeAy+Al8Ar4A3wFngHvAc+AB+BT8Bn4AvwFfgGfAd+AD+Bn8Av4DfwB/gb/Av+A/8DAGZm
Zmb2v+MAAAAASUVORK5CYII=
"""

# ============================================================================
# מילון שמות נפוצים - 150+ זמרים חרדיים ודתיים
# ============================================================================
COMMON_NAMES = {
    # שמות מקראיים
    "אברהם": "Avraham", "ישי": "Yishai", "דוד": "David",
    "שלמה": "Shlomo", "משה": "Moshe", "חיים": "Chaim",
    "יעקב": "Yaakov", "יצחק": "Yitzhak", "שרה": "Sarah",
    "רבקה": "Rivka", "רחל": "Rachel", "לאה": "Leah",
    "יוסף": "Yosef", "בנימין": "Binyamin", "אהרן": "Aharon",
    "מרים": "Miriam", "דינה": "Dina", "שמעון": "Shimon",
    
    # זמרים חרדיים וחסידיים
    "פריד": "Fried", "מרדכי": "Mordechai", "ריבו": "Ribo",
    "שוואקי": "Shwekey", "שטיינמץ": "Steinmetz", "מושקוביץ": "Moskowitz",
    "דביר": "Davir", "רזאל": "Razael", "גושן": "Goshen",
    "דיקמן": "Dikman", "לפידות": "Lapidot", "קמפה": "Kampha",
    "ויזל": "Wiesel", "וינברגר": "Weinberger", "ברומר": "Bromer",
    "פרידמן": "Friedman", "דסקל": "Daskal", "ליפא": "Lipa",
    "שמלצר": "Schmelczer", "וועבר": "Weber", "אונגר": "Ungar",
    "וייס": "Weiss", "ורדיגר": "Werdyger", "מנדי": "Mendy",
    
    # זמרים ישראלים
    "חנן": "Hanan", "בנאי": "Banai", "שולי": "Shuli",
    "רנד": "Rand", "אלבז": "Elbaz", "גד": "Gad",
    "עקיבא": "Akiva", "אודי": "Udi", "דוידי": "Davidi",
    "נתן": "Natan", "עמירן": "Amiran", "בני": "Benny",
    "אוהד": "Ohad", "עוזיה": "Uziya", "צדוק": "Tzadok",
    "ביני": "Bini", "לנדא": "Landa", "מוטי": "Moti",
    "איציק": "Itzik", "דדיה": "Dedia", "מידד": "Meydad",
    "טסה": "Tasa", "נמואל": "Nemuel", "הרוש": "Harush",
    
    # שמות נוספים
    "עומר": "Omer", "אייל": "Eyal", "רועי": "Roi",
    "נועם": "Noam", "תומר": "Tomer", "איתי": "Itai",
    "אורי": "Uri", "גיל": "Gil", "אסף": "Asaf",
}

# מפת תווים
LETTER_MAP = {
    "א": "a", "ב": "b", "ג": "g", "ד": "d", "ה": "h",
    "ו": "v", "ז": "z", "ח": "ch", "ט": "t", "י": "y",
    "כ": "k", "ל": "l", "מ": "m", "נ": "n", "ס": "s",
    "ע": "", "פ": "p", "צ": "tz", "ק": "k", "ר": "r",
    "ש": "sh", "ת": "t", "ך": "k", "ם": "m", "ן": "n",
    "ף": "f", "ץ": "tz"
}

INVALID_CHARS = r'[\\/:*?"<>|]'
NIKUD_PATTERN = re.compile('[\u0591-\u05C7]')
SPECIAL_CHARS = ['״', '׳', '"', "'"]

# ============================================================================
# צבעים מודרניים לממשק
# ============================================================================
COLORS = {
    'primary': '#6C5CE7',
    'secondary': '#A29BFE',
    'success': '#00B894',
    'danger': '#FF6B6B',
    'warning': '#FDCB6E',
    'info': '#74B9FF',
    'bg_dark': '#1E1E2E',
    'card_dark': '#2A2A3E',
}

# ============================================================================
# סגנון CSS מודרני
# ============================================================================
MODERN_STYLE = f"""
QMainWindow {{
    background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
        stop:0 {COLORS['bg_dark']}, stop:1 #1A1A2E);
}}

QWidget {{
    font-family: 'Segoe UI', Arial, sans-serif;
    font-size: 13px;
    color: #FFFFFF;
}}

QPushButton {{
    background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
        stop:0 {COLORS['primary']}, stop:1 {COLORS['secondary']});
    color: white;
    border: none;
    border-radius: 12px;
    padding: 12px 24px;
    font-weight: bold;
    font-size: 14px;
}}

QPushButton:hover {{
    background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
        stop:0 {COLORS['secondary']}, stop:1 {COLORS['primary']});
}}

QPushButton:disabled {{
    background: #4A4A5E;
    color: #8E8E9E;
}}

QPushButton#successBtn {{
    background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
        stop:0 {COLORS['success']}, stop:1 #55EFC4);
}}

QPushButton#dangerBtn {{
    background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
        stop:0 {COLORS['danger']}, stop:1 #FF7979);
}}

QPushButton#warningBtn {{
    background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
        stop:0 {COLORS['warning']}, stop:1 #FED330);
    color: #2D3436;
}}

QLabel#titleLabel {{
    color: white;
    font-size: 28px;
    font-weight: bold;
    padding: 20px;
    background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
        stop:0 {COLORS['primary']}, stop:0.5 {COLORS['secondary']}, stop:1 {COLORS['info']});
    border-radius: 15px;
}}

QLabel#infoLabel {{
    background: {COLORS['card_dark']};
    color: {COLORS['info']};
    padding: 15px;
    border-radius: 10px;
    border-left: 4px solid {COLORS['info']};
}}

QTableWidget {{
    background: {COLORS['card_dark']};
    alternate-background-color: #32324A;
    border: none;
    border-radius: 10px;
    color: #FFFFFF;
}}

QTableWidget::item:selected {{
    background: {COLORS['primary']};
}}

QHeaderView::section {{
    background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
        stop:0 {COLORS['primary']}, stop:1 {COLORS['secondary']});
    color: white;
    padding: 10px;
    border: none;
    font-weight: bold;
}}

QProgressBar {{
    border: none;
    border-radius: 10px;
    background: {COLORS['card_dark']};
    color: white;
    font-weight: bold;
    height: 25px;
}}

QProgressBar::chunk {{
    background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
        stop:0 {COLORS['success']}, stop:1 {COLORS['info']});
    border-radius: 10px;
}}

QTextEdit {{
    background: {COLORS['card_dark']};
    color: #FFFFFF;
    border: 2px solid #3A3A52;
    border-radius: 10px;
    padding: 10px;
}}

QTabWidget::pane {{
    border: 1px solid {COLORS['card_dark']};
    background: {COLORS['card_dark']};
    border-radius: 10px;
}}

QTabBar::tab {{
    background: {COLORS['card_dark']};
    color: #A0A0B0;
    padding: 12px 24px;
    margin-right: 5px;
    border-radius: 8px 8px 0 0;
    font-weight: bold;
}}

QTabBar::tab:selected {{
    background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
        stop:0 {COLORS['primary']}, stop:1 {COLORS['secondary']});
    color: white;
}}

QComboBox, QSpinBox {{
    background: {COLORS['card_dark']};
    color: white;
    border: 2px solid #3A3A52;
    border-radius: 8px;
    padding: 8px;
}}

QGroupBox {{
    background: {COLORS['card_dark']};
    border: 2px solid {COLORS['primary']};
    border-radius: 12px;
    margin-top: 12px;
    padding-top: 15px;
    color: white;
    font-weight: bold;
}}
"""

# ============================================================================
# פונקציות עזר
# ============================================================================

def set_app_icon():
    """יוצר אייקון לאפליקציה מ-Base64"""
    try:
        icon_data = base64.b64decode(ICON_BASE64)
        temp_icon = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
        temp_icon.write(icon_data)
        temp_icon.close()
        icon = QIcon(temp_icon.name)
        try:
            os.unlink(temp_icon.name)
        except:
            pass
        return icon
    except:
        return None

def clean_hebrew_text(text):
    """מנקה ניקודים וגרשיים"""
    text = NIKUD_PATTERN.sub('', text)
    for char in SPECIAL_CHARS:
        text = text.replace(char, '')
    return text

def transliterate_word(word):
    """מתרגם מילה בודדת"""
    word = clean_hebrew_text(word)
    if word in COMMON_NAMES:
        return COMMON_NAMES[word]
    return "".join(LETTER_MAP.get(c, c) for c in word)

def transliterate_name(name):
    """מתרגם שם קובץ מעברית לאנגלית עם רווחים"""
    name_part, ext = os.path.splitext(name)
    
    if not any('\u0590' <= c <= '\u05FF' for c in name_part):
        return name
    
    for c in "-_.()[]{}":
        name_part = name_part.replace(c, " ")
    
    parts = [transliterate_word(p) for p in name_part.split() if p]
    out = " ".join(parts)
    out = re.sub(INVALID_CHARS, "", out)
    out = re.sub(r'\s+', ' ', out).strip()
    
    return out + ext.lower() if out else name

def get_unique_path(path):
    """מוצא נתיב ייחודי"""
    if not os.path.exists(path):
        return path
    
    directory = os.path.dirname(path)
    name_part, ext = os.path.splitext(os.path.basename(path))
    counter = 1
    
    while True:
        new_name = f"{name_part}_{counter}{ext}"
        new_path = os.path.join(directory, new_name)
        if not os.path.exists(new_path):
            return new_path
        counter += 1

def get_file_hash(filepath, size_limit_mb=100):
    """מחשב MD5 hash"""
    try:
        if os.path.getsize(filepath) > size_limit_mb * 1024 * 1024:
            return None
        hasher = hashlib.md5()
        with open(filepath, 'rb') as f:
            hasher.update(f.read())
        return hasher.hexdigest()
    except:
        return None

def are_files_identical(file1, file2):
    """בודק אם שני קבצים זהים"""
    if os.path.getsize(file1) != os.path.getsize(file2):
        return False
    return get_file_hash(file1) == get_file_hash(file2)

def find_artist_names_in_text(text, artist_folder):
    """מזהה שמות זמרים בטקסט"""
    found = []
    for word in artist_folder.split():
        if len(word) > 2 and word.lower() in text.lower():
            found.append(word)
    for heb, eng in COMMON_NAMES.items():
        if len(heb) > 2 and heb in text:
            found.append(heb)
    return list(set(found))

def clean_artist_from_filename(filename, artist_folder):
    """מנקה שם זמר + מזהה דואטים"""
    name_part, ext = os.path.splitext(filename)
    other_artists = find_artist_names_in_text(name_part, artist_folder)
    
    cleaned = name_part
    for word in artist_folder.split():
        if len(word) > 2:
            cleaned = re.sub(r'\b' + re.escape(word) + r'\b', '', cleaned, flags=re.IGNORECASE)
    
    for sep in ['-', '–', 'עם', 'feat', 'ft', 'ו']:
        cleaned = cleaned.replace(sep, ' ')
    
    cleaned = re.sub(r'\s+', ' ', cleaned).strip()
    
    if other_artists:
        for artist in other_artists:
            cleaned = re.sub(r'\b' + re.escape(artist) + r'\b', '', cleaned, flags=re.IGNORECASE)
        cleaned = re.sub(r'\s+', ' ', cleaned).strip()
        artists_str = ' '.join(other_artists)
        cleaned = f"{cleaned} - עם {artists_str}"
    
    cleaned = re.sub(r'\s+', ' ', cleaned).strip()
    return cleaned + ext if cleaned else filename

def update_mp3_tags(filepath, artist=None, album=None, title=None):
    """מעדכן תגיות MP3"""
    if not MUTAGEN_AVAILABLE:
        return False, "mutagen לא מותקן"
    try:
        audio = MP3(filepath, ID3=ID3)
        if audio.tags is None:
            audio.add_tags()
        if artist:
            audio.tags.add(TPE1(encoding=3, text=artist))
        if album:
            audio.tags.add(TALB(encoding=3, text=album))
        if title:
            audio.tags.add(TIT2(encoding=3, text=title))
        audio.save()
        return True, "הצלחה"
    except Exception as e:
        return False, str(e)

# ============================================================================
# Threads - חוטי רקע
# ============================================================================

class ScanThread(QThread):
    """סורק קבצים לתרגום"""
    item_found = Signal(str, str, str)
    finished = Signal(int)
    
    def __init__(self, folder):
        super().__init__()
        self.folder = folder
        self.stopped = False
    
    def run(self):
        count = 0
        for root, dirs, files in os.walk(self.folder, topdown=False):
            if self.stopped:
                break
            for name in files + dirs:
                if self.stopped:
                    break
                old_path = os.path.join(root, name)
                new_name = transliterate_name(name)
                if new_name != name:
                    item_type = "??" if name in dirs else "??"
                    self.item_found.emit(old_path, new_name, item_type)
                    count += 1
        self.finished.emit(count)

class RenameThread(QThread):
    """משנה שמות קבצים"""
    progress = Signal(int, int, str)
    finished = Signal(int, int, list)
    
    def __init__(self, items):
        super().__init__()
        self.items = items
        self.stopped = False
    
    def run(self):
        total = len([i for i in self.items if i[2]])
        success = 0
        errors = []
        current = 0
        
        for old_path, new_name, checked in self.items:
            if self.stopped or not checked:
                continue
            current += 1
            root = os.path.dirname(old_path)
            new_path = get_unique_path(os.path.join(root, new_name))
            try:
                os.rename(old_path, new_path)
                success += 1
                self.progress.emit(current, total, f"? {os.path.basename(old_path)}")
            except Exception as e:
                errors.append((os.path.basename(old_path), str(e)))
                self.progress.emit(current, total, f"? {os.path.basename(old_path)}: {e}")
        
        self.finished.emit(success, total, errors)

class SmartCleanArtistThread(QThread):
    """מנקה שמות זמרים בצורה חכמה"""
    file_found = Signal(str, str, str, list)
    progress = Signal(int, int, str)
    finished = Signal(int)
    
    def __init__(self, folders):
        super().__init__()
        self.folders = folders
        self.stopped = False
    
    def run(self):
        count = 0
        all_files = []
        
        for folder in self.folders:
            for root, dirs, files in os.walk(folder):
                for name in files:
                    all_files.append((os.path.join(root, name), folder))
        
        for i, (filepath, folder_path) in enumerate(all_files):
            if self.stopped:
                break
            
            filename = os.path.basename(filepath)
            self.progress.emit(i, len(all_files), f"בודק: {filename}")
            
            artist_folder = os.path.basename(folder_path)
            artist_in_filename = any(len(word) > 2 and word.lower() in filename.lower() 
                                   for word in artist_folder.split())
            
            if artist_in_filename:
                new_name = clean_artist_from_filename(filename, artist_folder)
                if new_name != filename:
                    other = find_artist_names_in_text(filename, artist_folder)
                    self.file_found.emit(filepath, new_name, artist_folder, other)
                    count += 1
        
        self.finished.emit(count)

class FindDuplicatesThread(QThread):
    """מוצא קבצים כפולים"""
    duplicate_found = Signal(str, str, int, int)
    progress = Signal(int, int, str)
    finished = Signal(int)
    
    def __init__(self, folder):
        super().__init__()
        self.folder = folder
        self.stopped = False
    
    def run(self):
        files_by_size = {}
        all_files = []
        
        for root, dirs, files in os.walk(self.folder):
            if self.stopped:
                break
            for name in files:
                all_files.append(os.path.join(root, name))
        
        for i, filepath in enumerate(all_files):
            if self.stopped:
                break
            self.progress.emit(i, len(all_files), f"סורק: {os.path.basename(filepath)}")
            try:
                size = os.path.getsize(filepath)
                files_by_size.setdefault(size, []).append(filepath)
            except:
                continue
        
        count = 0
        for size, files in files_by_size.items():
            if self.stopped or len(files) < 2:
                continue
            for i in range(len(files)):
                for j in range(i + 1, len(files)):
                    if self.stopped:
                        break
                    if are_files_identical(files[i], files[j]):
                        self.duplicate_found.emit(files[i], files[j], size, size)
                        count += 1
        
        self.finished.emit(count)

class UpdateTagsThread(QThread):
    """מעדכן תגיות MP3"""
    file_updated = Signal(str, bool, str)
    progress = Signal(int, int, str)
    finished = Signal(int, int)
    
    def __init__(self, folder):
        super().__init__()
        self.folder = folder
        self.stopped = False
    
    def run(self):
        if not MUTAGEN_AVAILABLE:
            self.finished.emit(0, 0)
            return
        
        success = total = 0
        for root, dirs, files in os.walk(self.folder):
            if self.stopped:
                break
            for name in files:
                if self.stopped or not name.lower().endswith('.mp3'):
                    continue
                
                filepath = os.path.join(root, name)
                total += 1
                self.progress.emit(total, 0, f"מעדכן: {name}")
                
                parts = Path(filepath).parts
                artist = parts[-2] if len(parts) >= 2 else None
                album = parts[-3] if len(parts) >= 3 else None
                title = os.path.splitext(name)[0]
                
                ok, msg = update_mp3_tags(filepath, artist, album, title)
                if ok:
                    success += 1
                self.file_updated.emit(filepath, ok, msg)
        
        self.finished.emit(success, total)

# ============================================================================
# MainWindow - חלון ראשי
# ============================================================================

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("?? TuneMaster Pro - ניהול מוזיקה מתקדם")
        self.setMinimumSize(1200, 850)
        
        self.items = []
        self.duplicate_items = []
        self.clean_items = []
        self.selected_clean_folders = []
        
        self.setup_ui()
        self.setStyleSheet(MODERN_STYLE)
    
    def setup_ui(self):
        root = QWidget(self)
        self.setCentralWidget(root)
        layout = QVBoxLayout(root)
        layout.setSpacing(15)
        layout.setContentsMargins(20, 20, 20, 20)
        
        # כותרת
        title = QLabel("?? TuneMaster Pro")
        title.setObjectName("titleLabel")
        title.setAlignment(Qt.AlignCenter)
        layout.addWidget(title)
        
        subtitle = QLabel("ניהול וארגון חכם של ספריית המוזיקה שלך")
        subtitle.setAlignment(Qt.AlignCenter)
        subtitle.setStyleSheet("color: #A29BFE; font-size: 16px; margin-bottom: 15px;")
        layout.addWidget(subtitle)
        
        # טאבים
        tabs = QTabWidget()
        tabs.addTab(self.create_translate_tab(), "?? תרגום")
        tabs.addTab(self.create_clean_tab(), "?? ניקוי")
        tabs.addTab(self.create_duplicates_tab(), "??? כפולים")
        tabs.addTab(self.create_tags_tab(), "??? תגיות")
        tabs.addTab(self.create_log_tab(), "?? לוג")
        layout.addWidget(tabs)
        
        # סטטוס
        status_layout = QHBoxLayout()
        self.status_label = QLabel("מוכן לפעולה ?")
        self.status_label.setStyleSheet(f"color: {COLORS['success']}; font-weight: bold;")
        status_layout.addWidget(self.status_label)
        status_layout.addStretch()
        
        version_label = QLabel("גרסה 3.0")
        version_label.setStyleSheet("color: #8E8E9E; font-size: 11px;")
        status_layout.addWidget(version_label)
        layout.addLayout(status_layout)
    
    def create_translate_tab(self):
        """טאב תרגום"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        info = QLabel("?? תרגום אוטומטי מעברית לאנגלית עם רווחים")
        info.setObjectName("infoLabel")
        layout.addWidget(info)
        
        btn_layout = QHBoxLayout()
        self.scan_btn = QPushButton("?? בחר תיקייה וסרוק")
        self.scan_btn.clicked.connect(self.scan_folder)
        btn_layout.addWidget(self.scan_btn)
        
        self.select_all_btn = QPushButton("? בחר הכל")
        self.select_all_btn.setEnabled(False)
        self.select_all_btn.clicked.connect(lambda: self.select_all(self.table))
        btn_layout.addWidget(self.select_all_btn)
        
        layout.addLayout(btn_layout)
        
        self.table = QTableWidget()
        self.table.setColumnCount(4)
        self.table.setHorizontalHeaderLabels(["?", "סוג", "שם נוכחי", "שם חדש"])
        self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
        self.table.horizontalHeader().setSectionResizeMode(3, QHeaderView.Stretch)
        layout.addWidget(self.table)
        
        self.progress = QProgressBar()
        self.progress.setVisible(False)
        layout.addWidget(self.progress)
        
        self.rename_btn = QPushButton("?? בצע תרגום")
        self.rename_btn.setEnabled(False)
        self.rename_btn.setObjectName("successBtn")
        self.rename_btn.setMinimumHeight(50)
        self.rename_btn.clicked.connect(self.rename_files)
        layout.addWidget(self.rename_btn)
        
        return tab
    
    def create_clean_tab(self):
        """טאב ניקוי חכם"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        info = QLabel("?? ניקוי חכם + זיהוי דואטים")
        info.setObjectName("infoLabel")
        layout.addWidget(info)
        
        folders_group = QGroupBox("תיקיות זמרים")
        folders_layout = QVBoxLayout()
        
        btn_layout = QHBoxLayout()
        add_btn = QPushButton("? הוסף תיקייה")
        add_btn.setObjectName("successBtn")
        add_btn.clicked.connect(self.add_clean_folder)
        btn_layout.addWidget(add_btn)
        
        remove_btn = QPushButton("? הסר")
        remove_btn.setObjectName("dangerBtn")
        remove_btn.clicked.connect(self.remove_clean_folder)
        btn_layout.addWidget(remove_btn)
        
        folders_layout.addLayout(btn_layout)
        
        self.clean_folders_list = QTextEdit()
        self.clean_folders_list.setMaximumHeight(120)
        self.clean_folders_list.setReadOnly(True)
        folders_layout.addWidget(self.clean_folders_list)
        
        folders_group.setLayout(folders_layout)
        layout.addWidget(folders_group)
        
        scan_btn = QPushButton("?? סרוק")
        scan_btn.setObjectName("warningBtn")
        scan_btn.clicked.connect(self.scan_smart_clean)
        layout.addWidget(scan_btn)
        
        self.clean_table = QTableWidget()
        self.clean_table.setColumnCount(5)
        self.clean_table.setHorizontalHeaderLabels(["?", "זמר", "נוכחי", "חדש", "דואטים"])
        self.clean_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        layout.addWidget(self.clean_table)
        
        self.clean_progress = QProgressBar()
        self.clean_progress.setVisible(False)
        layout.addWidget(self.clean_progress)
        
        apply_btn = QPushButton("? בצע ניקוי")
        apply_btn.setObjectName("successBtn")
        apply_btn.setMinimumHeight(50)
        apply_btn.clicked.connect(self.apply_smart_clean)
        layout.addWidget(apply_btn)
        
        return tab
    
    def create_duplicates_tab(self):
        """טאב כפולים"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        info = QLabel("??? מחיקת כפולים בטוחה")
        info.setObjectName("infoLabel")
        layout.addWidget(info)
        
        btn = QPushButton("?? חפש כפולים")
        btn.setObjectName("dangerBtn")
        btn.clicked.connect(self.find_duplicates)
        layout.addWidget(btn)
        
        self.dup_table = QTableWidget()
        self.dup_table.setColumnCount(5)
        self.dup_table.setHorizontalHeaderLabels(["?", "קובץ 1", "קובץ 2", "גודל", "פעולה"])
        self.dup_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        layout.addWidget(self.dup_table)
        
        self.dup_progress = QProgressBar()
        self.dup_progress.setVisible(False)
        layout.addWidget(self.dup_progress)
        
        del_btn = QPushButton("??? מחק נבחרים")
        del_btn.setObjectName("dangerBtn")
        del_btn.setMinimumHeight(50)
        del_btn.clicked.connect(self.delete_duplicates)
        layout.addWidget(del_btn)
        
        return tab
    
    def create_tags_tab(self):
        """טאב תגיות"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        if not MUTAGEN_AVAILABLE:
            warning = QLabel("?? mutagen לא מותקן!\n\npip install mutagen")
            warning.setObjectName("infoLabel")
            warning.setAlignment(Qt.AlignCenter)
            layout.addWidget(warning)
            return tab
        
        info = QLabel("??? עדכון תגיות MP3 אוטומטי")
        info.setObjectName("infoLabel")
        layout.addWidget(info)
        
        btn = QPushButton("??? עדכן תגיות")
        btn.clicked.connect(self.update_tags)
        layout.addWidget(btn)
        
        self.tags_log = QTextEdit()
        self.tags_log.setReadOnly(True)
        layout.addWidget(self.tags_log)
        
        self.tags_progress = QProgressBar()
        self.tags_progress.setVisible(False)
        layout.addWidget(self.tags_progress)
        
        return tab
    
    def create_log_tab(self):
        """טאב לוג"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        self.log = QTextEdit()
        self.log.setReadOnly(True)
        layout.addWidget(self.log)
        
        btn_layout = QHBoxLayout()
        save_btn = QPushButton("?? שמור לוג")
        save_btn.setObjectName("successBtn")
        save_btn.clicked.connect(self.save_log)
        btn_layout.addWidget(save_btn)
        
        clear_btn = QPushButton("??? נקה")
        clear_btn.setObjectName("dangerBtn")
        clear_btn.clicked.connect(self.log.clear)
        btn_layout.addWidget(clear_btn)
        
        layout.addLayout(btn_layout)
        return tab
    
    def log_message(self, msg):
        """רושם הודעה ללוג"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        self.log.append(f"[{timestamp}] {msg}")
        QApplication.processEvents()
    
    # --- תרגום ---
    
    def scan_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "בחר תיקייה")
        if not folder:
            return
        
        self.items.clear()
        self.table.setRowCount(0)
        self.log_message(f"?? סורק: {folder}")
        
        self.scan_btn.setEnabled(False)
        
        self.scan_thread = ScanThread(folder)
        self.scan_thread.item_found.connect(self.add_translate_item)
        self.scan_thread.finished.connect(self.scan_finished)
        self.scan_thread.start()
    
    def add_translate_item(self, old_path, new_name, item_type):
        row = self.table.rowCount()
        self.table.insertRow(row)
        
        check = QCheckBox()
        check.setChecked(True)
        check_widget = QWidget()
        check_layout = QHBoxLayout(check_widget)
        check_layout.addWidget(check)
        check_layout.setAlignment(Qt.AlignCenter)
        check_layout.setContentsMargins(0, 0, 0, 0)
        self.table.setCellWidget(row, 0, check_widget)
        
        self.table.setItem(row, 1, QTableWidgetItem(item_type))
        self.table.setItem(row, 2, QTableWidgetItem(os.path.basename(old_path)))
        
        new_item = QTableWidgetItem(new_name)
        new_item.setBackground(QColor("#e8f5e9"))
        new_item.setForeground(QColor("#2D3436"))
        self.table.setItem(row, 3, new_item)
        
        self.items.append([old_path, new_name, True, check])
    
    def scan_finished(self, count):
        self.scan_btn.setEnabled(True)
        if count > 0:
            self.rename_btn.setEnabled(True)
            self.select_all_btn.setEnabled(True)
            self.log_message(f"? נמצאו {count} פריטים")
        else:
            self.log_message("?? לא נמצאו קבצים")
    
    def select_all(self, table):
        for i in range(table.rowCount()):
            widget = table.cellWidget(i, 0)
            if widget:
                check = widget.findChild(QCheckBox)
                if check:
                    check.setChecked(True)
    
    def rename_files(self):
        for i, item in enumerate(self.items):
            widget = self.table.cellWidget(i, 0)
            if widget:
                check = widget.findChild(QCheckBox)
                item[2] = check.isChecked() if check else False
        
        selected = sum(1 for item in self.items if item[2])
        if selected == 0:
            QMessageBox.warning(self, "אזהרה", "לא נבחרו פריטים!")
            return
        
        reply = QMessageBox.question(self, "אישור", f"לתרגם {selected} פריטים?",
                                     QMessageBox.Yes | QMessageBox.No)
        if reply != QMessageBox.Yes:
            return
        
        self.log_message("\n?? מתרגם...")
        self.rename_btn.setEnabled(False)
        self.progress.setVisible(True)
        self.progress.setMaximum(selected)
        
        self.rename_thread = RenameThread(self.items)
        self.rename_thread.progress.connect(self.update_progress)
        self.rename_thread.finished.connect(self.rename_finished)
        self.rename_thread.start()
    
    def update_progress(self, current, total, message):
        self.progress.setValue(current)
        self.log_message(message)
    
    def rename_finished(self, success, total, errors):
        self.rename_btn.setEnabled(True)
        self.progress.setVisible(False)
        self.log_message(f"? הושלם! {success}/{total}")
        QMessageBox.information(self, "סיום", f"תורגמו: {success}/{total}")
        self.table.setRowCount(0)
        self.items.clear()
    
    # --- ניקוי חכם ---
    
    def add_clean_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "בחר תיקיית זמר")
        if folder and folder not in self.selected_clean_folders:
            self.selected_clean_folders.append(folder)
            self.update_clean_folders_display()
            self.log_message(f"? נוסף: {folder}")
    
    def remove_clean_folder(self):
        if not self.selected_clean_folders:
            return
        # הסר אחרון
        removed = self.selected_clean_folders.pop()
        self.update_clean_folders_display()
        self.log_message(f"? הוסר: {removed}")
    
    def update_clean_folders_display(self):
        if not self.selected_clean_folders:
            self.clean_folders_list.setPlainText("אין תיקיות")
            return
        text = f"{len(self.selected_clean_folders)} תיקיות:\n\n"
        for i, f in enumerate(self.selected_clean_folders, 1):
            text += f"{i}. {os.path.basename(f)}\n"
        self.clean_folders_list.setPlainText(text)
    
    def scan_smart_clean(self):
        if not self.selected_clean_folders:
            QMessageBox.warning(self, "אזהרה", "הוסף תיקיות!")
            return
        
        self.clean_items.clear()
        self.clean_table.setRowCount(0)
        self.log_message(f"?? סורק {len(self.selected_clean_folders)} תיקיות...")
        
        self.clean_progress.setVisible(True)
        thread = SmartCleanArtistThread(self.selected_clean_folders)
        thread.file_found.connect(self.add_clean_item)
        thread.progress.connect(lambda c, t, m: self.clean_progress.setValue(int(c/t*100) if t > 0 else 0))
        thread.finished.connect(self.clean_scan_finished)
        thread.start()
        self.clean_thread = thread
    
    def add_clean_item(self, filepath, new_name, artist, other):
        row = self.clean_table.rowCount()
        self.clean_table.insertRow(row)
        
        check = QCheckBox()
        check.setChecked(True)
        check_widget = QWidget()
        check_layout = QHBoxLayout(check_widget)
        check_layout.addWidget(check)
        check_layout.setAlignment(Qt.AlignCenter)
        check_layout.setContentsMargins(0, 0, 0, 0)
        self.clean_table.setCellWidget(row, 0, check_widget)
        
        self.clean_table.setItem(row, 1, QTableWidgetItem(artist))
        self.clean_table.setItem(row, 2, QTableWidgetItem(os.path.basename(filepath)))
        
        new_item = QTableWidgetItem(new_name)
        new_item.setBackground(QColor("#e8f5e9"))
        new_item.setForeground(QColor("#2D3436"))
        self.clean_table.setItem(row, 3, new_item)
        
        duets = ", ".join(other) if other else "אין"
        self.clean_table.setItem(row, 4, QTableWidgetItem(duets))
        
        self.clean_items.append([filepath, new_name, True, check])
    
    def clean_scan_finished(self, count):
        self.clean_progress.setVisible(False)
        self.log_message(f"? נמצאו {count} קבצים")
        if count == 0:
            QMessageBox.information(self, "סיום", "לא נמצאו קבצים")
    
    def apply_smart_clean(self):
        for i, item in enumerate(self.clean_items):
            widget = self.clean_table.cellWidget(i, 0)
            if widget:
                check = widget.findChild(QCheckBox)
                item[2] = check.isChecked() if check else False
        
        selected = sum(1 for item in self.clean_items if item[2])
        if selected == 0:
            QMessageBox.warning(self, "אזהרה", "לא נבחרו פריטים!")
            return
        
        reply = QMessageBox.question(self, "אישור", f"לנקות {selected} קבצים?",
                                     QMessageBox.Yes | QMessageBox.No)
        if reply != QMessageBox.Yes:
            return
        
        self.log_message("\n?? מנקה...")
        success = 0
        
        for old_path, new_name, checked, _ in self.clean_items:
            if not checked:
                continue
            root = os.path.dirname(old_path)
            new_path = get_unique_path(os.path.join(root, new_name))
            try:
                os.rename(old_path, new_path)
                success += 1
                self.log_message(f"? {os.path.basename(old_path)}")
            except Exception as e:
                self.log_message(f"? {os.path.basename(old_path)}: {e}")
        
        self.log_message(f"? נוקו {success} קבצים")
        QMessageBox.information(self, "סיום", f"נוקו {success} קבצים")
        self.clean_table.setRowCount(0)
        self.clean_items.clear()
    
    # --- כפולים ---
    
    def find_duplicates(self):
        folder = QFileDialog.getExistingDirectory(self, "בחר תיקייה")
        if not folder:
            return
        
        self.duplicate_items.clear()
        self.dup_table.setRowCount(0)
        self.log_message(f"?? מחפש כפולים: {folder}")
        
        self.dup_progress.setVisible(True)
        thread = FindDuplicatesThread(folder)
        thread.duplicate_found.connect(self.add_duplicate_item)
        thread.progress.connect(lambda c, t, m: self.dup_progress.setValue(int(c/t*100) if t > 0 else 0))
        thread.finished.connect(self.dup_scan_finished)
        thread.start()
        self.dup_thread = thread
    
    def add_duplicate_item(self, file1, file2, size1, size2):
        row = self.dup_table.rowCount()
        self.dup_table.insertRow(row)
        
        check = QCheckBox()
        check.setChecked(True)
        check_widget = QWidget()
        check_layout = QHBoxLayout(check_widget)
        check_layout.addWidget(check)
        check_layout.setAlignment(Qt.AlignCenter)
        check_layout.setContentsMargins(0, 0, 0, 0)
        self.dup_table.setCellWidget(row, 0, check_widget)
        
        self.dup_table.setItem(row, 1, QTableWidgetItem(file1))
        self.dup_table.setItem(row, 2, QTableWidgetItem(file2))
        self.dup_table.setItem(row, 3, QTableWidgetItem(f"{size1/1024/1024:.2f} MB"))
        
        combo = QComboBox()
        combo.addItems(["מחק קובץ 2", "מחק קובץ 1", "שמור שניהם"])
        self.dup_table.setCellWidget(row, 4, combo)
        
        self.duplicate_items.append([file1, file2, True, check, combo])
    
    def dup_scan_finished(self, count):
        self.dup_progress.setVisible(False)
        self.log_message(f"? נמצאו {count} זוגות")
        if count == 0:
            QMessageBox.information(self, "סיום", "לא נמצאו כפולים")
    
    def delete_duplicates(self):
        to_delete = []
        for file1, file2, checked, _, combo in self.duplicate_items:
            if not checked:
                continue
            action = combo.currentText()
            if action == "מחק קובץ 2":
                to_delete.append(file2)
            elif action == "מחק קובץ 1":
                to_delete.append(file1)
        
        if not to_delete:
            QMessageBox.warning(self, "אזהרה", "לא נבחרו קבצים!")
            return
        
        reply = QMessageBox.question(self, "אישור מחיקה",
                                     f"למחוק {len(to_delete)} קבצים?\n\n?? לא ניתן לשחזר!",
                                     QMessageBox.Yes | QMessageBox.No)
        if reply != QMessageBox.Yes:
            return
        
        self.log_message("\n??? מוחק...")
        success = 0
        for filepath in to_delete:
            try:
                os.remove(filepath)
                success += 1
                self.log_message(f"? נמחק: {filepath}")
            except Exception as e:
                self.log_message(f"? {filepath}: {e}")
        
        self.log_message(f"? נמחקו {success} קבצים")
        QMessageBox.information(self, "סיום", f"נמחקו {success} קבצים")
        self.dup_table.setRowCount(0)
        self.duplicate_items.clear()
    
    # --- תגיות ---
    
    def update_tags(self):
        if not MUTAGEN_AVAILABLE:
            QMessageBox.critical(self, "שגיאה", "mutagen לא מותקן!")
            return
        
        folder = QFileDialog.getExistingDirectory(self, "בחר תיקייה")
        if not folder:
            return
        
        self.tags_log.clear()
        self.tags_log.append(f"??? מעדכן: {folder}\n")
        self.tags_progress.setVisible(True)
        
        thread = UpdateTagsThread(folder)
        thread.file_updated.connect(self.tag_updated)
        thread.progress.connect(lambda c, t, m: self.tags_progress.setValue(c if t == 0 else int(c/t*100)))
        thread.finished.connect(self.tags_finished)
        thread.start()
        self.tags_thread = thread
    
    def tag_updated(self, filepath, success, message):
        status = "?" if success else "?"
        self.tags_log.append(f"{status} {os.path.basename(filepath)}")
    
    def tags_finished(self, success, total):
        self.tags_progress.setVisible(False)
        self.tags_log.append(f"\n? {success}/{total}")
        self.log_message(f"??? עודכנו {success}/{total}")
        QMessageBox.information(self, "סיום", f"עודכנו {success}/{total}")
    
    # --- לוג ---
    
    def save_log(self):
        filename, _ = QFileDialog.getSaveFileName(
            self, "שמור לוג",
            f"tunemaster_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt",
            "Text Files (*.txt)"
        )
        if filename:
            try:
                with open(filename, 'w', encoding='utf-8') as f:
                    f.write(self.log.toPlainText())
                self.log_message(f"?? נשמר: {filename}")
                QMessageBox.information(self, "שמירה", "הלוג נשמר!")
            except Exception as e:
                QMessageBox.critical(self, "שגיאה", f"שגיאה: {e}")

# ============================================================================
# Main - נקודת הכניסה
# ============================================================================

if __name__ == "__main__":
    app = QApplication(sys.argv)
    
    # אייקון
    icon = set_app_icon()
    if icon:
        app.setWindowIcon(icon)
    
    # פונט
    font = QFont("Segoe UI", 10)
    app.setFont(font)
    
    # חלון ראשי
    win = MainWindow()
    if icon:
        win.setWindowIcon(icon)
    
    win.show()
    sys.exit(app.exec())