# This file is part of Gajim. # # Gajim is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Gajim is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Gajim. If not, see . from __future__ import annotations import logging import typing from pathlib import Path from typing import TYPE_CHECKING import whisper from gi.repository import Gtk from gajim.common import app from gajim.common.app import Any from gajim.gtk.builder import get_builder from gajim.gtk.const import Setting, SettingKind, SettingType from gajim.gtk.settings import SettingsBox from gajim.gtk.sidebar_switcher import SideBarSwitcher from gajim.plugins.helpers import get_builder from gajim.plugins.plugins_i18n import _ from ..models import openai_whisper if TYPE_CHECKING: from ..stt_voice_messages import STTVoiceMessagesPlugin log = logging.getLogger('gajim.p.stt_voice_messages_config_dialog') SUPPORTED_MODELS: dict[str, dict[str, typing.Union[list[str], Any, str]]] = { 'model_openaiwhisper': { 'moduls': ['whisper'], 'class': openai_whisper.WhisperModel, 'name': 'OpenAI Whisper' }, 'model_ctranslate2': { 'moduls': ['ctranslate2'], 'class': None, 'name': _('CTranslate2') }, 'model_faster-whisper': { 'moduls': ['faster-whisper'], 'class': None, 'name:': _('Faster-Whisper') }, 'model_distill': { 'moduls': ['transformers', 'accelerate', 'datasets[audio]'], 'class': None, 'name': _('Distill') } } class Configuration: def __init__(self, plugin: STTVoiceMessagesPlugin): self._plugin = plugin self._available_models: dict[ str, dict[str, typing.Union[list[str], Any, str]]] = {} self.check_available_moduls() @property def plugin(self) -> STTVoiceMessagesPlugin: return self._plugin @property def available_models(self) -> dict[str, dict[str, typing.Union[list[str], Any, str]]]: return self._available_models def on_setting(self, value: Any, data: Any) -> None: if isinstance(value, str): value.strip() log.debug('plugin config before:\n %s', self.plugin.config.data) self.plugin.config[data] = value log.debug('plugin config after:\n %s', self.plugin.config.data) def on_set_model(self, value: Any, data: Any) -> None: if isinstance(value, str): value.strip() log.debug('plugin config before:\n %s', self.plugin.config.data) self.plugin.config['model_class'] = self._available_models[value][ 'class'] self.on_setting(value, data) log.debug('plugin config after:\n %s', self.plugin.config.data) @staticmethod def is_module_available(module: str) -> bool: try: __import__(module) return True except ModuleNotFoundError: log.debug('Could not find module %s', module) return False except ImportError as ex: log.debug(str(ex)) return False def check_available_moduls(self): for model in SUPPORTED_MODELS: available = True for modul in SUPPORTED_MODELS[model]['moduls']: if not self.is_module_available(modul): available = False continue if available: self._available_models[model] = SUPPORTED_MODELS[model] if (self.plugin.config.data['model_class'] is None and len(self._available_models) > 0): model = list(self._available_models)[0] self.on_set_model(model, 'model') log.debug('Choose first available model!') else: log.debug('Available model already chosen!') log.debug('models = %s', self._available_models) class PreferenceBox(SettingsBox): def __init__(self, settings: list[Setting]) -> None: SettingsBox.__init__(self, None) self.get_style_context().add_class('border') self.set_selection_mode(Gtk.SelectionMode.NONE) self.set_vexpand(False) self.set_valign(Gtk.Align.END) for setting in settings: self.add_setting(setting) self.update_states() class STTVoiceMessagesConfigDialog(Gtk.ApplicationWindow): def __init__(self, config: Configuration, parent: Gtk.Window) -> None: Gtk.ApplicationWindow.__init__(self) self.set_application(app.app) self.set_position(Gtk.WindowPosition.CENTER) self.set_show_menubar(False) self.set_name('PreferencesWindow') self.set_default_size(900, 650) self.set_resizable(True) self.set_title(_('STT Voice Messages - Preferences')) ui_path = Path(__file__).parent self._ui = get_builder(str(ui_path.resolve() / 'config_dialog.ui')) self._prefs: dict[str, PreferenceBox] = {} side_bar_switcher = SideBarSwitcher() side_bar_switcher.set_stack(self._ui.stack) self._ui.grid.attach(side_bar_switcher, 0, 0, 1, 1) self.add(self._ui.grid) self.config = config self.plugin = self.config.plugin prefs: list[tuple[str, type[PreferenceBox]]] = [ ('stt_behaviour', self.STTBehaviour), ('models', self.Models), ('whisper_general', self.OpenAIWhisperGeneral), ] self._add_prefs(prefs) self.show_all() class STTBehaviour(PreferenceBox): def __init__(self, config_dialog: STTVoiceMessagesConfigDialog) -> None: settings = [ Setting(SettingKind.SWITCH, _('Auto Transcribe'), SettingType.VALUE, value=config_dialog.plugin.config['auto_transcribe'], data='auto_transcribe', callback=config_dialog.config.on_setting) ] PreferenceBox.__init__(self, settings) class Models(PreferenceBox): def __init__(self, config_dialog: STTVoiceMessagesConfigDialog) -> None: models: list[tuple[str, str]] = [] for key, value in config_dialog.config.available_models.items(): models.append( (key, str(value['name'])) ) settings = [ Setting(SettingKind.COMBO, _('Speech To Text Model'), SettingType.VALUE, value=config_dialog.plugin.config['model'], data='model', callback=config_dialog.config.on_set_model, props={'combo_items': models}, desc=_('Choose Model to use')), ] PreferenceBox.__init__(self, settings) class OpenAIWhisperGeneral(PreferenceBox): def __init__(self, config_dialog: STTVoiceMessagesConfigDialog) -> None: settings = [ Setting(SettingKind.POPOVER, _('Language Model Size'), SettingType.VALUE, value=config_dialog.plugin.config['whisperai_model_size'], data='whisperai_model_size', callback=config_dialog.config.on_setting, props={'entries': whisper.available_models()}), Setting(SettingKind.SWITCH, _('Translate'), SettingType.VALUE, value=config_dialog.plugin.config['whisperai_translate'], data='whisperai_translate', callback=config_dialog.config.on_setting) ] PreferenceBox.__init__(self, settings) def _add_prefs(self, prefs: list[tuple[str, type[PreferenceBox]]]): for ui_name, klass in prefs: pref_box = getattr(self._ui, ui_name) pref = klass(self) # pyright: ignore pref_box.add(pref) self._prefs[ui_name] = pref