Составь для этого кода полный и необходимый requirements.txt (не файл а все необходимое содержание! ВАЖНО НИЧЕГО НЕ УПУСТИ И перепровь

import os
import sys
import logging
import threading
import subprocess
import json
from urllib.parse import urlparse

# Попытка импортировать необходимые библиотеки
try:
    import torchaudio
    from demucs.pretrained import get_model as demucs_get_model
    from demucs.apply import apply_model as demucs_apply_model
    DEMUCS_AVAILABLE = True
except ImportError:
    DEMUCS_AVAILABLE = False

try:
    import noisereduce as nr
    NOISEREDUCE_AVAILABLE = True
except ImportError:
    NOISEREDUCE_AVAILABLE = False

from pydub import AudioSegment, effects
import numpy as np  # Необходим для noisereduce

# Импорт библиотек для GUI с использованием ttkbootstrap
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from tkinter import filedialog, messagebox
from tkinterdnd2 import DND_FILES, TkinterDnD

# Настройка логирования
LOG_FILE = "audio_extraction.log"
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

# Обработчики для логирования в файл и консоль
file_handler = logging.FileHandler(LOG_FILE)
file_handler.setLevel(logging.DEBUG)
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.INFO)
logger.addHandler(file_handler)
logger.addHandler(console_handler)

# Конфигурация
CONFIG_FILE = "config.json"

DEFAULT_CONFIG = {
    "output_path": "audio",
    "audio_format": "flac",  # Формат по умолчанию
    "use_demucs": False,
    "use_noise_reduction": False,
    "bitrate": "320k",  # Битрейт по умолчанию
    "volume_level": -14.0,  # Целевой уровень громкости
    "normalize_audio": True,  # Нормализация аудио
}

# Загрузка конфигурации
def load_config():
    if os.path.exists(CONFIG_FILE):
        try:
            with open(CONFIG_FILE, "r", encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            logger.error(f"Ошибка загрузки конфигурации: {e}")
    return DEFAULT_CONFIG.copy()

# Сохранение конфигурации
def save_config(config):
    try:
        with open(CONFIG_FILE, "w", encoding='utf-8') as f:
            json.dump(config, f, indent=4, ensure_ascii=False)
    except Exception as e:
        logger.error(f"Ошибка сохранения конфигурации: {e}")

# Заглушка для функции проверки обновлений
def check_for_updates(current_version):
    """Проверяет наличие обновлений."""
    pass  # Реализуйте логику проверки обновлений при необходимости

# Проверка валидности URL
def is_valid_url(url):
    """Проверяет, является ли строка валидным URL."""
    try:
        result = urlparse(url)
        return all([result.scheme, result.netloc])
    except ValueError:
        return False

def enhance_audio(audio_path, config):
    """Улучшает аудио без использования Demucs."""
    try:
        logger.info(f"Улучшение аудио: {audio_path}")
        audio = AudioSegment.from_file(audio_path)

        # Шумоподавление
        if config['use_noise_reduction'] and NOISEREDUCE_AVAILABLE:
            logger.info("Применение шумоподавления...")
            samples = np.array(audio.get_array_of_samples()).astype(np.float32)
            sample_rate = audio.frame_rate

            reduced_noise = nr.reduce_noise(
                y=samples,
                sr=sample_rate,
                prop_decrease=1.0  # Отрегулируйте интенсивность при необходимости
            )
            audio = AudioSegment(
                reduced_noise.astype(np.int16).tobytes(),
                frame_rate=audio.frame_rate,
                sample_width=2,
                channels=audio.channels
            )
        elif config['use_noise_reduction']:
            logger.warning("Библиотека для шумоподавления недоступна. Установите 'noisereduce'.")

        # Нормализация
        if config['normalize_audio']:
            audio = effects.normalize(audio)

        # Логирование текущего уровня громкости
        logger.info(f"Текущий уровень громкости: {audio.dBFS:.2f} dBFS")

        # Регулировка громкости
        change_in_dBFS = config['volume_level'] - audio.dBFS
        logger.info(f"Применение изменения громкости: {change_in_dBFS:.2f} dB")
        audio = audio.apply_gain(change_in_dBFS)

        # Логирование нового уровня громкости
        logger.info(f"Новый уровень громкости: {audio.dBFS:.2f} dBFS")

        # Сохранение аудио
        audio.export(audio_path, format=audio_path.split('.')[-1])
    except Exception as e:
        logger.error(f"Ошибка улучшения аудио {audio_path}: {e}")

def enhance_audio_with_demucs(audio_path):
    """Использует модель Demucs для улучшения качества аудио."""
    if not DEMUCS_AVAILABLE:
        logger.error("Demucs не установлен или не может быть импортирован.")
        return
    try:
        logger.info(f"Улучшение аудио с помощью Demucs: {audio_path}")
        model = demucs_get_model('demucs_quantized')
        waveform, sr = torchaudio.load(audio_path)
        sources = demucs_apply_model(model, waveform, device='cpu', split=True, overlap=0.25)
        # Объединение всех источников
        enhanced_waveform = sum(sources)
        torchaudio.save(audio_path, enhanced_waveform, sr)
    except Exception as e:
        logger.error(f"Ошибка улучшения аудио с помощью Demucs для {audio_path}: {e}")

def process_audio(audio_path, config):
    """Обрабатывает аудио файл для улучшения его качества."""
    if config['use_demucs']:
        if DEMUCS_AVAILABLE:
            enhance_audio_with_demucs(audio_path)
        else:
            logger.warning("Demucs недоступен. Будет использовано стандартное улучшение.")
            enhance_audio(audio_path, config)
    else:
        enhance_audio(audio_path, config)

def extract_audio_from_local_file(file_path, output_path, config):
    """Извлекает аудио из локального видео файла."""
    if not os.path.exists(output_path):
        os.makedirs(output_path)

    try:
        logger.info(f"Извлечение аудио из локального файла: {file_path}")
        base_name = os.path.splitext(os.path.basename(file_path))[0]
        audio_path = os.path.join(output_path, f'{base_name}.{config["audio_format"]}')
        logger.info("Конвертация и улучшение аудио с помощью FFmpeg...")

        command = [
            'ffmpeg', '-y',
            '-i', file_path,
            '-vn',  # Отключение обработки видео
            '-ar', '44100',
            '-b:a', config["bitrate"],
            audio_path
        ]
        subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)

        process_audio(audio_path, config)
        logger.info(f'Аудио сохранено: {audio_path}')
        return audio_path
    except subprocess.CalledProcessError as e:
        logger.error(f"Ошибка обработки локального файла: {e}")
        return None

def download_audio_with_ytdlp(url, output_path, config):
    """Скачивает аудио из видео используя yt-dlp, поддерживает многие платформы."""
    if not os.path.exists(output_path):
        os.makedirs(output_path)

    try:
        logger.info(f"Загрузка аудио: {url}")
        output_template = os.path.join(output_path, '%(title)s.%(ext)s')
        command = [
            'yt-dlp', '-x', '--audio-format', config["audio_format"],
            '--audio-quality', '0',  # Лучшее качество
            '--output', output_template,
            url
        ]
        subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
        # Поиск скачанного файла(ов)
        downloaded_files = [f for f in os.listdir(output_path) if f.endswith(config["audio_format"])]
        for file in downloaded_files:
            audio_path = os.path.join(output_path, file)
            process_audio(audio_path, config)
        logger.info('Аудио успешно загружено и извлечено.')
        return True
    except subprocess.CalledProcessError as e:
        logger.error(f"Ошибка при загрузке аудио: {e}")
        return None

def process_video(urls, output_path, config, progress_callback=None):
    results = []
    total = len(urls)
    for idx, url in enumerate(urls):
        url = url.strip()
        if not url:
            continue
        try:
            if os.path.isfile(url):
                result = extract_audio_from_local_file(url, output_path, config)
                results.append(result)
            elif is_valid_url(url):
                result = download_audio_with_ytdlp(url, output_path, config)
                results.append(result)
            else:
                logger.warning(f"Недействительный путь или URL: {url}")
                results.append(None)
        except Exception as e:
            logger.error(f"Ошибка при обработке {url}: {e}")
            results.append(None)
        if progress_callback:
            progress_callback(idx + 1, total)
    return results

def show_summary(results):
    successful_count = sum(1 for result in results if result is not None)
    messagebox.showinfo("Завершено", f"Обработка завершена!\nУспешно обработано: {successful_count}/{len(results)}")

def create_tooltip(widget, text):
    """Создает всплывающую подсказку для виджета."""
    tooltip = ttk.Balloon(widget)
    tooltip.subwidget("label").config(wraplength=300)
    tooltip.bind_widget(widget, balloonmsg=text)

def drag_and_drop(event, file_listbox):
    files = event.data
    files = files.strip("{}")  # Убираем фигурные скобки, если есть
    files_list = files.split()
    for file in files_list:
        file_listbox.insert(END, file)

def gui_main():
    """Графический интерфейс пользователя."""
    config = load_config()

    # Опции для форматов и битрейтов
    AUDIO_FORMAT_OPTIONS = {
        'MP3 (Стандартный формат сжатия)': 'mp3',
        'WAV (Несжатый формат, лучшее качество)': 'wav',
        'FLAC (Сжатый без потерь, для архивирования)': 'flac',
        'AAC (Современный формат с высоким качеством)': 'aac'
    }

    BITRATE_OPTIONS = {
        '128k (Стандартное качество)': '128k',
        '192k (Выше среднего качества)': '192k',
        '256k (Высокое качество)': '256k',
        '320k (Максимальное качество)': '320k'
    }

    # Инициализация основного окна с использованием ttkbootstrap
    app = ttk.Window(title="Извлечение Аудио", themename="cosmo", size=(600, 600))
    # app.iconphoto(False, ttk.PhotoImage(file='icon.png'))  # Закомментируйте или измените, если нет иконки

    notebook = ttk.Notebook(app)
    notebook.pack(fill=BOTH, expand=TRUE, padx=10, pady=10)

    # Главная вкладка
    frame_main = ttk.Frame(notebook)
    notebook.add(frame_main, text="Главная")

    # Настройки вкладка
    frame_settings = ttk.Frame(notebook)
    notebook.add(frame_settings, text="Настройки")

    # ------------------- Главная вкладка ------------------- #

    # Ссылки на видео
    label_urls = ttk.Label(frame_main, text="Ссылки на видео (через запятую):", font=('Helvetica', 10, 'bold'))
    label_urls.pack(fill=X, pady=(10, 0))

    entry_urls = ttk.Entry(frame_main)
    entry_urls.pack(fill=X, pady=5)
    create_tooltip(entry_urls, "Введите ссылки с любой поддерживаемого платформы (например, YouTube, Vimeo, Dailymotion и т.д.)")

    # Drag and Drop файлов
    label_drag_drop = ttk.Label(frame_main, text="Перетащите видео файлы сюда:", font=('Helvetica', 10, 'bold'))
    label_drag_drop.pack(fill=X, pady=(10, 0))

    file_listbox = tk.Listbox(frame_main, height=5)
    file_listbox.pack(fill=X, pady=5)
    file_listbox.drop_target_register(DND_FILES)
    file_listbox.dnd_bind('<<Drop>>', lambda event: drag_and_drop(event, file_listbox))

    scrollbar = ttk.Scrollbar(frame_main, orient=VERTICAL, command=file_listbox.yview)
    file_listbox.config(yscrollcommand=scrollbar.set)
    scrollbar.pack(side=RIGHT, fill=Y)

    # Кнопка очистки списка файлов
    button_clear_files = ttk.Button(frame_main, text="Очистить список файлов", command=lambda: file_listbox.delete(0, END), bootstyle='secondary')
    button_clear_files.pack(pady=5)

    # Полоса прогресса
    progress_var = tk.DoubleVar()
    progress_bar = ttk.Progressbar(frame_main, variable=progress_var, maximum=100, bootstyle='info-striped')
    progress_bar.pack(fill=X, pady=10)

    # Кнопка "Начать"
    button_start = ttk.Button(frame_main, text="Начать", bootstyle='success', command=lambda: start_processing(
        entry_urls, file_listbox, config, AUDIO_FORMAT_OPTIONS, BITRATE_OPTIONS,
        format_var, bitrate_var, demucs_var, noise_reduction_var, volume_var, normalize_var, app, progress_var
    ))
    button_start.pack(pady=10)

    # ------------------- Настройки вкладка ------------------- #

    settings_frame = ttk.Frame(frame_settings, padding=10)
    settings_frame.pack(fill=BOTH, expand=TRUE)

    # Формат аудио
    label_format = ttk.Label(settings_frame, text="Формат аудио:", font=('Helvetica', 10, 'bold'))
    label_format.grid(row=0, column=0, sticky='w', pady=5)

    # Установка формата аудио по умолчанию
    for key, value in AUDIO_FORMAT_OPTIONS.items():
        if value == config['audio_format']:
            format_default_option = key
            break
    else:
        format_default_option = 'MP3 (Стандартный формат сжатия)'

    format_var = tk.StringVar(value=format_default_option)
    entry_format = ttk.Combobox(settings_frame, textvariable=format_var, values=list(AUDIO_FORMAT_OPTIONS.keys()), state='readonly')
    entry_format.grid(row=1, column=0, sticky='we', pady=5)
    create_tooltip(entry_format, "Выберите формат аудио файла.")

    # Битрейт
    label_bitrate = ttk.Label(settings_frame, text="Битрейт:", font=('Helvetica', 10, 'bold'))
    label_bitrate.grid(row=2, column=0, sticky='w', pady=5)

    # Установка битрейта по умолчанию
    for key, value in BITRATE_OPTIONS.items():
        if value == config['bitrate']:
            bitrate_default_option = key
            break
    else:
        bitrate_default_option = '320k (Максимальное качество)'

    bitrate_var = tk.StringVar(value=bitrate_default_option)
    entry_bitrate = ttk.Combobox(settings_frame, textvariable=bitrate_var, values=list(BITRATE_OPTIONS.keys()), state='readonly')
    entry_bitrate.grid(row=3, column=0, sticky='we', pady=5)
    create_tooltip(entry_bitrate, "Выберите битрейт аудио. Более высокий битрейт означает лучшее качество, но больший размер файла.")

    # Целевой уровень громкости
    label_volume = ttk.Label(settings_frame, text="Целевой уровень громкости (дБ):", font=('Helvetica', 10, 'bold'))
    label_volume.grid(row=4, column=0, sticky='w', pady=5)

    volume_var = tk.DoubleVar(value=config.get('volume_level', -14.0))

    def update_volume_label(value):
        volume_value_label.config(text=f"{float(value):.1f} дБ")

    entry_volume = ttk.Scale(settings_frame, from_=-60.0, to=0.0, variable=volume_var, orient=HORIZONTAL, command=update_volume_label, bootstyle='info')
    entry_volume.grid(row=5, column=0, sticky='we', pady=5)

    volume_value_label = ttk.Label(settings_frame, text=f"{volume_var.get():.1f} дБ")
    volume_value_label.grid(row=5, column=1, padx=5)
    create_tooltip(entry_volume, """Установите целевой уровень громкости для нормализации аудио.
Рекомендуемые уровни:
-14 дБ: Стандарт для стриминговых сервисов.
-16 дБ: Стандарт для подкастов.
-23 дБ: Стандарт для телетрансляций.
""")

    # Нормализация аудио
    normalize_var = tk.BooleanVar(value=config.get('normalize_audio', True))
    checkbox_normalize = ttk.Checkbutton(settings_frame, text="Нормализовать аудио", variable=normalize_var, bootstyle='success-round-toggle')
    checkbox_normalize.grid(row=6, column=0, sticky='w', pady=5)

    # Шумоподавление
    noise_reduction_var = tk.BooleanVar(value=config.get('use_noise_reduction', False))
    checkbox_noise_reduction = ttk.Checkbutton(settings_frame, text="Применить шумоподавление", variable=noise_reduction_var, bootstyle='success-round-toggle')
    checkbox_noise_reduction.grid(row=7, column=0, sticky='w', pady=5)

    if not NOISEREDUCE_AVAILABLE:
        checkbox_noise_reduction.config(state='disabled')
        label_noisereduce_unavailable = ttk.Label(settings_frame, text="Библиотека 'noisereduce' недоступна.", foreground="red")
        label_noisereduce_unavailable.grid(row=8, column=0, sticky='w', pady=5)

    # Использовать Demucs
    demucs_var = tk.BooleanVar(value=config.get('use_demucs', False))
    checkbox_demucs = ttk.Checkbutton(settings_frame, text="Использовать Demucs для улучшения", variable=demucs_var, bootstyle='success-round-toggle')
    checkbox_demucs.grid(row=9, column=0, sticky='w', pady=5)

    if not DEMUCS_AVAILABLE:
        checkbox_demucs.config(state='disabled')
        label_demucs_unavailable = ttk.Label(settings_frame, text="Demucs недоступен.", foreground="red")
        label_demucs_unavailable.grid(row=10, column=0, sticky='w', pady=5)

    # Ограничение размеров колонок
    settings_frame.columnconfigure(0, weight=1)
    settings_frame.columnconfigure(1, weight=0)

    # Горячие клавиши
    app.bind('<Return>', lambda event: start_processing(
        entry_urls, file_listbox, config, AUDIO_FORMAT_OPTIONS, BITRATE_OPTIONS,
        format_var, bitrate_var, demucs_var, noise_reduction_var, volume_var, normalize_var, app, progress_var
    ))

    app.mainloop()

def start_processing(entry_urls, file_listbox, config, AUDIO_FORMAT_OPTIONS, BITRATE_OPTIONS,
                     format_var, bitrate_var, demucs_var, noise_reduction_var, volume_var, normalize_var, root, progress_var):
    """Запускает обработку в отдельном потоке."""
    # Выбор директории для сохранения в основном потоке
    output_dir = filedialog.askdirectory(title="Выберите папку для сохранения") or config['output_path']

    if not output_dir:
        messagebox.showwarning("Предупреждение", "Пожалуйста, выберите папку для сохранения.")
        return

    threading.Thread(target=lambda: processing_task(
        entry_urls, file_listbox, config, AUDIO_FORMAT_OPTIONS, BITRATE_OPTIONS,
        format_var, bitrate_var, demucs_var, noise_reduction_var, volume_var, normalize_var, root, progress_var, output_dir
    )).start()

def processing_task(entry_urls, file_listbox, config, AUDIO_FORMAT_OPTIONS, BITRATE_OPTIONS,
                    format_var, bitrate_var, demucs_var, noise_reduction_var, volume_var, normalize_var, root, progress_var, output_dir):
    try:
        urls = list(filter(None, [u.strip() for u in entry_urls.get().split(',')]))
        file_paths = file_listbox.get(0, tk.END)
        urls.extend(file_paths)

        if not urls:
            root.after(0, messagebox.showwarning, "Предупреждение", "Пожалуйста, введите ссылки или добавьте файлы для обработки.")
            return

        audio_format = AUDIO_FORMAT_OPTIONS.get(format_var.get(), 'mp3')
        use_demucs = demucs_var.get()
        use_noise_reduction = noise_reduction_var.get()
        bitrate = BITRATE_OPTIONS.get(bitrate_var.get(), '320k')
        target_level = volume_var.get()
        normalize_audio = normalize_var.get()

        # Обновление конфигурации
        config['output_path'] = output_dir
        config['audio_format'] = audio_format
        config['use_demucs'] = use_demucs
        config['use_noise_reduction'] = use_noise_reduction
        config['bitrate'] = bitrate
        config['volume_level'] = target_level
        config['normalize_audio'] = normalize_audio
        save_config(config)

        def progress_callback(current, total):
            progress = (current / total) * 100
            root.after(0, progress_var.set, progress)

        results = process_video(urls, output_dir, config, progress_callback)
        root.after(0, show_summary, results)
    except Exception as e:
        logger.error(f"Произошла ошибка: {e}")
        root.after(0, messagebox.showerror, "Ошибка", f"Произошла ошибка во время обработки: {e}")

if name == 'main':
    check_for_updates(current_version="1.0.0")
    gui_main()