GameSentenceMiner 2.1.2.post1__py3-none-any.whl → 2.2.1__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-2.2.1.dist-info/METADATA +229 -0
- GameSentenceMiner-2.2.1.dist-info/RECORD +24 -0
- GameSentenceMiner-2.2.1.dist-info/entry_points.txt +2 -0
- GameSentenceMiner-2.2.1.dist-info/top_level.txt +1 -0
- {GameSentenceMiner → src}/anki.py +12 -15
- {GameSentenceMiner → src}/config_gui.py +3 -4
- {GameSentenceMiner → src}/configuration.py +6 -8
- src/downloader/Untitled_json.py +472 -0
- src/downloader/download_tools.py +149 -0
- {GameSentenceMiner → src}/ffmpeg.py +20 -13
- {GameSentenceMiner → src}/gametext.py +6 -6
- {GameSentenceMiner → src}/gsm.py +86 -57
- {GameSentenceMiner → src}/notification.py +5 -3
- {GameSentenceMiner → src}/obs.py +48 -16
- {GameSentenceMiner → src}/package_updater.py +1 -1
- {GameSentenceMiner → src}/util.py +11 -6
- src/vad/__init__.py +0 -0
- {GameSentenceMiner → src}/vad/silero_trim.py +2 -2
- {GameSentenceMiner → src}/vad/vosk_helper.py +2 -2
- {GameSentenceMiner → src}/vad/whisper_helper.py +2 -2
- GameSentenceMiner-2.1.2.post1.dist-info/METADATA +0 -352
- GameSentenceMiner-2.1.2.post1.dist-info/RECORD +0 -21
- GameSentenceMiner-2.1.2.post1.dist-info/entry_points.txt +0 -2
- GameSentenceMiner-2.1.2.post1.dist-info/top_level.txt +0 -1
- {GameSentenceMiner-2.1.2.post1.dist-info → GameSentenceMiner-2.2.1.dist-info}/WHEEL +0 -0
- {GameSentenceMiner → src}/__init__.py +0 -0
- {GameSentenceMiner/vad → src/downloader}/__init__.py +0 -0
- {GameSentenceMiner → src}/model.py +0 -0
@@ -1,19 +1,24 @@
|
|
1
|
+
import subprocess
|
1
2
|
import tempfile
|
2
3
|
import time
|
3
4
|
|
4
|
-
from
|
5
|
-
from . import
|
6
|
-
from . import
|
7
|
-
from .configuration import *
|
8
|
-
from .util import *
|
5
|
+
from src import obs, util, configuration
|
6
|
+
from src.configuration import *
|
7
|
+
from src.util import *
|
9
8
|
|
10
|
-
|
9
|
+
def get_ffmpeg_path():
|
10
|
+
return os.path.join(get_app_directory(), "ffmpeg", "ffmpeg.exe") if util.is_windows() else "ffmpeg"
|
11
|
+
|
12
|
+
def get_ffprobe_path():
|
13
|
+
return os.path.join(get_app_directory(), "ffmpeg", "ffprobe.exe") if util.is_windows() else "ffprobe"
|
14
|
+
|
15
|
+
ffmpeg_base_command_list = [get_ffmpeg_path(), "-hide_banner", "-loglevel", "error", '-nostdin']
|
11
16
|
|
12
17
|
|
13
18
|
def get_screenshot(video_file, time_from_end):
|
14
19
|
time_from_end_to_capture = -time_from_end if time_from_end else -1
|
15
|
-
output_image = make_unique_file_name(
|
16
|
-
get_config().paths.screenshot_destination
|
20
|
+
output_image = make_unique_file_name(os.path.join(
|
21
|
+
get_config().paths.screenshot_destination, f"{obs.get_current_game(sanitize=True)}.{get_config().screenshot.extension}"))
|
17
22
|
# FFmpeg command to extract the last frame of the video
|
18
23
|
ffmpeg_command = ffmpeg_base_command_list + [
|
19
24
|
"-sseof", f"{time_from_end_to_capture}", # Seek to 1 second before the end of the video
|
@@ -59,7 +64,7 @@ def get_screenshot_time(video_path, line_time):
|
|
59
64
|
|
60
65
|
def process_image(image_file):
|
61
66
|
output_image = make_unique_file_name(
|
62
|
-
get_config().paths.screenshot_destination
|
67
|
+
os.path.join(get_config().paths.screenshot_destination, f"{obs.get_current_game(sanitize=True)}.{get_config().screenshot.extension}"))
|
63
68
|
|
64
69
|
# FFmpeg command to process the input image
|
65
70
|
ffmpeg_command = ffmpeg_base_command_list + [
|
@@ -88,7 +93,7 @@ def process_image(image_file):
|
|
88
93
|
|
89
94
|
def get_audio_codec(video_path):
|
90
95
|
command = [
|
91
|
-
"
|
96
|
+
f"{get_ffprobe_path()}",
|
92
97
|
"-v", "error",
|
93
98
|
"-select_streams", "a:0",
|
94
99
|
"-show_entries", "stream=codec_name",
|
@@ -112,7 +117,7 @@ def get_audio_codec(video_path):
|
|
112
117
|
|
113
118
|
def get_audio_and_trim(video_path, line_time, next_line_time):
|
114
119
|
supported_formats = {
|
115
|
-
'opus': '
|
120
|
+
'opus': 'libopus',
|
116
121
|
'mp3': 'libmp3lame',
|
117
122
|
'ogg': 'libvorbis',
|
118
123
|
'aac': 'aac',
|
@@ -139,7 +144,7 @@ def get_audio_and_trim(video_path, line_time, next_line_time):
|
|
139
144
|
|
140
145
|
# FFmpeg command to extract OR re-encode the audio
|
141
146
|
# command = f"{ffmpeg_base_command} -i \"{video_path}\" -map 0:a {codec_command} \"{untrimmed_audio}\""
|
142
|
-
|
147
|
+
logger.debug("Doing initial audio extraction")
|
143
148
|
logger.debug(" ".join(command))
|
144
149
|
|
145
150
|
subprocess.run(command)
|
@@ -149,12 +154,13 @@ def get_audio_and_trim(video_path, line_time, next_line_time):
|
|
149
154
|
|
150
155
|
def get_video_duration(file_path):
|
151
156
|
ffprobe_command = [
|
152
|
-
"
|
157
|
+
f"{get_ffprobe_path()}",
|
153
158
|
"-v", "error",
|
154
159
|
"-show_entries", "format=duration",
|
155
160
|
"-of", "json",
|
156
161
|
file_path
|
157
162
|
]
|
163
|
+
logger.debug(" ".join(ffprobe_command))
|
158
164
|
result = subprocess.run(ffprobe_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
159
165
|
duration_info = json.loads(result.stdout)
|
160
166
|
return float(duration_info["format"]["duration"]) # Return the duration in seconds
|
@@ -295,3 +301,4 @@ def is_video_big_enough(file_path, min_size_kb=250):
|
|
295
301
|
except Exception as e:
|
296
302
|
logger.error(f"Error: {e}")
|
297
303
|
return False
|
304
|
+
|
@@ -7,10 +7,10 @@ from datetime import datetime
|
|
7
7
|
import pyperclip
|
8
8
|
import websockets
|
9
9
|
|
10
|
-
from
|
11
|
-
from .configuration import *
|
12
|
-
from .configuration import get_config, logger
|
13
|
-
from .util import remove_html_tags
|
10
|
+
from src import util
|
11
|
+
from src.configuration import *
|
12
|
+
from src.configuration import get_config, logger
|
13
|
+
from src.util import remove_html_tags
|
14
14
|
from difflib import SequenceMatcher
|
15
15
|
|
16
16
|
|
@@ -51,7 +51,7 @@ async def listen_websocket():
|
|
51
51
|
try:
|
52
52
|
async with websockets.connect(f'ws://{get_config().general.websocket_uri}', ping_interval=None) as websocket:
|
53
53
|
if reconnecting:
|
54
|
-
|
54
|
+
logger.info(f"Texthooker WebSocket connected Successfully!")
|
55
55
|
reconnecting = False
|
56
56
|
while True:
|
57
57
|
message = await websocket.recv()
|
@@ -71,7 +71,7 @@ async def listen_websocket():
|
|
71
71
|
|
72
72
|
except (websockets.ConnectionClosed, ConnectionError) as e:
|
73
73
|
if not reconnecting:
|
74
|
-
|
74
|
+
logger.warning(f"Texthooker WebSocket connection lost: {e}. Attempting to Reconnect...")
|
75
75
|
reconnecting = True
|
76
76
|
await asyncio.sleep(5)
|
77
77
|
|
{GameSentenceMiner → src}/gsm.py
RENAMED
@@ -1,8 +1,8 @@
|
|
1
|
-
import shutil
|
2
1
|
import signal
|
2
|
+
import subprocess
|
3
3
|
import sys
|
4
|
-
import tempfile
|
5
4
|
import time
|
5
|
+
from subprocess import Popen
|
6
6
|
|
7
7
|
import keyboard
|
8
8
|
import psutil
|
@@ -12,21 +12,23 @@ from pystray import Icon, Menu, MenuItem
|
|
12
12
|
from watchdog.events import FileSystemEventHandler
|
13
13
|
from watchdog.observers import Observer
|
14
14
|
|
15
|
-
from
|
16
|
-
from
|
17
|
-
from
|
18
|
-
from
|
19
|
-
from
|
20
|
-
from
|
21
|
-
from
|
22
|
-
from
|
23
|
-
from .
|
24
|
-
from .
|
25
|
-
from .
|
26
|
-
from .
|
27
|
-
from .
|
28
|
-
|
29
|
-
|
15
|
+
from src import anki
|
16
|
+
from src import config_gui
|
17
|
+
from src import configuration
|
18
|
+
from src import ffmpeg
|
19
|
+
from src import gametext
|
20
|
+
from src import notification
|
21
|
+
from src import obs
|
22
|
+
from src import util
|
23
|
+
from src.downloader.download_tools import download_obs_if_needed, download_ffmpeg_if_needed
|
24
|
+
from src.vad import vosk_helper, silero_trim, whisper_helper
|
25
|
+
from src.configuration import *
|
26
|
+
from src.ffmpeg import get_audio_and_trim
|
27
|
+
from src.gametext import get_line_timing
|
28
|
+
from src.util import *
|
29
|
+
|
30
|
+
obs_process: Popen
|
31
|
+
procs_to_close = []
|
30
32
|
settings_window: config_gui.ConfigApp = None
|
31
33
|
obs_paused = False
|
32
34
|
icon: Icon
|
@@ -35,7 +37,7 @@ menu: Menu
|
|
35
37
|
|
36
38
|
class VideoToAudioHandler(FileSystemEventHandler):
|
37
39
|
def on_created(self, event):
|
38
|
-
if event.is_directory or "Replay" not in event.src_path:
|
40
|
+
if event.is_directory or ("Replay" not in event.src_path and "GSM" not in event.src_path):
|
39
41
|
return
|
40
42
|
if event.src_path.endswith(".mkv") or event.src_path.endswith(".mp4"): # Adjust based on your OBS output format
|
41
43
|
logger.info(f"MKV {event.src_path} FOUND, RUNNING LOGIC")
|
@@ -46,14 +48,19 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
46
48
|
def convert_to_audio(video_path):
|
47
49
|
try:
|
48
50
|
with util.lock:
|
51
|
+
if os.path.exists(video_path) and os.access(video_path, os.R_OK):
|
52
|
+
logger.debug(f"Video found and is readable: {video_path}")
|
53
|
+
|
49
54
|
if get_config().obs.minimum_replay_size and not ffmpeg.is_video_big_enough(video_path,
|
50
55
|
get_config().obs.minimum_replay_size):
|
56
|
+
logger.debug("Checking if video is big enough")
|
51
57
|
notification.send_check_obs_notification(reason="Video may be empty, check scene in OBS.")
|
52
58
|
logger.error(
|
53
59
|
f"Video was unusually small, potentially empty! Check OBS for Correct Scene Settings! Path: {video_path}")
|
54
60
|
return
|
55
61
|
util.use_previous_audio = True
|
56
62
|
last_note = None
|
63
|
+
logger.debug("Attempting to get last anki card")
|
57
64
|
if get_config().anki.update_anki:
|
58
65
|
last_note = anki.get_last_anki_card()
|
59
66
|
if get_config().features.backfill_audio:
|
@@ -70,6 +77,7 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
70
77
|
tango = last_note['fields'][get_config().anki.word_field]['value'] if last_note else ''
|
71
78
|
|
72
79
|
if get_config().anki.sentence_audio_field:
|
80
|
+
logger.debug("Attempting to get audio from video")
|
73
81
|
final_audio_output, should_update_audio, vad_trimmed_audio = VideoToAudioHandler.get_audio(
|
74
82
|
line_time,
|
75
83
|
next_line_time,
|
@@ -92,10 +100,9 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
92
100
|
except Exception as e:
|
93
101
|
logger.error(f"Card failed to update! Maybe it was removed? {e}")
|
94
102
|
except FileNotFoundError as f:
|
95
|
-
|
96
|
-
print("Something went wrong with processing, anki card not updated")
|
103
|
+
logger.error("Something went wrong with processing, anki card not updated")
|
97
104
|
except Exception as e:
|
98
|
-
logger.error(f"Some error was hit catching to allow further work to be done: {e}")
|
105
|
+
logger.error(f"Some error was hit catching to allow further work to be done: {e}", exc_info=1)
|
99
106
|
if get_config().paths.remove_video and os.path.exists(video_path):
|
100
107
|
os.remove(video_path) # Optionally remove the video after conversion
|
101
108
|
if get_config().paths.remove_audio and os.path.exists(vad_trimmed_audio):
|
@@ -106,8 +113,7 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
106
113
|
trimmed_audio = get_audio_and_trim(video_path, line_time, next_line_time)
|
107
114
|
vad_trimmed_audio = make_unique_file_name(
|
108
115
|
f"{os.path.abspath(configuration.get_temporary_directory())}/{obs.get_current_game(sanitize=True)}.{get_config().audio.extension}")
|
109
|
-
final_audio_output = make_unique_file_name(
|
110
|
-
f"{get_config().paths.audio_destination}{obs.get_current_game(sanitize=True)}.{get_config().audio.extension}")
|
116
|
+
final_audio_output = make_unique_file_name(os.path.join(get_config().paths.audio_destination, f"{obs.get_current_game(sanitize=True)}.{get_config().audio.extension}"))
|
111
117
|
should_update_audio = True
|
112
118
|
if get_config().vad.do_vad_postprocessing:
|
113
119
|
match get_config().vad.selected_vad_model:
|
@@ -139,26 +145,33 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
139
145
|
|
140
146
|
|
141
147
|
def initialize(reloading=False):
|
148
|
+
global obs_process
|
142
149
|
if not reloading:
|
150
|
+
if is_windows():
|
151
|
+
download_obs_if_needed()
|
152
|
+
download_ffmpeg_if_needed()
|
143
153
|
if get_config().obs.enabled:
|
154
|
+
obs_process = obs.start_obs()
|
144
155
|
obs.connect_to_obs(start_replay=True)
|
145
156
|
anki.start_monitoring_anki()
|
146
|
-
if get_config().general.open_config_on_startup:
|
147
|
-
proc = subprocess.Popen([sys.executable, "config_gui.py"])
|
148
|
-
config_pids.append(proc.pid)
|
149
157
|
gametext.start_text_monitor()
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
os.mkdir(get_config().paths.screenshot_destination)
|
154
|
-
if not os.path.exists(get_config().paths.audio_destination):
|
155
|
-
os.mkdir(get_config().paths.audio_destination)
|
158
|
+
os.makedirs(get_config().paths.folder_to_watch, exist_ok=True)
|
159
|
+
os.makedirs(get_config().paths.screenshot_destination, exist_ok=True)
|
160
|
+
os.makedirs(get_config().paths.audio_destination, exist_ok=True)
|
156
161
|
if get_config().vad.do_vad_postprocessing:
|
157
162
|
if VOSK in (get_config().vad.backup_vad_model, get_config().vad.selected_vad_model):
|
158
163
|
vosk_helper.get_vosk_model()
|
159
164
|
if WHISPER in (get_config().vad.backup_vad_model, get_config().vad.selected_vad_model):
|
160
165
|
whisper_helper.initialize_whisper_model()
|
161
166
|
|
167
|
+
def initial_checks():
|
168
|
+
try:
|
169
|
+
subprocess.run(ffmpeg.ffmpeg_base_command_list)
|
170
|
+
logger.debug("FFMPEG is installed and accessible.")
|
171
|
+
except FileNotFoundError:
|
172
|
+
logger.error("FFmpeg not found, please install it and add it to your PATH.")
|
173
|
+
raise
|
174
|
+
|
162
175
|
|
163
176
|
def register_hotkeys():
|
164
177
|
keyboard.add_hotkey(get_config().hotkeys.reset_line, gametext.reset_line_hotkey_pressed)
|
@@ -216,9 +229,9 @@ def open_settings():
|
|
216
229
|
def open_log():
|
217
230
|
"""Function to handle opening log."""
|
218
231
|
"""Open log file with the default application."""
|
219
|
-
log_file_path =
|
232
|
+
log_file_path = get_log_path()
|
220
233
|
if not os.path.exists(log_file_path):
|
221
|
-
|
234
|
+
logger.error("Log file not found!")
|
222
235
|
return
|
223
236
|
|
224
237
|
if sys.platform.startswith("win"): # Windows
|
@@ -228,30 +241,22 @@ def open_log():
|
|
228
241
|
elif sys.platform.startswith("linux"): # Linux
|
229
242
|
subprocess.call(["xdg-open", log_file_path])
|
230
243
|
else:
|
231
|
-
|
232
|
-
|
244
|
+
logger.error("Unsupported platform!")
|
245
|
+
logger.info("Log opened.")
|
233
246
|
|
234
247
|
|
235
248
|
def exit_program(icon, item):
|
236
249
|
"""Exit the application."""
|
237
|
-
|
250
|
+
logger.info("Exiting...")
|
238
251
|
icon.stop()
|
239
252
|
cleanup()
|
240
253
|
|
241
254
|
|
242
255
|
def play_pause(icon, item):
|
243
256
|
global obs_paused, menu
|
244
|
-
|
245
|
-
obs.start_replay_buffer()
|
246
|
-
else:
|
247
|
-
obs.stop_replay_buffer()
|
248
|
-
|
249
|
-
obs_paused = not obs_paused
|
257
|
+
obs.toggle_replay_buffer()
|
250
258
|
update_icon()
|
251
259
|
|
252
|
-
def get_obs_icon_text():
|
253
|
-
return "Pause OBS" if obs_paused else "Resume OBS"
|
254
|
-
|
255
260
|
|
256
261
|
def update_icon():
|
257
262
|
global menu, icon
|
@@ -264,7 +269,8 @@ def update_icon():
|
|
264
269
|
menu = Menu(
|
265
270
|
MenuItem("Open Settings", open_settings),
|
266
271
|
MenuItem("Open Log", open_log),
|
267
|
-
MenuItem(
|
272
|
+
MenuItem("Toggle Replay Buffer", play_pause),
|
273
|
+
MenuItem("Restart OBS", restart_obs),
|
268
274
|
MenuItem("Switch Profile", profile_menu),
|
269
275
|
MenuItem("Exit", exit_program)
|
270
276
|
)
|
@@ -295,7 +301,8 @@ def run_tray():
|
|
295
301
|
menu = Menu(
|
296
302
|
MenuItem("Open Settings", open_settings),
|
297
303
|
MenuItem("Open Log", open_log),
|
298
|
-
MenuItem(
|
304
|
+
MenuItem("Toggle Replay Buffer", play_pause),
|
305
|
+
MenuItem("Restart OBS", restart_obs),
|
299
306
|
MenuItem("Switch Profile", profile_menu),
|
300
307
|
MenuItem("Exit", exit_program)
|
301
308
|
)
|
@@ -303,24 +310,43 @@ def run_tray():
|
|
303
310
|
icon = Icon("TrayApp", create_image(), "Game Sentence Miner", menu)
|
304
311
|
icon.run()
|
305
312
|
|
313
|
+
def close_obs():
|
314
|
+
if obs_process:
|
315
|
+
logger.info("Closing OBS")
|
316
|
+
obs_process.terminate()
|
317
|
+
obs_process.wait()
|
318
|
+
|
319
|
+
def restart_obs():
|
320
|
+
global obs_process
|
321
|
+
close_obs()
|
322
|
+
time.sleep(2)
|
323
|
+
obs_process = obs.start_obs()
|
324
|
+
obs.connect_to_obs(start_replay=True)
|
306
325
|
|
307
326
|
def cleanup():
|
308
327
|
logger.info("Performing cleanup...")
|
309
328
|
util.keep_running = False
|
310
329
|
|
311
|
-
|
330
|
+
if get_config().obs.enabled:
|
331
|
+
if get_config().obs.start_buffer:
|
332
|
+
obs.stop_replay_buffer()
|
333
|
+
obs.disconnect_from_obs()
|
334
|
+
close_obs()
|
335
|
+
|
336
|
+
proc: Popen
|
337
|
+
for proc in procs_to_close:
|
312
338
|
try:
|
313
|
-
|
314
|
-
|
339
|
+
logger.info(f"Terminating process {proc.args[0]}")
|
340
|
+
proc.terminate()
|
341
|
+
proc.wait() # Wait for OBS to fully close
|
342
|
+
logger.info(f"Process {proc.args[0]} terminated.")
|
315
343
|
except psutil.NoSuchProcess:
|
316
|
-
logger.info("
|
344
|
+
logger.info("PID already closed.")
|
317
345
|
except Exception as e:
|
318
|
-
|
346
|
+
proc.kill()
|
347
|
+
logger.error(f"Error terminating process {proc}: {e}")
|
348
|
+
|
319
349
|
|
320
|
-
if get_config().obs.enabled:
|
321
|
-
if get_config().obs.start_buffer:
|
322
|
-
obs.stop_replay_buffer()
|
323
|
-
obs.disconnect_from_obs()
|
324
350
|
settings_window.window.destroy()
|
325
351
|
logger.info("Cleanup complete.")
|
326
352
|
|
@@ -340,6 +366,7 @@ def main(reloading=False, do_config_input=True):
|
|
340
366
|
global settings_window
|
341
367
|
logger.info("Script started.")
|
342
368
|
initialize(reloading)
|
369
|
+
initial_checks()
|
343
370
|
event_handler = VideoToAudioHandler()
|
344
371
|
observer = Observer()
|
345
372
|
observer.schedule(event_handler, get_config().paths.folder_to_watch, recursive=False)
|
@@ -360,6 +387,8 @@ def main(reloading=False, do_config_input=True):
|
|
360
387
|
settings_window = config_gui.ConfigApp()
|
361
388
|
if get_config().general.check_for_update_on_startup:
|
362
389
|
settings_window.window.after(0, settings_window.check_update)
|
390
|
+
if get_config().general.open_config_on_startup:
|
391
|
+
settings_window.window.after(0, settings_window.show)
|
363
392
|
settings_window.add_save_hook(update_icon)
|
364
393
|
settings_window.window.mainloop()
|
365
394
|
except KeyboardInterrupt:
|
@@ -1,6 +1,8 @@
|
|
1
1
|
import requests
|
2
2
|
from plyer import notification
|
3
3
|
|
4
|
+
from src.configuration import logger
|
5
|
+
|
4
6
|
|
5
7
|
def open_anki_card(note_id):
|
6
8
|
url = "http://localhost:8765"
|
@@ -17,11 +19,11 @@ def open_anki_card(note_id):
|
|
17
19
|
try:
|
18
20
|
response = requests.post(url, json=data, headers=headers)
|
19
21
|
if response.status_code == 200:
|
20
|
-
|
22
|
+
logger.info(f"Opened Anki note with ID {note_id}")
|
21
23
|
else:
|
22
|
-
|
24
|
+
logger.error(f"Failed to open Anki note with ID {note_id}")
|
23
25
|
except Exception as e:
|
24
|
-
|
26
|
+
logger.info(f"Error connecting to AnkiConnect: {e}")
|
25
27
|
|
26
28
|
|
27
29
|
def send_notification(tango):
|
{GameSentenceMiner → src}/obs.py
RENAMED
@@ -1,18 +1,37 @@
|
|
1
|
+
import subprocess
|
1
2
|
import time
|
2
|
-
from sys import platform
|
3
3
|
|
4
4
|
from obswebsocket import obsws, requests
|
5
5
|
|
6
|
-
from
|
7
|
-
from . import
|
8
|
-
from .
|
9
|
-
from .model import *
|
6
|
+
from src import util, configuration
|
7
|
+
from src.configuration import *
|
8
|
+
from src.model import *
|
10
9
|
|
11
10
|
client: obsws = None
|
12
11
|
|
13
12
|
# REFERENCE: https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md
|
14
13
|
|
15
14
|
|
15
|
+
def get_obs_path():
|
16
|
+
return os.path.join(configuration.get_app_directory(), 'obs-studio/bin/64bit/obs64.exe')
|
17
|
+
|
18
|
+
def start_obs():
|
19
|
+
obs_path = get_obs_path()
|
20
|
+
if not os.path.exists(obs_path):
|
21
|
+
logger.error(f"OBS not found at {obs_path}. Please install OBS.")
|
22
|
+
return None
|
23
|
+
|
24
|
+
try:
|
25
|
+
# process = subprocess.Popen([obs_path], cwd=os.path.dirname(obs_path))
|
26
|
+
# process = subprocess.Popen([obs_path, '--minimize-to-tray'], cwd=os.path.dirname(obs_path))
|
27
|
+
process = subprocess.Popen([obs_path, '--disable-shutdown-check'], cwd=os.path.dirname(obs_path))
|
28
|
+
logger.info("OBS launched")
|
29
|
+
return process
|
30
|
+
except Exception as e:
|
31
|
+
logger.error(f"Error launching OBS: {e}")
|
32
|
+
return None
|
33
|
+
|
34
|
+
|
16
35
|
def get_obs_websocket_config_values():
|
17
36
|
if platform == "win32":
|
18
37
|
config_path = os.path.expanduser(r"~\AppData\Roaming\obs-studio\plugin_config\obs-websocket\config.json")
|
@@ -89,30 +108,43 @@ def disconnect_from_obs():
|
|
89
108
|
logger.info("Disconnected from OBS WebSocket.")
|
90
109
|
|
91
110
|
|
111
|
+
def toggle_replay_buffer():
|
112
|
+
try:
|
113
|
+
client.call(requests.ToggleReplayBuffer())
|
114
|
+
logger.info("Replay buffer Toggled.")
|
115
|
+
except Exception as e:
|
116
|
+
logger.error(f"Error toggling buffer: {e}")
|
117
|
+
|
118
|
+
|
92
119
|
# Start replay buffer
|
93
120
|
def start_replay_buffer():
|
94
121
|
try:
|
95
122
|
client.call(requests.GetVersion())
|
96
123
|
client.call(requests.StartReplayBuffer())
|
97
124
|
except Exception as e:
|
98
|
-
|
125
|
+
logger.error(f"Error starting replay buffer: {e}")
|
99
126
|
|
100
127
|
|
101
128
|
# Stop replay buffer
|
102
129
|
def stop_replay_buffer():
|
103
130
|
try:
|
104
131
|
client.call(requests.StopReplayBuffer())
|
105
|
-
|
132
|
+
logger.error("Replay buffer stopped.")
|
106
133
|
except Exception as e:
|
107
|
-
|
134
|
+
logger.error(f"Error stopping replay buffer: {e}")
|
108
135
|
|
109
136
|
|
110
137
|
# Save the current replay buffer
|
111
138
|
def save_replay_buffer():
|
112
139
|
try:
|
113
|
-
client.call(requests.
|
140
|
+
replay_buffer_started = client.call(requests.GetReplayBufferStatus()).datain['outputActive']
|
141
|
+
if replay_buffer_started:
|
142
|
+
client.call(requests.SaveReplayBuffer())
|
143
|
+
logger.info("Replay buffer saved.")
|
144
|
+
else:
|
145
|
+
logger.error("Replay Buffer is not active, could not save Replay Buffer!")
|
114
146
|
except Exception as e:
|
115
|
-
|
147
|
+
logger.error(f"Error saving replay buffer: {e}")
|
116
148
|
|
117
149
|
|
118
150
|
def get_current_scene():
|
@@ -121,7 +153,7 @@ def get_current_scene():
|
|
121
153
|
scene_info = SceneInfo.from_dict(response.datain)
|
122
154
|
return scene_info.sceneName
|
123
155
|
except Exception as e:
|
124
|
-
|
156
|
+
logger.error(f"Couldn't get scene: {e}")
|
125
157
|
return ''
|
126
158
|
|
127
159
|
|
@@ -129,27 +161,27 @@ def get_source_from_scene(scene_name):
|
|
129
161
|
try:
|
130
162
|
response = client.call(requests.GetSceneItemList(sceneName=scene_name))
|
131
163
|
scene_list = SceneItemsResponse.from_dict(response.datain)
|
132
|
-
print(scene_list)
|
133
164
|
return scene_list.sceneItems[0]
|
134
165
|
except Exception as e:
|
135
|
-
|
166
|
+
logger.error(f"Error getting source from scene: {e}")
|
136
167
|
return ''
|
137
168
|
|
138
169
|
|
139
170
|
def get_screenshot():
|
140
171
|
try:
|
141
|
-
screenshot = util.make_unique_file_name(os.path.abspath(
|
172
|
+
screenshot = util.make_unique_file_name(os.path.abspath(
|
173
|
+
configuration.get_temporary_directory()) + '/screenshot.png')
|
142
174
|
update_current_game()
|
143
175
|
current_source = get_source_from_scene(get_current_game())
|
144
176
|
current_source_name = current_source.sourceName
|
145
177
|
if not current_source_name:
|
146
|
-
|
178
|
+
logger.error("No active scene found.")
|
147
179
|
return
|
148
180
|
client.call(
|
149
181
|
requests.SaveSourceScreenshot(sourceName=current_source_name, imageFormat='png', imageFilePath=screenshot))
|
150
182
|
return screenshot
|
151
183
|
except Exception as e:
|
152
|
-
|
184
|
+
logger.error(f"Error getting screenshot: {e}")
|
153
185
|
|
154
186
|
|
155
187
|
def update_current_game():
|
@@ -5,7 +5,7 @@ import sys
|
|
5
5
|
import pyperclip
|
6
6
|
import requests
|
7
7
|
|
8
|
-
from .configuration import logger, get_app_directory
|
8
|
+
from src.configuration import logger, get_app_directory
|
9
9
|
|
10
10
|
PACKAGE_NAME = "GameSentenceMiner"
|
11
11
|
VERSION_FILE_PATH = os.path.join(get_app_directory(), 'version.txt')
|
@@ -9,6 +9,8 @@ from sys import platform
|
|
9
9
|
|
10
10
|
from rapidfuzz import process
|
11
11
|
|
12
|
+
from src.configuration import logger
|
13
|
+
|
12
14
|
SCRIPTS_DIR = r"E:\Japanese Stuff\agent-v0.1.4-win32-x64\data\scripts"
|
13
15
|
|
14
16
|
# Global variables to control script execution
|
@@ -56,7 +58,7 @@ def get_file_modification_time(file_path):
|
|
56
58
|
def get_process_id_by_title(game_title):
|
57
59
|
powershell_command = f"Get-Process | Where-Object {{$_.MainWindowTitle -like '*{game_title}*'}} | Select-Object -First 1 -ExpandProperty Id"
|
58
60
|
process_id = subprocess.check_output(["powershell", "-Command", powershell_command], text=True).strip()
|
59
|
-
|
61
|
+
logger.info(f"Process ID for {game_title}: {process_id}")
|
60
62
|
return process_id
|
61
63
|
|
62
64
|
|
@@ -101,21 +103,21 @@ def find_script_for_game(game_title):
|
|
101
103
|
best_script, matched_game_name, confidence = find_most_similar_script(game_title, steam_scripts)
|
102
104
|
|
103
105
|
if best_script:
|
104
|
-
|
106
|
+
logger.info(f"Found Script: {best_script}")
|
105
107
|
return best_script
|
106
108
|
else:
|
107
|
-
|
109
|
+
logger.warning("No similar script found.")
|
108
110
|
|
109
111
|
|
110
112
|
def run_agent_and_hook(pname, agent_script):
|
111
113
|
command = f'agent --script=\"{agent_script}\" --pname={pname}'
|
112
|
-
|
114
|
+
logger.info("Running and Hooking Agent!")
|
113
115
|
try:
|
114
116
|
dos_process = subprocess.Popen(command, shell=True)
|
115
117
|
dos_process.wait() # Wait for the process to complete
|
116
|
-
|
118
|
+
logger.info("Agent script finished or closed.")
|
117
119
|
except Exception as e:
|
118
|
-
|
120
|
+
logger.error(f"Error occurred while running agent script: {e}")
|
119
121
|
|
120
122
|
keep_running = False
|
121
123
|
|
@@ -123,6 +125,9 @@ def run_agent_and_hook(pname, agent_script):
|
|
123
125
|
def is_linux():
|
124
126
|
return platform == 'linux'
|
125
127
|
|
128
|
+
def is_windows():
|
129
|
+
return platform == 'win32'
|
130
|
+
|
126
131
|
# def run_command(command, shell=False, input=None, capture_output=False, timeout=None, check=False, **kwargs):
|
127
132
|
# # Use shell=True if the OS is Linux, otherwise shell=False
|
128
133
|
# if is_linux():
|
src/vad/__init__.py
ADDED
File without changes
|
@@ -2,8 +2,8 @@ import tempfile
|
|
2
2
|
|
3
3
|
from silero_vad import load_silero_vad, read_audio, get_speech_timestamps
|
4
4
|
|
5
|
-
from
|
6
|
-
from
|
5
|
+
from src import configuration, ffmpeg
|
6
|
+
from src.configuration import *
|
7
7
|
|
8
8
|
# Silero VAD setup
|
9
9
|
vad_model = load_silero_vad()
|
@@ -7,8 +7,8 @@ import requests
|
|
7
7
|
import soundfile as sf
|
8
8
|
import vosk
|
9
9
|
|
10
|
-
from
|
11
|
-
from
|
10
|
+
from src import configuration, ffmpeg
|
11
|
+
from src.configuration import *
|
12
12
|
|
13
13
|
ffmpeg_base_command_list = ["ffmpeg", "-hide_banner", "-loglevel", "error"]
|
14
14
|
vosk.SetLogLevel(-1)
|
@@ -4,8 +4,8 @@ import warnings
|
|
4
4
|
import stable_whisper as whisper
|
5
5
|
from stable_whisper import WhisperResult
|
6
6
|
|
7
|
-
from
|
8
|
-
from
|
7
|
+
from src import configuration, ffmpeg
|
8
|
+
from src.configuration import *
|
9
9
|
|
10
10
|
ffmpeg_base_command_list = ["ffmpeg", "-hide_banner", "-loglevel", "error"]
|
11
11
|
whisper_model = None
|