GameSentenceMiner 2.5.11__py3-none-any.whl → 2.5.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- GameSentenceMiner/anki.py +7 -7
 - GameSentenceMiner/config_gui.py +0 -6
 - GameSentenceMiner/configuration.py +0 -1
 - GameSentenceMiner/gsm.py +54 -66
 - GameSentenceMiner/obs.py +5 -21
 - GameSentenceMiner/util.py +29 -35
 - {gamesentenceminer-2.5.11.dist-info → gamesentenceminer-2.5.12.dist-info}/METADATA +2 -2
 - {gamesentenceminer-2.5.11.dist-info → gamesentenceminer-2.5.12.dist-info}/RECORD +12 -12
 - {gamesentenceminer-2.5.11.dist-info → gamesentenceminer-2.5.12.dist-info}/WHEEL +0 -0
 - {gamesentenceminer-2.5.11.dist-info → gamesentenceminer-2.5.12.dist-info}/entry_points.txt +0 -0
 - {gamesentenceminer-2.5.11.dist-info → gamesentenceminer-2.5.12.dist-info}/licenses/LICENSE +0 -0
 - {gamesentenceminer-2.5.11.dist-info → gamesentenceminer-2.5.12.dist-info}/top_level.txt +0 -0
 
    
        GameSentenceMiner/anki.py
    CHANGED
    
    | 
         @@ -1,22 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import time
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            import base64
         
     | 
| 
       2 
4 
     | 
    
         
             
            import subprocess
         
     | 
| 
       3 
5 
     | 
    
         
             
            import threading
         
     | 
| 
       4 
     | 
    
         
            -
            import time
         
     | 
| 
       5 
6 
     | 
    
         
             
            import urllib.request
         
     | 
| 
       6 
7 
     | 
    
         
             
            from datetime import datetime, timedelta
         
     | 
| 
      
 8 
     | 
    
         
            +
            from requests import post
         
     | 
| 
       7 
9 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
            import  
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
            from GameSentenceMiner import obs, util, notification, ffmpeg, gametext
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
      
 10 
     | 
    
         
            +
            from GameSentenceMiner import obs, util, notification, ffmpeg
         
     | 
| 
       12 
11 
     | 
    
         
             
            from GameSentenceMiner.configuration import *
         
     | 
| 
       13 
12 
     | 
    
         
             
            from GameSentenceMiner.configuration import get_config
         
     | 
| 
       14 
13 
     | 
    
         
             
            from GameSentenceMiner.gametext import get_text_event
         
     | 
| 
       15 
14 
     | 
    
         
             
            from GameSentenceMiner.model import AnkiCard
         
     | 
| 
       16 
     | 
    
         
            -
            from GameSentenceMiner.utility_gui import  
     | 
| 
      
 15 
     | 
    
         
            +
            from GameSentenceMiner.utility_gui import get_utility_window
         
     | 
| 
       17 
16 
     | 
    
         
             
            from GameSentenceMiner.obs import get_current_game
         
     | 
| 
       18 
17 
     | 
    
         
             
            from GameSentenceMiner.util import remove_html_and_cloze_tags, combine_dialogue
         
     | 
| 
       19 
18 
     | 
    
         | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
       20 
20 
     | 
    
         
             
            audio_in_anki = None
         
     | 
| 
       21 
21 
     | 
    
         
             
            screenshot_in_anki = None
         
     | 
| 
       22 
22 
     | 
    
         
             
            prev_screenshot_in_anki = None
         
     | 
| 
         @@ -309,7 +309,7 @@ def monitor_anki(): 
     | 
|
| 
       309 
309 
     | 
    
         | 
| 
       310 
310 
     | 
    
         
             
            # Fetch recent note IDs from Anki
         
     | 
| 
       311 
311 
     | 
    
         
             
            def get_note_ids():
         
     | 
| 
       312 
     | 
    
         
            -
                response =  
     | 
| 
      
 312 
     | 
    
         
            +
                response = post(get_config().anki.url, json={
         
     | 
| 
       313 
313 
     | 
    
         
             
                    "action": "findNotes",
         
     | 
| 
       314 
314 
     | 
    
         
             
                    "version": 6,
         
     | 
| 
       315 
315 
     | 
    
         
             
                    "params": {"query": "added:1"}
         
     | 
    
        GameSentenceMiner/config_gui.py
    CHANGED
    
    | 
         @@ -835,12 +835,6 @@ class ConfigApp: 
     | 
|
| 
       835 
835 
     | 
    
         
             
                    self.add_label_and_increment_row(obs_frame, "Password for the OBS WebSocket server.", row=self.current_row,
         
     | 
| 
       836 
836 
     | 
    
         
             
                                                     column=2)
         
     | 
| 
       837 
837 
     | 
    
         | 
| 
       838 
     | 
    
         
            -
                    ttk.Label(obs_frame, text="Start/Stop Buffer:").grid(row=self.current_row, column=0, sticky='W')
         
     | 
| 
       839 
     | 
    
         
            -
                    self.obs_start_buffer = tk.BooleanVar(value=self.settings.obs.start_buffer)
         
     | 
| 
       840 
     | 
    
         
            -
                    ttk.Checkbutton(obs_frame, variable=self.obs_start_buffer).grid(row=self.current_row, column=1, sticky='W')
         
     | 
| 
       841 
     | 
    
         
            -
                    self.add_label_and_increment_row(obs_frame, "Start and Stop the Buffer when Script runs.", row=self.current_row,
         
     | 
| 
       842 
     | 
    
         
            -
                                                     column=2)
         
     | 
| 
       843 
     | 
    
         
            -
             
     | 
| 
       844 
838 
     | 
    
         
             
                    ttk.Label(obs_frame, text="Get Game From Scene Name:").grid(row=self.current_row, column=0, sticky='W')
         
     | 
| 
       845 
839 
     | 
    
         
             
                    self.get_game_from_scene_name = tk.BooleanVar(value=self.settings.obs.get_game_from_scene)
         
     | 
| 
       846 
840 
     | 
    
         
             
                    ttk.Checkbutton(obs_frame, variable=self.get_game_from_scene_name).grid(row=self.current_row, column=1,
         
     | 
    
        GameSentenceMiner/gsm.py
    CHANGED
    
    | 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
             
     | 
| 
       1 
2 
     | 
    
         
             
            import os.path
         
     | 
| 
       2 
3 
     | 
    
         
             
            import signal
         
     | 
| 
       3 
     | 
    
         
            -
            import time
         
     | 
| 
       4 
4 
     | 
    
         
             
            from subprocess import Popen
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
            import keyboard
         
     | 
| 
         @@ -11,6 +11,8 @@ from pystray import Icon, Menu, MenuItem 
     | 
|
| 
       11 
11 
     | 
    
         
             
            from watchdog.events import FileSystemEventHandler
         
     | 
| 
       12 
12 
     | 
    
         
             
            from watchdog.observers import Observer
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
            import time
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
       14 
16 
     | 
    
         
             
            from GameSentenceMiner import anki
         
     | 
| 
       15 
17 
     | 
    
         
             
            from GameSentenceMiner import config_gui
         
     | 
| 
       16 
18 
     | 
    
         
             
            from GameSentenceMiner import configuration
         
     | 
| 
         @@ -29,11 +31,11 @@ from GameSentenceMiner.gametext import get_text_event, get_mined_line, GameLine 
     | 
|
| 
       29 
31 
     | 
    
         
             
            from GameSentenceMiner.obs import check_obs_folder_is_correct
         
     | 
| 
       30 
32 
     | 
    
         
             
            from GameSentenceMiner.util import *
         
     | 
| 
       31 
33 
     | 
    
         
             
            from GameSentenceMiner.utility_gui import init_utility_window, get_utility_window
         
     | 
| 
       32 
     | 
    
         
            -
            from GameSentenceMiner.vad import silero_trim, whisper_helper, vosk_helper
         
     | 
| 
       33 
34 
     | 
    
         | 
| 
       34 
35 
     | 
    
         
             
            if is_windows():
         
     | 
| 
       35 
36 
     | 
    
         
             
                import win32api
         
     | 
| 
       36 
37 
     | 
    
         | 
| 
      
 38 
     | 
    
         
            +
            silero_trim, whisper_helper, vosk_helper = None, None, None
         
     | 
| 
       37 
39 
     | 
    
         
             
            procs_to_close = []
         
     | 
| 
       38 
40 
     | 
    
         
             
            settings_window: config_gui.ConfigApp = None
         
     | 
| 
       39 
41 
     | 
    
         
             
            obs_paused = False
         
     | 
| 
         @@ -42,34 +44,16 @@ menu: Menu 
     | 
|
| 
       42 
44 
     | 
    
         
             
            root = None
         
     | 
| 
       43 
45 
     | 
    
         | 
| 
       44 
46 
     | 
    
         | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
       45 
48 
     | 
    
         
             
            class VideoToAudioHandler(FileSystemEventHandler):
         
     | 
| 
       46 
49 
     | 
    
         
             
                def on_created(self, event):
         
     | 
| 
       47 
50 
     | 
    
         
             
                    if event.is_directory or ("Replay" not in event.src_path and "GSM" not in event.src_path):
         
     | 
| 
       48 
51 
     | 
    
         
             
                        return
         
     | 
| 
       49 
52 
     | 
    
         
             
                    if event.src_path.endswith(".mkv") or event.src_path.endswith(".mp4"):  # Adjust based on your OBS output format
         
     | 
| 
       50 
53 
     | 
    
         
             
                        logger.info(f"MKV {event.src_path} FOUND, RUNNING LOGIC")
         
     | 
| 
       51 
     | 
    
         
            -
                         
     | 
| 
      
 54 
     | 
    
         
            +
                        wait_for_stable_file(event.src_path)
         
     | 
| 
       52 
55 
     | 
    
         
             
                        self.convert_to_audio(event.src_path)
         
     | 
| 
       53 
56 
     | 
    
         | 
| 
       54 
     | 
    
         
            -
                @staticmethod
         
     | 
| 
       55 
     | 
    
         
            -
                def wait_for_stable_file(file_path, timeout=10, check_interval=0.5):
         
     | 
| 
       56 
     | 
    
         
            -
                    elapsed_time = 0
         
     | 
| 
       57 
     | 
    
         
            -
                    last_size = -1
         
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
                    while elapsed_time < timeout:
         
     | 
| 
       60 
     | 
    
         
            -
                        try:
         
     | 
| 
       61 
     | 
    
         
            -
                            current_size = os.path.getsize(file_path)
         
     | 
| 
       62 
     | 
    
         
            -
                            if current_size == last_size:
         
     | 
| 
       63 
     | 
    
         
            -
                                return True
         
     | 
| 
       64 
     | 
    
         
            -
                            last_size = current_size
         
     | 
| 
       65 
     | 
    
         
            -
                            time.sleep(check_interval)
         
     | 
| 
       66 
     | 
    
         
            -
                            elapsed_time += check_interval
         
     | 
| 
       67 
     | 
    
         
            -
                        except Exception as e:
         
     | 
| 
       68 
     | 
    
         
            -
                            logger.warning(f"Error checking file size, will still try updating Anki Card!: {e}")
         
     | 
| 
       69 
     | 
    
         
            -
                            return False
         
     | 
| 
       70 
     | 
    
         
            -
                    logger.warning("File size did not stabilize within the timeout period. Continuing...")
         
     | 
| 
       71 
     | 
    
         
            -
                    return False
         
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
57 
     | 
    
         
             
                @staticmethod
         
     | 
| 
       74 
58 
     | 
    
         
             
                def convert_to_audio(video_path):
         
     | 
| 
       75 
59 
     | 
    
         
             
                    try:
         
     | 
| 
         @@ -183,26 +167,10 @@ class VideoToAudioHandler(FileSystemEventHandler): 
     | 
|
| 
       183 
167 
     | 
    
         
             
                                                                            f"{obs.get_current_game(sanitize=True)}.{get_config().audio.extension}"))
         
     | 
| 
       184 
168 
     | 
    
         
             
                    should_update_audio = True
         
     | 
| 
       185 
169 
     | 
    
         
             
                    if get_config().vad.do_vad_postprocessing:
         
     | 
| 
       186 
     | 
    
         
            -
                         
     | 
| 
       187 
     | 
    
         
            -
                            case configuration.SILERO:
         
     | 
| 
       188 
     | 
    
         
            -
                                should_update_audio = silero_trim.process_audio_with_silero(trimmed_audio, vad_trimmed_audio)
         
     | 
| 
       189 
     | 
    
         
            -
                            case configuration.VOSK:
         
     | 
| 
       190 
     | 
    
         
            -
                                should_update_audio = vosk_helper.process_audio_with_vosk(trimmed_audio, vad_trimmed_audio)
         
     | 
| 
       191 
     | 
    
         
            -
                            case configuration.WHISPER:
         
     | 
| 
       192 
     | 
    
         
            -
                                should_update_audio = whisper_helper.process_audio_with_whisper(trimmed_audio,
         
     | 
| 
       193 
     | 
    
         
            -
                                                                                                vad_trimmed_audio)
         
     | 
| 
      
 170 
     | 
    
         
            +
                        should_update_audio = do_vad_processing(get_config().vad.selected_vad_model, trimmed_audio, vad_trimmed_audio)
         
     | 
| 
       194 
171 
     | 
    
         
             
                        if not should_update_audio:
         
     | 
| 
       195 
     | 
    
         
            -
                             
     | 
| 
       196 
     | 
    
         
            -
             
     | 
| 
       197 
     | 
    
         
            -
                                    pass
         
     | 
| 
       198 
     | 
    
         
            -
                                case configuration.SILERO:
         
     | 
| 
       199 
     | 
    
         
            -
                                    should_update_audio = silero_trim.process_audio_with_silero(trimmed_audio,
         
     | 
| 
       200 
     | 
    
         
            -
                                                                                                vad_trimmed_audio)
         
     | 
| 
       201 
     | 
    
         
            -
                                case configuration.VOSK:
         
     | 
| 
       202 
     | 
    
         
            -
                                    should_update_audio = vosk_helper.process_audio_with_vosk(trimmed_audio, vad_trimmed_audio)
         
     | 
| 
       203 
     | 
    
         
            -
                                case configuration.WHISPER:
         
     | 
| 
       204 
     | 
    
         
            -
                                    should_update_audio = whisper_helper.process_audio_with_whisper(trimmed_audio,
         
     | 
| 
       205 
     | 
    
         
            -
                                                                                                    vad_trimmed_audio)
         
     | 
| 
      
 172 
     | 
    
         
            +
                            should_update_audio = do_vad_processing(get_config().vad.selected_vad_model, trimmed_audio,
         
     | 
| 
      
 173 
     | 
    
         
            +
                                                                    vad_trimmed_audio)
         
     | 
| 
       206 
174 
     | 
    
         
             
                        if not should_update_audio and get_config().vad.add_audio_on_no_results:
         
     | 
| 
       207 
175 
     | 
    
         
             
                            logger.info("No voice activity detected, using full audio.")
         
     | 
| 
       208 
176 
     | 
    
         
             
                            vad_trimmed_audio = trimmed_audio
         
     | 
| 
         @@ -214,6 +182,22 @@ class VideoToAudioHandler(FileSystemEventHandler): 
     | 
|
| 
       214 
182 
     | 
    
         
             
                        shutil.move(vad_trimmed_audio, final_audio_output)
         
     | 
| 
       215 
183 
     | 
    
         
             
                    return final_audio_output, should_update_audio, vad_trimmed_audio
         
     | 
| 
       216 
184 
     | 
    
         | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
            def do_vad_processing(model, trimmed_audio, vad_trimmed_audio, second_pass=False):
         
     | 
| 
      
 187 
     | 
    
         
            +
                match model:
         
     | 
| 
      
 188 
     | 
    
         
            +
                    case configuration.OFF:
         
     | 
| 
      
 189 
     | 
    
         
            +
                        pass
         
     | 
| 
      
 190 
     | 
    
         
            +
                    case configuration.SILERO:
         
     | 
| 
      
 191 
     | 
    
         
            +
                        from GameSentenceMiner.vad import silero_trim
         
     | 
| 
      
 192 
     | 
    
         
            +
                        return silero_trim.process_audio_with_silero(trimmed_audio, vad_trimmed_audio)
         
     | 
| 
      
 193 
     | 
    
         
            +
                    case configuration.VOSK:
         
     | 
| 
      
 194 
     | 
    
         
            +
                        from GameSentenceMiner.vad import vosk_helper
         
     | 
| 
      
 195 
     | 
    
         
            +
                        return vosk_helper.process_audio_with_vosk(trimmed_audio, vad_trimmed_audio)
         
     | 
| 
      
 196 
     | 
    
         
            +
                    case configuration.WHISPER:
         
     | 
| 
      
 197 
     | 
    
         
            +
                        from GameSentenceMiner.vad import whisper_helper
         
     | 
| 
      
 198 
     | 
    
         
            +
                        return whisper_helper.process_audio_with_whisper(trimmed_audio, vad_trimmed_audio)
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
       217 
201 
     | 
    
         
             
            def play_audio_in_external(filepath):
         
     | 
| 
       218 
202 
     | 
    
         
             
                exe = get_config().advanced.audio_player_path
         
     | 
| 
       219 
203 
     | 
    
         | 
| 
         @@ -244,6 +228,8 @@ def play_video_in_external(line, filepath): 
     | 
|
| 
       244 
228 
     | 
    
         
             
                    command.append(convert_to_vlc_seconds(start))
         
     | 
| 
       245 
229 
     | 
    
         
             
                command.append(os.path.normpath(filepath))
         
     | 
| 
       246 
230 
     | 
    
         | 
| 
      
 231 
     | 
    
         
            +
                logger.info(" ".join(command))
         
     | 
| 
      
 232 
     | 
    
         
            +
             
     | 
| 
       247 
233 
     | 
    
         
             
                try:
         
     | 
| 
       248 
234 
     | 
    
         
             
                    proc = subprocess.Popen(command)
         
     | 
| 
       249 
235 
     | 
    
         
             
                    print(f"Opened {filepath} in {get_config().advanced.video_player_path}.")
         
     | 
| 
         @@ -286,7 +272,7 @@ def register_hotkeys(): 
     | 
|
| 
       286 
272 
     | 
    
         
             
            def get_screenshot():
         
     | 
| 
       287 
273 
     | 
    
         
             
                try:
         
     | 
| 
       288 
274 
     | 
    
         
             
                    image = obs.get_screenshot()
         
     | 
| 
       289 
     | 
    
         
            -
                     
     | 
| 
      
 275 
     | 
    
         
            +
                    wait_for_stable_file(image, timeout=3)
         
     | 
| 
       290 
276 
     | 
    
         
             
                    if not image:
         
     | 
| 
       291 
277 
     | 
    
         
             
                        raise Exception("Failed to get Screenshot from OBS")
         
     | 
| 
       292 
278 
     | 
    
         
             
                    encoded_image = ffmpeg.process_image(image)
         
     | 
| 
         @@ -479,9 +465,8 @@ def cleanup(): 
     | 
|
| 
       479 
465 
     | 
    
         
             
                util.keep_running = False
         
     | 
| 
       480 
466 
     | 
    
         | 
| 
       481 
467 
     | 
    
         
             
                if get_config().obs.enabled:
         
     | 
| 
       482 
     | 
    
         
            -
                     
     | 
| 
       483 
     | 
    
         
            -
             
     | 
| 
       484 
     | 
    
         
            -
                obs.disconnect_from_obs()
         
     | 
| 
      
 468 
     | 
    
         
            +
                    obs.stop_replay_buffer()
         
     | 
| 
      
 469 
     | 
    
         
            +
                    obs.disconnect_from_obs()
         
     | 
| 
       485 
470 
     | 
    
         
             
                if get_config().obs.close_obs:
         
     | 
| 
       486 
471 
     | 
    
         
             
                    close_obs()
         
     | 
| 
       487 
472 
     | 
    
         | 
| 
         @@ -521,9 +506,9 @@ def initialize(reloading=False): 
     | 
|
| 
       521 
506 
     | 
    
         
             
                    if is_windows():
         
     | 
| 
       522 
507 
     | 
    
         
             
                        download_obs_if_needed()
         
     | 
| 
       523 
508 
     | 
    
         
             
                        download_ffmpeg_if_needed()
         
     | 
| 
       524 
     | 
    
         
            -
                     
     | 
| 
       525 
     | 
    
         
            -
                         
     | 
| 
       526 
     | 
    
         
            -
                             
     | 
| 
      
 509 
     | 
    
         
            +
                    if get_config().obs.enabled:
         
     | 
| 
      
 510 
     | 
    
         
            +
                        if get_config().obs.open_obs:
         
     | 
| 
      
 511 
     | 
    
         
            +
                            obs_process = obs.start_obs()
         
     | 
| 
       527 
512 
     | 
    
         
             
                        # obs.connect_to_obs(start_replay=True)
         
     | 
| 
       528 
513 
     | 
    
         
             
                        # anki.start_monitoring_anki()
         
     | 
| 
       529 
514 
     | 
    
         
             
                    # gametext.start_text_monitor()
         
     | 
| 
         @@ -541,27 +526,29 @@ def initialize(reloading=False): 
     | 
|
| 
       541 
526 
     | 
    
         
             
            def initialize_async():
         
     | 
| 
       542 
527 
     | 
    
         
             
                tasks = [gametext.start_text_monitor, connect_websocket, run_tray]
         
     | 
| 
       543 
528 
     | 
    
         
             
                threads = []
         
     | 
| 
       544 
     | 
    
         
            -
                 
     | 
| 
       545 
     | 
    
         
            -
                    if get_config().obs.open_obs:
         
     | 
| 
       546 
     | 
    
         
            -
                        tasks.append(obs.start_obs)
         
     | 
| 
       547 
     | 
    
         
            -
                    else:
         
     | 
| 
       548 
     | 
    
         
            -
                        tasks.append(obs.connect_to_obs)
         
     | 
| 
       549 
     | 
    
         
            -
                    tasks.append(anki.start_monitoring_anki)
         
     | 
| 
       550 
     | 
    
         
            -
                if get_config().vad.do_vad_postprocessing:
         
     | 
| 
       551 
     | 
    
         
            -
                    if VOSK in (get_config().vad.backup_vad_model, get_config().vad.selected_vad_model):
         
     | 
| 
       552 
     | 
    
         
            -
                        tasks.append(vosk_helper.get_vosk_model)
         
     | 
| 
       553 
     | 
    
         
            -
                if WHISPER in (get_config().vad.backup_vad_model, get_config().vad.selected_vad_model):
         
     | 
| 
       554 
     | 
    
         
            -
                        tasks.append(whisper_helper.initialize_whisper_model)
         
     | 
| 
      
 529 
     | 
    
         
            +
                tasks.append(anki.start_monitoring_anki)
         
     | 
| 
       555 
530 
     | 
    
         
             
                for task in tasks:
         
     | 
| 
       556 
531 
     | 
    
         
             
                    threads.append(util.run_new_thread(task))
         
     | 
| 
       557 
532 
     | 
    
         
             
                return threads
         
     | 
| 
       558 
533 
     | 
    
         | 
| 
       559 
     | 
    
         
            -
             
     | 
| 
       560 
     | 
    
         
            -
             
     | 
| 
       561 
     | 
    
         
            -
             
     | 
| 
       562 
     | 
    
         
            -
             
     | 
| 
       563 
     | 
    
         
            -
                     
     | 
| 
       564 
     | 
    
         
            -
             
     | 
| 
      
 534 
     | 
    
         
            +
             
     | 
| 
      
 535 
     | 
    
         
            +
            def post_init():
         
     | 
| 
      
 536 
     | 
    
         
            +
                def do_post_init():
         
     | 
| 
      
 537 
     | 
    
         
            +
                    global silero_trim, whisper_helper, vosk_helper
         
     | 
| 
      
 538 
     | 
    
         
            +
                    logger.info("Post-Initialization started.")
         
     | 
| 
      
 539 
     | 
    
         
            +
                    if get_config().obs.enabled:
         
     | 
| 
      
 540 
     | 
    
         
            +
                        obs.connect_to_obs()
         
     | 
| 
      
 541 
     | 
    
         
            +
                        check_obs_folder_is_correct()
         
     | 
| 
      
 542 
     | 
    
         
            +
                        from GameSentenceMiner.vad import vosk_helper
         
     | 
| 
      
 543 
     | 
    
         
            +
                        from GameSentenceMiner.vad import whisper_helper
         
     | 
| 
      
 544 
     | 
    
         
            +
                        if get_config().vad.is_vosk():
         
     | 
| 
      
 545 
     | 
    
         
            +
                            vosk_helper.get_vosk_model()
         
     | 
| 
      
 546 
     | 
    
         
            +
                        if get_config().vad.is_whisper():
         
     | 
| 
      
 547 
     | 
    
         
            +
                            whisper_helper.initialize_whisper_model()
         
     | 
| 
      
 548 
     | 
    
         
            +
                        if get_config().vad.is_silero():
         
     | 
| 
      
 549 
     | 
    
         
            +
                            from GameSentenceMiner.vad import silero_trim
         
     | 
| 
      
 550 
     | 
    
         
            +
             
     | 
| 
      
 551 
     | 
    
         
            +
                util.run_new_thread(do_post_init)
         
     | 
| 
       565 
552 
     | 
    
         | 
| 
       566 
553 
     | 
    
         | 
| 
       567 
554 
     | 
    
         
             
            def handle_websocket_message(message: Message):
         
     | 
| 
         @@ -581,7 +568,6 @@ def main(reloading=False): 
     | 
|
| 
       581 
568 
     | 
    
         
             
                init_utility_window(root)
         
     | 
| 
       582 
569 
     | 
    
         
             
                initialize(reloading)
         
     | 
| 
       583 
570 
     | 
    
         
             
                initialize_async()
         
     | 
| 
       584 
     | 
    
         
            -
                check_obs_folder_is_correct()
         
     | 
| 
       585 
571 
     | 
    
         
             
                observer = Observer()
         
     | 
| 
       586 
572 
     | 
    
         
             
                observer.schedule(VideoToAudioHandler(), get_config().paths.folder_to_watch, recursive=False)
         
     | 
| 
       587 
573 
     | 
    
         
             
                observer.start()
         
     | 
| 
         @@ -599,6 +585,7 @@ def main(reloading=False): 
     | 
|
| 
       599 
585 
     | 
    
         
             
                        root.after(0, settings_window.show)
         
     | 
| 
       600 
586 
     | 
    
         
             
                    if get_config().general.open_multimine_on_startup:
         
     | 
| 
       601 
587 
     | 
    
         
             
                        root.after(0, get_utility_window().show)
         
     | 
| 
      
 588 
     | 
    
         
            +
                    root.after(0, post_init)
         
     | 
| 
       602 
589 
     | 
    
         
             
                    settings_window.add_save_hook(update_icon)
         
     | 
| 
       603 
590 
     | 
    
         
             
                    settings_window.on_exit = exit_program
         
     | 
| 
       604 
591 
     | 
    
         
             
                    root.mainloop()
         
     | 
| 
         @@ -613,4 +600,5 @@ def main(reloading=False): 
     | 
|
| 
       613 
600 
     | 
    
         | 
| 
       614 
601 
     | 
    
         | 
| 
       615 
602 
     | 
    
         
             
            if __name__ == "__main__":
         
     | 
| 
      
 603 
     | 
    
         
            +
                logger.info("Starting GSM")
         
     | 
| 
       616 
604 
     | 
    
         
             
                main()
         
     | 
    
        GameSentenceMiner/obs.py
    CHANGED
    
    | 
         @@ -28,12 +28,9 @@ def start_obs(): 
     | 
|
| 
       28 
28 
     | 
    
         
             
                    return None
         
     | 
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
                try:
         
     | 
| 
       31 
     | 
    
         
            -
                     
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
                        return obs_pid
         
     | 
| 
       34 
     | 
    
         
            -
                    obs_process = subprocess.Popen([obs_path, '--disable-shutdown-check', '--portable'], cwd=os.path.dirname(obs_path))
         
     | 
| 
      
 31 
     | 
    
         
            +
                    obs_process = subprocess.Popen([obs_path, '--disable-shutdown-check', '--portable', '--startreplaybuffer'], cwd=os.path.dirname(obs_path))
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
       35 
33 
     | 
    
         
             
                    logger.info("OBS launched")
         
     | 
| 
       36 
     | 
    
         
            -
                    connect_to_obs()
         
     | 
| 
       37 
34 
     | 
    
         
             
                    return obs_process.pid
         
     | 
| 
       38 
35 
     | 
    
         
             
                except Exception as e:
         
     | 
| 
       39 
36 
     | 
    
         
             
                    logger.error(f"Error launching OBS: {e}")
         
     | 
| 
         @@ -48,15 +45,6 @@ def check_obs_folder_is_correct(): 
     | 
|
| 
       48 
45 
     | 
    
         
             
                    get_master_config().sync_shared_fields()
         
     | 
| 
       49 
46 
     | 
    
         
             
                    save_full_config(get_master_config())
         
     | 
| 
       50 
47 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
            def is_obs_running(obs_path):
         
     | 
| 
       52 
     | 
    
         
            -
                obs_path = os.path.abspath(obs_path)  # Normalize path
         
     | 
| 
       53 
     | 
    
         
            -
                for process in psutil.process_iter(['exe']):
         
     | 
| 
       54 
     | 
    
         
            -
                    try:
         
     | 
| 
       55 
     | 
    
         
            -
                        if process.info['exe'] and os.path.abspath(process.info['exe']) == obs_path:
         
     | 
| 
       56 
     | 
    
         
            -
                            return process.pid
         
     | 
| 
       57 
     | 
    
         
            -
                    except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
         
     | 
| 
       58 
     | 
    
         
            -
                        continue
         
     | 
| 
       59 
     | 
    
         
            -
                return False
         
     | 
| 
       60 
48 
     | 
    
         | 
| 
       61 
49 
     | 
    
         
             
            def get_obs_websocket_config_values():
         
     | 
| 
       62 
50 
     | 
    
         
             
                config_path = os.path.join(get_app_directory(), 'obs-studio', 'config', 'obs-studio', 'plugin_config', 'obs-websocket', 'config.json')
         
     | 
| 
         @@ -92,10 +80,8 @@ def get_obs_websocket_config_values(): 
     | 
|
| 
       92 
80 
     | 
    
         | 
| 
       93 
81 
     | 
    
         | 
| 
       94 
82 
     | 
    
         
             
            def on_connect(obs):
         
     | 
| 
       95 
     | 
    
         
            -
                logger.info(" 
     | 
| 
       96 
     | 
    
         
            -
                 
     | 
| 
       97 
     | 
    
         
            -
                if get_config().obs.start_buffer:
         
     | 
| 
       98 
     | 
    
         
            -
                    start_replay_buffer()
         
     | 
| 
      
 83 
     | 
    
         
            +
                logger.info("Reconnected to OBS WebSocket.")
         
     | 
| 
      
 84 
     | 
    
         
            +
                start_replay_buffer()
         
     | 
| 
       99 
85 
     | 
    
         | 
| 
       100 
86 
     | 
    
         | 
| 
       101 
87 
     | 
    
         
             
            def on_disconnect(obs):
         
     | 
| 
         @@ -111,9 +97,6 @@ def connect_to_obs(): 
     | 
|
| 
       111 
97 
     | 
    
         
             
                                   password=get_config().obs.password, authreconnect=1, on_connect=on_connect,
         
     | 
| 
       112 
98 
     | 
    
         
             
                                   on_disconnect=on_disconnect)
         
     | 
| 
       113 
99 
     | 
    
         
             
                    client.connect()
         
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
                    if get_config().obs.start_buffer:
         
     | 
| 
       116 
     | 
    
         
            -
                        start_replay_buffer()
         
     | 
| 
       117 
100 
     | 
    
         
             
                    update_current_game()
         
     | 
| 
       118 
101 
     | 
    
         | 
| 
       119 
102 
     | 
    
         | 
| 
         @@ -135,6 +118,7 @@ def do_obs_call(request, from_dict = None, retry=10): 
     | 
|
| 
       135 
118 
     | 
    
         
             
                        return from_dict(response.datain)
         
     | 
| 
       136 
119 
     | 
    
         
             
                    return None
         
     | 
| 
       137 
120 
     | 
    
         
             
                except Exception as e:
         
     | 
| 
      
 121 
     | 
    
         
            +
                    logger.error(e)
         
     | 
| 
       138 
122 
     | 
    
         
             
                    if "socket is already closed" in str(e) or "object has no attribute" in str(e):
         
     | 
| 
       139 
123 
     | 
    
         
             
                        if retry > 0:
         
     | 
| 
       140 
124 
     | 
    
         
             
                            time.sleep(1)
         
     | 
    
        GameSentenceMiner/util.py
    CHANGED
    
    | 
         @@ -6,6 +6,7 @@ import string 
     | 
|
| 
       6 
6 
     | 
    
         
             
            import subprocess
         
     | 
| 
       7 
7 
     | 
    
         
             
            import sys
         
     | 
| 
       8 
8 
     | 
    
         
             
            import threading
         
     | 
| 
      
 9 
     | 
    
         
            +
            import time
         
     | 
| 
       9 
10 
     | 
    
         
             
            from datetime import datetime
         
     | 
| 
       10 
11 
     | 
    
         
             
            from sys import platform
         
     | 
| 
       11 
12 
     | 
    
         | 
| 
         @@ -178,38 +179,31 @@ def combine_dialogue(dialogue_lines, new_lines=None): 
     | 
|
| 
       178 
179 
     | 
    
         | 
| 
       179 
180 
     | 
    
         
             
                return new_lines
         
     | 
| 
       180 
181 
     | 
    
         | 
| 
       181 
     | 
    
         
            -
             
     | 
| 
       182 
     | 
    
         
            -
             
     | 
| 
       183 
     | 
    
         
            -
             
     | 
| 
       184 
     | 
    
         
            -
             
     | 
| 
       185 
     | 
    
         
            -
             
     | 
| 
       186 
     | 
    
         
            -
             
     | 
| 
       187 
     | 
    
         
            -
             
     | 
| 
       188 
     | 
    
         
            -
             
     | 
| 
       189 
     | 
    
         
            -
             
     | 
| 
       190 
     | 
    
         
            -
             
     | 
| 
       191 
     | 
    
         
            -
             
     | 
| 
       192 
     | 
    
         
            -
             
     | 
| 
       193 
     | 
    
         
            -
             
     | 
| 
       194 
     | 
    
         
            -
             
     | 
| 
       195 
     | 
    
         
            -
             
     | 
| 
       196 
     | 
    
         
            -
             
     | 
| 
       197 
     | 
    
         
            -
             
     | 
| 
       198 
     | 
    
         
            -
             
     | 
| 
       199 
     | 
    
         
            -
             
     | 
| 
       200 
     | 
    
         
            -
             
     | 
| 
       201 
     | 
    
         
            -
             
     | 
| 
       202 
     | 
    
         
            -
             
     | 
| 
       203 
     | 
    
         
            -
             
     | 
| 
       204 
     | 
    
         
            -
             
     | 
| 
       205 
     | 
    
         
            -
             
     | 
| 
       206 
     | 
    
         
            -
             
     | 
| 
       207 
     | 
    
         
            -
             
     | 
| 
       208 
     | 
    
         
            -
             
     | 
| 
       209 
     | 
    
         
            -
            #     if get_config().vad.is_vosk():
         
     | 
| 
       210 
     | 
    
         
            -
            #         if check_and_install("vosk"):
         
     | 
| 
       211 
     | 
    
         
            -
            #             from GameSentenceMiner.vad import vosk_helper
         
     | 
| 
       212 
     | 
    
         
            -
            #         else:
         
     | 
| 
       213 
     | 
    
         
            -
            #             logger.error("Vosk is enabled and vosk package could not be installed.")
         
     | 
| 
       214 
     | 
    
         
            -
            #
         
     | 
| 
       215 
     | 
    
         
            -
            #     return silero_trim, whisper_helper, vosk_helper
         
     | 
| 
      
 182 
     | 
    
         
            +
            def wait_for_stable_file(file_path, timeout=10, check_interval=0.1):
         
     | 
| 
      
 183 
     | 
    
         
            +
                elapsed_time = 0
         
     | 
| 
      
 184 
     | 
    
         
            +
                last_size = -1
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
                while elapsed_time < timeout:
         
     | 
| 
      
 187 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 188 
     | 
    
         
            +
                        current_size = os.path.getsize(file_path)
         
     | 
| 
      
 189 
     | 
    
         
            +
                        if current_size == last_size:
         
     | 
| 
      
 190 
     | 
    
         
            +
                            return True
         
     | 
| 
      
 191 
     | 
    
         
            +
                        last_size = current_size
         
     | 
| 
      
 192 
     | 
    
         
            +
                        time.sleep(check_interval)
         
     | 
| 
      
 193 
     | 
    
         
            +
                        elapsed_time += check_interval
         
     | 
| 
      
 194 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 195 
     | 
    
         
            +
                        logger.warning(f"Error checking file size, will still try updating Anki Card!: {e}")
         
     | 
| 
      
 196 
     | 
    
         
            +
                        return False
         
     | 
| 
      
 197 
     | 
    
         
            +
                logger.warning("File size did not stabilize within the timeout period. Continuing...")
         
     | 
| 
      
 198 
     | 
    
         
            +
                return False
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
      
 201 
     | 
    
         
            +
            def import_vad_models():
         
     | 
| 
      
 202 
     | 
    
         
            +
                silero_trim, whisper_helper, vosk_helper = None, None, None
         
     | 
| 
      
 203 
     | 
    
         
            +
                if get_config().vad.is_silero():
         
     | 
| 
      
 204 
     | 
    
         
            +
                    from GameSentenceMiner.vad import silero_trim
         
     | 
| 
      
 205 
     | 
    
         
            +
                if get_config().vad.is_whisper():
         
     | 
| 
      
 206 
     | 
    
         
            +
                    from GameSentenceMiner.vad import whisper_helper
         
     | 
| 
      
 207 
     | 
    
         
            +
                if get_config().vad.is_vosk():
         
     | 
| 
      
 208 
     | 
    
         
            +
                    from GameSentenceMiner.vad import vosk_helper
         
     | 
| 
      
 209 
     | 
    
         
            +
                return silero_trim, whisper_helper, vosk_helper
         
     | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            Metadata-Version: 2.4
         
     | 
| 
       2 
2 
     | 
    
         
             
            Name: GameSentenceMiner
         
     | 
| 
       3 
     | 
    
         
            -
            Version: 2.5. 
     | 
| 
      
 3 
     | 
    
         
            +
            Version: 2.5.12
         
     | 
| 
       4 
4 
     | 
    
         
             
            Summary: A tool for mining sentences from games. Update: Multi-Line Mining! Fixed!
         
     | 
| 
       5 
5 
     | 
    
         
             
            Author-email: Beangate <bpwhelan95@gmail.com>
         
     | 
| 
       6 
6 
     | 
    
         
             
            License: MIT License
         
     | 
| 
         @@ -97,7 +97,7 @@ steps before making an issue: 
     | 
|
| 
       97 
97 
     | 
    
         | 
| 
       98 
98 
     | 
    
         
             
            - Try Restarting OBS
         
     | 
| 
       99 
99 
     | 
    
         
             
            - Make sure your hook is the best you can find. (Preferably it gives you the text RIGHT when the voice line starts)
         
     | 
| 
       100 
     | 
    
         
            -
            - Try Adjusting Offset Configuration in  
     | 
| 
      
 100 
     | 
    
         
            +
            - Try Adjusting Offset Configuration in the config to better match your situation. (i.e. if the hook is late, add a
         
     | 
| 
       101 
101 
     | 
    
         
             
              negative beginning offset)
         
     | 
| 
       102 
102 
     | 
    
         
             
            - Try using "Trim beginning" in `VAD` settings.
         
     | 
| 
       103 
103 
     | 
    
         | 
| 
         @@ -1,15 +1,15 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         
     | 
| 
       2 
     | 
    
         
            -
            GameSentenceMiner/anki.py,sha256= 
     | 
| 
       3 
     | 
    
         
            -
            GameSentenceMiner/config_gui.py,sha256= 
     | 
| 
       4 
     | 
    
         
            -
            GameSentenceMiner/configuration.py,sha256= 
     | 
| 
      
 2 
     | 
    
         
            +
            GameSentenceMiner/anki.py,sha256=kMl-VvBkT_VtEjfTjliIHLGZwrJhHzTT1lRQ6qCdtYw,13062
         
     | 
| 
      
 3 
     | 
    
         
            +
            GameSentenceMiner/config_gui.py,sha256=9w7qXuGwt2lMTo4xXwuZKZyaLCoXBvzIDxSMrJzsUi8,60787
         
     | 
| 
      
 4 
     | 
    
         
            +
            GameSentenceMiner/configuration.py,sha256=u6Jm-Ox7PSZ1Cf5_AkV6hvQInayq7jXjLduf1eMK7Rc,20119
         
     | 
| 
       5 
5 
     | 
    
         
             
            GameSentenceMiner/ffmpeg.py,sha256=fzOxrn-a4KqFdUY2oove164CTDOSsdPQtzqRW5f1P7c,12002
         
     | 
| 
       6 
6 
     | 
    
         
             
            GameSentenceMiner/gametext.py,sha256=LORVdE2WEo1CDI8gonc7qxrhbS4KFKXFQVKjhlkpLbc,7368
         
     | 
| 
       7 
     | 
    
         
            -
            GameSentenceMiner/gsm.py,sha256= 
     | 
| 
      
 7 
     | 
    
         
            +
            GameSentenceMiner/gsm.py,sha256=H6kG2HerHhY37jXWGoQCLWjtzOoxAU_tAJ4RY9pAWbc,23970
         
     | 
| 
       8 
8 
     | 
    
         
             
            GameSentenceMiner/model.py,sha256=JdnkT4VoPOXmOpRgFdvERZ09c9wLN6tUJxdrKlGZcqo,5305
         
     | 
| 
       9 
9 
     | 
    
         
             
            GameSentenceMiner/notification.py,sha256=FY39ChSRK0Y8TQ6lBGsLnpZUFPtFpSy2tweeXVoV7kc,2809
         
     | 
| 
       10 
     | 
    
         
            -
            GameSentenceMiner/obs.py,sha256= 
     | 
| 
      
 10 
     | 
    
         
            +
            GameSentenceMiner/obs.py,sha256=MlxRToq5wALPI1XrD8rxEU-N8mWII91cNJWY7rUa5uI,7642
         
     | 
| 
       11 
11 
     | 
    
         
             
            GameSentenceMiner/package.py,sha256=YlS6QRMuVlm6mdXx0rlXv9_3erTGS21jaP3PNNWfAH0,1250
         
     | 
| 
       12 
     | 
    
         
            -
            GameSentenceMiner/util.py,sha256= 
     | 
| 
      
 12 
     | 
    
         
            +
            GameSentenceMiner/util.py,sha256=Awhy57vX4NgQzygqKaGQn2EJ75T0uiXlhmINFOWlQkU,6825
         
     | 
| 
       13 
13 
     | 
    
         
             
            GameSentenceMiner/utility_gui.py,sha256=H4aOddlsrVR768RwbMzYScCziuOz1JeySUigNrPlaac,7692
         
     | 
| 
       14 
14 
     | 
    
         
             
            GameSentenceMiner/communication/__init__.py,sha256=_jGn9PJxtOAOPtJ2rI-Qu9hEHVZVpIvWlxKvqk91_zI,638
         
     | 
| 
       15 
15 
     | 
    
         
             
            GameSentenceMiner/communication/send.py,sha256=oOJdCS6-LNX90amkRn5FL2xqx6THGm56zHR2ntVIFTE,229
         
     | 
| 
         @@ -21,9 +21,9 @@ GameSentenceMiner/vad/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h 
     | 
|
| 
       21 
21 
     | 
    
         
             
            GameSentenceMiner/vad/silero_trim.py,sha256=-thDIZLuTLra3YBj7WR16Z6JeDgSpge2YuahprBvD8I,1585
         
     | 
| 
       22 
22 
     | 
    
         
             
            GameSentenceMiner/vad/vosk_helper.py,sha256=BI_mg_qyrjNbuEJjXSUDoV0FWEtQtEOAPmrrNixnZ_8,5974
         
     | 
| 
       23 
23 
     | 
    
         
             
            GameSentenceMiner/vad/whisper_helper.py,sha256=OF4J8TPPoKPJR1uFwrWAZ2Q7v0HJkVvNGmF8l1tACX0,3447
         
     | 
| 
       24 
     | 
    
         
            -
            gamesentenceminer-2.5. 
     | 
| 
       25 
     | 
    
         
            -
            gamesentenceminer-2.5. 
     | 
| 
       26 
     | 
    
         
            -
            gamesentenceminer-2.5. 
     | 
| 
       27 
     | 
    
         
            -
            gamesentenceminer-2.5. 
     | 
| 
       28 
     | 
    
         
            -
            gamesentenceminer-2.5. 
     | 
| 
       29 
     | 
    
         
            -
            gamesentenceminer-2.5. 
     | 
| 
      
 24 
     | 
    
         
            +
            gamesentenceminer-2.5.12.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
         
     | 
| 
      
 25 
     | 
    
         
            +
            gamesentenceminer-2.5.12.dist-info/METADATA,sha256=iibLRrqHFjwy-4U4_j1JhHB3USB-W8CR0AMpW_JneF0,5433
         
     | 
| 
      
 26 
     | 
    
         
            +
            gamesentenceminer-2.5.12.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
         
     | 
| 
      
 27 
     | 
    
         
            +
            gamesentenceminer-2.5.12.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
         
     | 
| 
      
 28 
     | 
    
         
            +
            gamesentenceminer-2.5.12.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
         
     | 
| 
      
 29 
     | 
    
         
            +
            gamesentenceminer-2.5.12.dist-info/RECORD,,
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     |