kannst du was verbessern?

import os
import json
import logging
import asyncio
import aiohttp
from datetime import datetime
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QPushButton, QVBoxLayout, QLineEdit, QFileDialog, 
    QLabel, QWidget, QListWidget, QProgressBar, QMessageBox, QCheckBox, QListView, QListWidgetItem
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
from PyQt5.QtGui import QIcon, QPixmap
from moviepy.editor import VideoFileClip
from pytube import YouTube
import librosa
import numpy as np
from aubio import tempo, source
from pydub import AudioSegment
from mutagen.id3 import ID3, TIT2, TPE1
import webbrowser
import configparser
from PIL import Image

class AsyncWorkerThread(QThread):
    recognition_completed = pyqtSignal(str, str, int)
    progress_update = pyqtSignal(int, int, str)

    def __init__(self, track_path, segment_index, use_external_db, musicbrainz_key='', acousticbrainz_key=''):
        super().__init__()
        self.track_path = track_path
        self.segment_index = segment_index
        self.use_external_db = use_external_db
        self.musicbrainz_key = musicbrainz_key
        self.acousticbrainz_key = acousticbrainz_key

    async def query_musicbrainz(self, session, title, artist):
        self.progress_update.emit(self.segment_index, 0, "Querying MusicBrainz...")
        url = f"https://musicbrainz.org/ws/2/recording/?query={title}&fmt=json"
        headers = {"User-Agent": "YourAppName/0.1 youremail@example.com"}
        
        async with session.get(url, headers=headers) as response:
            if response.status == 200:
                data = await response.json()
                if 'recordings' in data and len(data['recordings']) > 0:
                    title_info = data['recordings'][0]
                    title = title_info.get('title', title)
                    artist_info = title_info.get('artist-credit', [{}])[0]
                    artist = artist_info.get('name', artist)
        self.progress_update.emit(self.segment_index, 100, "MusicBrainz query complete.")
        return title, artist

    async def run(self):
        start_time = datetime.now()
        title = f"MockTrack {self.segment_index}"
        artist = f"MockArtist {self.segment_index}"

        if self.use_external_db:
            async with aiohttp.ClientSession() as session:
                title, artist = await self.query_musicbrainz(session, title, artist)
        
        elapsed_time = datetime.now() - start_time
        logging.info(f"Segment {self.segment_index} processing time: {elapsed_time}")
        self.recognition_completed.emit(title, artist, self.segment_index)

class MusicIdentifierApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Music Identifier")
        self.setGeometry(100, 100, 1200, 800)
        self.setWindowIcon(QIcon("icon.png"))

        logging.basicConfig(level=logging.DEBUG, filename='app.log', filemode='a',
                            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

        self.config = configparser.ConfigParser()
        self.load_settings()

        # UI Components
        self.url_input = QLineEdit(self)
        self.url_input.setPlaceholderText("Enter URL or select a local file")
        self.url_input.setToolTip("Enter a valid URL for an online video/audio or choose a local file.")

        self.select_file_btn = QPushButton("Select File")
        self.clear_frames_btn = QPushButton("Clear All Frames")
        self.clear_frames_btn.clicked.connect(self.clear_frames)

        self.save_frames_btn = QPushButton("Save Frames")
        self.save_frames_btn.clicked.connect(self.save_selected_frames)

        self.progress_bar = QProgressBar()
        self.current_action_label = QLabel("Ready")
        self.elapsed_time_label = QLabel("Elapsed time: 0s")
        self.remaining_time_label = QLabel("Estimated remaining time: calculating...")
        self.segment_count_label = QLabel("Segments processed: 0")

        self.frame_list = QListWidget()
        self.frame_list.setSelectionMode(QListView.MultiSelection)

        self.use_external_db = QCheckBox("Use External Databases (MusicBrainz, AcousticBrainz)")
        self.musicbrainz_key_input = QLineEdit()
        self.musicbrainz_key_input.setPlaceholderText("MusicBrainz API Key")
        self.acousticbrainz_key_input = QLineEdit()
        self.acousticbrainz_key_input.setPlaceholderText("AcousticBrainz API Key")

        self.musicbrainz_key_input.setText(self.config.get('API_KEYS', 'MusicBrainz', fallback=''))
        self.acousticbrainz_key_input.setText(self.config.get('API_KEYS', 'AcousticBrainz', fallback=''))

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_elapsed_time)
        self.start_time = None

        layout = QVBoxLayout()
        layout.addWidget(QLabel("Enter a URL or select a local file:"))
        layout.addWidget(self.url_input)
        layout.addWidget(self.select_file_btn)
        layout.addWidget(self.current_action_label)
        layout.addWidget(self.progress_bar)
        layout.addWidget(self.elapsed_time_label)
        layout.addWidget(self.remaining_time_label)
        layout.addWidget(self.segment_count_label)
        layout.addWidget(QLabel("Extracted Frames:"))
        layout.addWidget(self.frame_list)
        layout.addWidget(self.save_frames_btn)
        layout.addWidget(self.clear_frames_btn)       
        layout.addWidget(self.use_external_db)
        layout.addWidget(self.musicbrainz_key_input)
        layout.addWidget(self.acousticbrainz_key_input)
        layout.addWidget(QPushButton("Get MusicBrainz Key", clicked=lambda: webbrowser.open("https://musicbrainz.org/doc/Development/XML_Web_Service/Version_2#Authentication")))
        layout.addWidget(QPushButton("Get AcousticBrainz Key", clicked=lambda: webbrowser.open("https://acousticbrainz.org/login")))

        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)

        self.select_file_btn.clicked.connect(self.open_file_dialog)
        self.url_input.returnPressed.connect(self.handle_url_input)

        self.extracted_frames = []

    def load_settings(self):
        self.config.read('settings.ini')
        if 'API_KEYS' not in self.config:
            self.config['API_KEYS'] = {}

    def save_settings(self):
        self.config.set('API_KEYS', 'MusicBrainz', self.musicbrainz_key_input.text())
        self.config.set('API_KEYS', 'AcousticBrainz', self.acousticbrainz_key_input.text())
        with open('settings.ini', 'w') as configfile:
            self.config.write(configfile)

    def closeEvent(self, event):
        self.save_settings()
        self.clear_frames()  # Clean up any remaining frames
        event.accept()

    def open_file_dialog(self):
        file_dialog = QFileDialog(self)
        file_dialog.setNameFilter("Audio/Video Files (*.wav *.mp3 *.mp4 *.avi *.mkv)")
        if file_dialog.exec_():
            file_path = file_dialog.selectedFiles()[0]
            if os.path.exists(file_path):
                self.url_input.setText(file_path)
                logging.info(f"Selected file: {file_path}")
                self.extract_audio(file_path)
            else:
                logging.error("File path does not exist.")
                QMessageBox.warning(self, "Invalid File", "Selected file does not exist.")

    def handle_url_input(self):
        url = self.url_input.text().strip()
        if url.startswith("http"):
            self.download_and_extract_audio(url)
        else:
            logging.warning("Invalid URL input.")
            QMessageBox.warning(self, "Invalid Input", "Please enter a valid URL or select a local file.")
    
    def download_and_extract_audio(self, url):
        self.update_progress(0, "Downloading audio...")
        try:
            video = YouTube(url)
            stream = video.streams.filter(only_audio=True).first()
            if not stream:
                self.update_progress(100, "No suitable audio stream found.")
                QMessageBox.warning(self, "Download Error", "No suitable audio stream found.")
                return
            filepath = stream.download()
            self.extract_audio(filepath)
        except Exception as e:
            logging.error(f"Download error: {str(e)}")
            self.update_progress(100, f"Download error: {str(e)}")
            QMessageBox.warning(self, "Download Error", f"Error downloading video: {str(e)}")

    def extract_audio(self, video_path):
        self.update_progress(10, "Extracting audio...")
        try:
            with VideoFileClip(video_path) as video:
                audio = video.audio
                temp_audio_path = os.path.join(os.path.dirname(video_path), "temp_audio.wav")
                audio.write_audiofile(temp_audio_path, codec='pcm_s16le')
                self.identify_music_tracks(temp_audio_path)
                os.remove(temp_audio_path)
        except Exception as e:
            logging.error(f"Audio extraction error: {str(e)}")
            self.update_progress(100, f"Extraction error: {str(e)}")
            QMessageBox.warning(self, "Extraction Error", f"Failed to extract audio: {str(e)}")

    def identify_music_tracks(self, audio_path):
        self.update_progress(20, "Identifying music tracks...")
        self.start_time = datetime.now()
        self.timer.start(1000)
        try:
            hop_size = 512
            samplerate = 44100

            s = source(audio_path, samplerate, hop_size)
            o = tempo("specdiff", samplerate, hop_size)
            beats = []

            while True:
                samples, read = s()
                if o(samples):  # Checks for beats
                    beats.append(o.get_last_s())
                if read < hop_size:
                    break

            logging.info(f"Detected beats: {beats}")
            
            y, sr = librosa.load(audio_path, sr=None)
            segments = librosa.effects.split(y, top_db=20)

            self.segment_count_label.setText(f"Segments found: {len(segments)}")

            for i, (start, end) in enumerate(segments):
                track = y[start:end]
                track_path = f"temp_segment_{i}.wav"
                librosa.output.write_wav(track_path, track, sr)
                
                self.extracted_frames.append(track_path)

                frame_preview = QPixmap(100, 100)  # Placeholder size 100x100
                frame_preview.fill(Qt.gray)
                item = QListWidgetItem()
                item.setIcon(QIcon(frame_preview))
                item.setText(f"Segment {i}")
                item.setCheckState(Qt.Unchecked)
                self.frame_list.addItem(item)

                worker = AsyncWorkerThread(
                    track_path, i, 
                    self.use_external_db.isChecked(),
                    self.musicbrainz_key_input.text(),
                    self.acousticbrainz_key_input.text()
                )
                worker.progress_update.connect(self.update_progress)
                loop = asyncio.new_event_loop()
                asyncio.set_event_loop(loop)
                loop.run_in_executor(None, asyncio.run, worker.run())
                worker.recognition_completed.connect(self.handle_track_identification)
                
        except Exception as e:
            logging.error(f"Error during music track identification: {str(e)}")
            self.update_progress(100, f"Identification error: {str(e)}")
            QMessageBox.warning(self, "Identification Error", f"Failed to identify tracks: {str(e)}")

    def handle_track_identification(self, title, artist, index):
        self.music_list.addItem(f"Segment {index}: {title} by {artist}")
        self.save_track_as_mp3(title, artist, index)
        self.show_notification(f"Segment {index} completed", f"{title} by {artist}")
        logging.info(f"Completed track identification for segment {index}")

    def save_track_as_mp3(self, title, artist, index):
        self.update_progress(90, f"Saving segment {index} as MP3...")
        try:
            track_path = f"temp_segment_{index}.wav"
            audio = AudioSegment.from_wav(track_path)
            mp3_path = f"{title} - {artist}.mp3"
            audio.export(mp3_path, format="mp3")

            mp3file = MP3(mp3_path, ID3=ID3)
            try:
                mp3file.add_tags()
            except:
                logging.debug("ID3 tags already exist.")
            mp3file.tags.add(TIT2(encoding=3, text=title))
            mp3file.tags.add(TPE1(encoding=3, text=artist))
            mp3file.save()
            os.remove(track_path)
        except Exception as e:
            logging.error(f"Save error: {str(e)}")
            self.update_progress(100, f"Save error: {str(e)}")
            QMessageBox.warning(self, "Save Error", f"Failed to save track: {str(e)}")

    def show_notification(self, title, message):
        tray = QSystemTrayIcon(QIcon("icon.png"), self)
        tray.showMessage(title, message, QSystemTrayIcon.Information, 3000)

    def update_progress(self, progress, message):
        self.progress_bar.setValue(progress)
        self.current_action_label.setText(message)

    def update_elapsed_time(self):
        elapsed_time = (datetime.now() - self.start_time).seconds
        self.elapsed_time_label.setText(f"Elapsed time: {elapsed_time}s")
        
        segments_processed = self.music_list.count()
        if self.segment_count_label.text() != "Segments processed: 0":
            total_segments = int(self.segment_count_label.text().split(": ")[1])
            if segments_processed > 0:
                avg_time_per_segment = elapsed_time / segments_processed
                remaining_segments = total_segments - segments_processed
                est_remaining_time = int(avg_time_per_segment * remaining_segments)
                self.remaining_time_label.setText(f"Estimated remaining time: {est_remaining_time}s")
            else:
                self.remaining_time_label.setText("Estimated remaining time: calculating...")

    def save_selected_frames(self):
        directory = QFileDialog.getExistingDirectory(self, "Select Directory")
        if not directory:
            return

        for i, frame_path in enumerate(self.extracted_frames):
            item = self.frame_list.item(i)
            if item.checkState() == Qt.Unchecked:
                target_path = os.path.join(directory, os.path.basename(frame_path))
                os.rename(frame_path, target_path)
                logging.info(f"Saved frame: {target_path}")
        
        self.clear_frames()

    def clear_frames(self):
        confirm = QMessageBox.question(self, "Confirm Clear", 
                                       "Are you sure you want to clear all frames? This action cannot be undone.", 
                                       QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if confirm == QMessageBox.Yes:
            for frame_path in self.extracted_frames:
                try:
                    os.remove(frame_path)
                    logging.info(f"Removed temp frame: {frame_path}")
                except FileNotFoundError:
                    logging.warning(f"Temp frame not found: {frame_path}")
            self.extracted_frames.clear()
            self.frame_list.clear()
            self.update_progress(0, "Ready")
            logging.info("Cleared all frames")

if __name__ == "__main__":
    app = QApplication([])
    app.setStyle("Fusion")
    window = MusicIdentifierApp()
    
    window.show()
    app.exec_()