make this app more stable: # – coding: utf-8 –
# YuoTubeAndroidSearch.py

import time
import re
import speech_recognition as sr
import pyttsx3
import yt_dlp as youtube_dl
import pygame
import imageio_ffmpeg as ffmpeg
from freeGPT import Client
from googletrans import Translator
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
import os
import logging
import sys

# Создаем обработчик консоли с уровнем логирования ERROR
console_handler = logging.StreamHandler(sys.stderr)
console_handler.setLevel(logging.WARNING)

# Создаем форматтер для вывода логов в консоль
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)

# Добавляем обработчик консоли в корневой логгер
logger = logging.getLogger()
logger.addHandler(console_handler)

# Устанавливаем уровень корневого логгера на ERROR
logger.setLevel(logging.WARNING)

# Теперь только сообщения с уровнем ERROR и выше будут выводиться в консоль
logger.error('Это сообщение об ошибке')
logger.warning('Это предупреждение')
logger.info('Это информационное сообщение')
logger.debug('Это отладочное сообщение')

# Проверка, запущено ли приложение на Android
try:
    from android.permissions import request_permissions, Permission
    from android import api_version

    android_platform = True
except ImportError:
    android_platform = False


def safe_print(text):
    try:
        print(text)
    except UnicodeEncodeError:
        print(text.encode('utf-8').decode('utf-8'))


def speak(text):
    engine = pyttsx3.init()
    engine.say(text)
    engine.runAndWait()


# Инициация распознавания речи
recognizer = sr.Recognizer()


def listen_command():
    with sr.Microphone() as source:
        safe_print("Listening for command…")
        audio = recognizer.listen(source)
        try:
            command = recognizer.recognize_google(audio, language="ru-RU")
            safe_print(f"Command heard: {command}")
            return command.lower()
        except sr.UnknownValueError:
            speak("Я не понял ваш запрос")
            return ""
        except sr.RequestError:
            speak("Ошибка сервиса распознавания речи")
            return ""


# Функция для удаления недопустимых символов из имени файла
def sanitize_filename(filename):
    return re.sub(r'[/*?:"<>|]', '', filename)


# Функция для поиска на YouTube и скачивания аудио
def search_youtube(query):
    try:
        sanitized_query = sanitize_filename(query)
        directory = "mp3"

        # Создание директории, если она не существует
        if not os.path.exists(directory):
            os.makedirs(directory)

        youtube_dl_opts = {
            'format': 'bestaudio/best',
            'noplaylist': True,
            'quiet': True,
            'outtmpl': os.path.join(directory, f'{sanitized_query}.%(ext)s'),
            'ffmpeg_location': ffmpeg.get_ffmpeg_exe(),
            'postprocessors': [{
                'key': 'FFmpegExtractAudio',
                'preferredcodec': 'mp3',
                'preferredquality': '192',
            }],
        }
    except Exception as e:
        # обработка ошибки
        print("Ошибка:", e)

    file_path = os.path.join(directory, f"{sanitized_query}.mp3")
    safe_print(f"File path: {file_path}")

    if os.path.exists(file_path):
        speak(f"Файл уже существует. Проигрываю {sanitized_query}.")
        return file_path

    with youtube_dl.YoutubeDL(youtube_dl_opts) as ydl:
        info = ydl.extract_info(f"ytsearch1:{query}", download=False)
        video_info = info['entries'][0]
        ydl.download([f"ytsearch1:{query}"])
        while not os.path.exists(file_path):
            time.sleep(1)
        return file_path
      


# Функция регулировки громкости
def set_volume(level):
    try:
        level = int(level)
        if 0 <= level <= 10:
            pygame.mixer.music.set_volume(level / 10.0)
            speak(f"Громкость установлена на {level}")
        else:
            speak("Пожалуйста, укажите уровень громкости от 0 до 10.")
    except ValueError:
        speak("Я не понял уровень громкости, укажите значение от 0 до 10.")


commands = [
        "найди песню - для поиска и воспроизведения песни",
        "продолжай - для продолжения воспроизведения",
        "пауза - для паузы воспроизведения",
        "стоп - для остановки воспроизведения и удаления песни",
        "включи - для воспроизведения песни",
        "найди или найти - для поиска новой песни",
        "перестань слушать или не слушай - для прекращения слушать команды",
        "слушай - для возобновления слушания команд",
        "громкость - от нуля до десяти",
        "команды - для воспроизведения этого списка команд",
        "спасибо, выход или завершить - для завершения программы"
    ]
# Функция для вывода доступных команд
def list_commands():
    speak("Доступные команды: " + ", ".join(commands))


# Функция для запроса к GPT
def get_gpt_response(query):
    translator = Translator()
    try:
        translated_query = translator.translate(query, src='ru', dest='en').text
        resp = Client.create_completion("gpt3_5", translated_query)
        translated_resp = translator.translate(resp, src='en', dest='ru').text
        return translated_resp
    except Exception as e:
        return f"Произошла ошибка: {e}"


# Словарь для перевода текстовых чисел в числовые значения
text_to_number = {
    "ноль": 0, "один": 1, "два": 2, "три": 3, "четыре": 4, "пять": 5,
    "шесть": 6, "семь": 7, "восемь": 8, "девять": 9, "десять": 10
}


# Функция для получения числового значения из текста
def get_number_from_text(text):
    pattern = r"\d+"
    match = re.search(pattern, text)
    if match:
        return int(match.group(0))
    else:
        return None


# Главная функция приложения Kivy
class MyApp(App):
    def build(self):
        pygame.init()
        pygame.mixer.init()

        if android_platform:
            # Запрашиваем разрешения, если приложение запущено на Android
            self.request_android_permissions()

        self.current_song = None
        self.listening = True

        self.layout = BoxLayout(orientation='vertical')

        self.command_input = TextInput(size_hint=(1, 0.1), multiline=False, hint_text='Введите команду или запрос')
        self.layout.add_widget(self.command_input)

        self.result_output = TextInput(size_hint=(1, 0.5), readonly=True)
        self.layout.add_widget(self.result_output)

        # Кнопка для отправки текстового запроса
        self.text_query_button = Button(text='Отправить запрос', size_hint=(1, 0.1))
        self.text_query_button.bind(on_release=self.process_text_query)
        self.layout.add_widget(self.text_query_button)

        self.button_layout = BoxLayout(size_hint=(1, 0.3))
        commands = ["пауза", "назад", "продолжай", "вперед", "стоп", "громкость +", "громкость -", "команды", "выход"]
        for command in commands:
            btn = Button(text=command)
            btn.bind(on_release=self.button_action)
            if command == "пауза":
                self.button_layout.add_widget(btn)
            elif command == "назад":
                self.button_layout.add_widget(btn)
            elif command == "продолжай":
                self.button_layout.add_widget(btn)
            elif command == "вперед":
                self.button_layout.add_widget(btn)
            elif command == "стоп":
                self.button_layout.add_widget(btn)
            elif command == "громкость +":
                btn.bind(on_release=self.volume_up_button_action)
                self.button_layout.add_widget(btn)
            elif command == "громкость -":
                btn.bind(on_release=self.volume_down_button_action)
                self.button_layout.add_widget(btn)
            elif command == "команды":
                self.button_layout.add_widget(btn)
            elif command == "выход":
                self.button_layout.add_widget(btn)

        self.layout.add_widget(self.button_layout)

        # Кнопка для прослушивания команды
        self.listen_button = Button(text='Голосовая команда', size_hint=(1, 0.1))
        self.listen_button.bind(on_release=self.listen_command_action)
        self.layout.add_widget(self.listen_button)
        self.result_output.text += "\nНажми 'Голосовая команда' и скажи 'песню'"

        # Кнопка для перемотки назад
        self.back_button = Button(text='Назад', size_hint=(1, 0.1))
        self.back_button.bind(on_release=self.back_button_action)


        # Кнопка для перемотки вперед
        self.forward_button = Button(text='Вперед', size_hint=(1, 0.1))
        self.forward_button.bind(on_release=self.forward_button_action)


        # Кнопка для увеличения громкости
        self.volume_up_button = Button(text='Громкость +', size_hint=(1, 0.1))
        self.volume_up_button.bind(on_release=self.volume_up_button_action)


        # Кнопка для уменьшения громкости
        self.volume_down_button = Button(text='Громкость -', size_hint=(1, 0.1))
        self.volume_up_button.bind(on_release=self.volume_down_button_action)

        return self.layout

    def request_android_permissions(self):
        def callback(permissions, results):
            if all(results):
                safe_print("Permissions granted")
            else:
                safe_print("Permissions denied")
                speak("Приложению необходимы все разрешения для корректной работы.")
                return

        permissions_to_request = [Permission.READ_EXTERNAL_STORAGE,
                                  Permission.WRITE_EXTERNAL_STORAGE,
                                  Permission.RECORD_AUDIO]
        request_permissions(permissions_to_request, callback)

    def process_text_query(self, instance):
        query = self.command_input.text
        self.result_output.text += f"\nВы: {query}"
        self.command_input.text = ''
        self.execute_command(query)

    def listen_command_action(self, instance):
        command = listen_command()
        self.result_output.text += f"\nВы (голосовая команда): {command}"
        self.execute_command(command)

    def button_action(self, instance):
        command = instance.text
        self.result_output.text += f"\nВы (кнопка): {command}"
        self.execute_command(command)

    def stop_playback(self):
        if self.current_song:
            pygame.mixer.music.stop()
            while pygame.mixer.music.get_busy():
                time.sleep(0.1)
            if os.path.exists(self.current_song):
                os.remove(self.current_song)
            self.current_song = None
            self.result_output.text += "\nВоспроизведение остановлено"

    def back_button_action(self, instance):
        # Логика перемотки назад
        # Увеличиваем текущую позицию видео на 10 секунд
        current_position = self.player.get_time()
        new_position = max(current_position - 10000, 0)
        self.player.seek(new_position)

    def forward_button_action(self, instance):
        # Логика перемотки вперед
        # Уменьшаем текущую позицию видео на 10 секунд
        current_position = self.player.get_time()
        new_position = min(current_position + 10000, self.player.get_duration())
        self.player.seek(new_position)

    def volume_up_button_action(self, instance):
        # Логика увеличения громкости
        # Увеличиваем громкость на 1
        current_volume = self.player.audio_get_volume()
        new_volume = min(current_volume + 1, 10)
        self.player.audio_set_volume(new_volume)

    def volume_down_button_action(self, instance):
        # Логика уменьшения громкости
        # Уменьшаем громкость на 1
        current_volume = self.player.audio_get_volume()
        new_volume = max(current_volume - 1, 0)
        self.player.audio_set_volume(new_volume)


    def execute_command(self, command):
        if "перестань слушать" in command or "не слушай" in command:
            speak("Я перестаю слушать. Скажите 'Слушай', чтобы я снова начал слушать.")
            self.listening = False
            return

        if "слушай" in command:
            speak("Я снова слушаю.")
            self.listening = True
            return

        if "команды" in command:
            # list_commands()
            self.result_output.text += "\nДоступные команды:\n"
            for command in commands:
                self.result_output.text += f"- {command}\n"
            self.result_output.text += "\nДоступные команды перечислены."

            return

        if "песню" in command or "песня" in command or "музыку" in command or "музыка" in command or "youtube" in command:
            speak("Какую песню вы хотите услышать?")
            search_query = listen_command()
            safe_print(f"Search Query: {search_query}")

            if search_query:
                song_path = search_youtube(search_query)
                safe_print(f"Song Path: {song_path}")
                self.result_output.text += "\nЗагружаю файл..."
                if song_path and os.path.exists(song_path):
                    if self.current_song:
                        pygame.mixer.music.stop()
                        while pygame.mixer.music.get_busy():
                            time.sleep(0.1)
                        if os.path.exists(self.current_song):
                            os.remove(self.current_song)
                    self.current_song = song_path
                    pygame.mixer.music.load(self.current_song)
                    pygame.mixer.music.play()
                    speak(f"Проигрываю песню '{search_query}'")
                    self.result_output.text += f"\nПроигрываю песню '{search_query}'"
                else:
                    speak("Не удалось найти песню.")
            return

        if "продолжай" in command:
            if self.current_song and not pygame.mixer.music.get_busy():
                pygame.mixer.music.unpause()
                self.result_output.text += "\nВоспроизведение продолжено"
            return

        if "пауза" in command:
            if self.current_song and pygame.mixer.music.get_busy():
                pygame.mixer.music.pause()
                self.result_output.text += "\nВоспроизведение приостановлено"
            return

        if "стоп" in command:
            if self.current_song:
                try:
                    pygame.mixer.music.stop()
                    self.current_song = None
                    self.result_output.text += "\nВоспроизведение остановлено"
                    self.listen_for_commands()

                except Exception as e:
                    logger.error(f"Произошла ошибка при остановке проигрывания: {str(e).encode('utf-8').decode('ascii', 'ignore')}")
                    return

        if "включи" in command:
            if self.current_song:
                pygame.mixer.music.play()
                self.result_output.text += "\nПроигрывание начато"
            return

        if "найди" in command or "найти" in command:
            if self.current_song:
                pygame.mixer.music.stop()
                while pygame.mixer.music.get_busy():
                    time.sleep(0.1)
                if os.path.exists(self.current_song):
                    os.remove(self.current_song)
                self.current_song = None

            speak("Что вы хотите найти?")
            search_query = listen_command()
            safe_print(f"Search Query: {search_query}")

            if search_query:
                song_path = search_youtube(search_query)
                safe_print(f"Song Path: {song_path}")
                if song_path and os.path.exists(song_path):
                    self.current_song = song_path
                    pygame.mixer.music.load(self.current_song)
                    pygame.mixer.music.play()
                    speak(f"Проигрываю песню '{search_query}'")
                    self.result_output.text += f"\nПроигрываю песню '{search_query}'"
                else:
                    speak("Не удалось найти песню.")
            return

        if "громкость" in command:
            parts = command.split()
            if len(parts) > 1:
                number_word = parts[1]
                level = get_number_from_text(number_word)
                if level is not None:
                    set_volume(level)
                    self.result_output.text += f"\nГромкость установлена на {level}"
                else:
                    speak("Пожалуйста, укажите уровень громкости от 0 до 10.")
            else:
                speak("Пожалуйста, укажите уровень громкости от 0 до 10.")
            return

        if "спасибо" in command or "выход" in command or "завершить" in command:
            speak("Завершаю работу. До свидания!")
            App.get_running_app().stop()
            return

        else:
            gpt_response = get_gpt_response(command)
            speak(gpt_response)
            self.result_output.text += f"\nОтвет: {gpt_response}"
            return


if __name__ == "__main__":
    try:
        MyApp().run()
    except KeyboardInterrupt:
        pass
    finally:
        pygame.quit()