GameSentenceMiner 2.9.2__py3-none-any.whl → 2.9.3__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.
@@ -39,6 +39,13 @@ DEFAULT_CONFIG = 'Default'
39
39
 
40
40
  current_game = ''
41
41
 
42
+ def is_linux():
43
+ return platform == 'linux'
44
+
45
+
46
+ def is_windows():
47
+ return platform == 'win32'
48
+
42
49
 
43
50
  class Language(Enum):
44
51
  JAPANESE = "ja"
@@ -153,7 +160,7 @@ class Audio:
153
160
  extension: str = 'opus'
154
161
  beginning_offset: float = 0.0
155
162
  end_offset: float = 0.5
156
- ffmpeg_reencode_options: str = '-c:a libopus -f opus -af \"afade=t=in:d=0.10\"'
163
+ ffmpeg_reencode_options: str = '-c:a libopus -f opus -af \"afade=t=in:d=0.10\"' if is_windows() else ''
157
164
  external_tool: str = ""
158
165
  anki_media_collection: str = ""
159
166
  external_tool_enabled: bool = True
@@ -619,7 +626,7 @@ console_handler.setFormatter(formatter)
619
626
  logger.addHandler(console_handler)
620
627
 
621
628
  # Create rotating file handler with level DEBUG
622
- if 'gsm' in sys.argv[0]:
629
+ if 'gsm' in sys.argv[0].lower() or 'gamesentenceminer' in sys.argv[0].lower():
623
630
  file_handler = RotatingFileHandler(get_log_path(), maxBytes=1024 * 1024, backupCount=5, encoding='utf-8')
624
631
  file_handler.setLevel(logging.DEBUG)
625
632
  file_handler.setFormatter(formatter)
@@ -637,5 +644,4 @@ class GsmAppState:
637
644
  self.previous_screenshot = None
638
645
  self.previous_replay = None
639
646
 
640
- gsm_state = GsmAppState()
641
-
647
+ gsm_state = GsmAppState()
@@ -1,6 +1,7 @@
1
1
  import shutil
2
2
  import tempfile
3
3
 
4
+ import GameSentenceMiner.configuration
4
5
  from GameSentenceMiner import obs, util, configuration
5
6
  from GameSentenceMiner.configuration import *
6
7
  from GameSentenceMiner.text_log import initial_time
@@ -8,10 +9,10 @@ from GameSentenceMiner.util import *
8
9
 
9
10
 
10
11
  def get_ffmpeg_path():
11
- return os.path.join(get_app_directory(), "ffmpeg", "ffmpeg.exe") if util.is_windows() else "ffmpeg"
12
+ return os.path.join(get_app_directory(), "ffmpeg", "ffmpeg.exe") if is_windows() else "ffmpeg"
12
13
 
13
14
  def get_ffprobe_path():
14
- return os.path.join(get_app_directory(), "ffmpeg", "ffprobe.exe") if util.is_windows() else "ffprobe"
15
+ return os.path.join(get_app_directory(), "ffmpeg", "ffprobe.exe") if is_windows() else "ffprobe"
15
16
 
16
17
  ffmpeg_base_command_list = [get_ffmpeg_path(), "-hide_banner", "-loglevel", "error", '-nostdin']
17
18
 
@@ -398,7 +399,7 @@ def convert_audio_to_wav(input_audio, output_wav):
398
399
  "-i", input_audio,
399
400
  "-ar", "16000", # Resample to 16kHz
400
401
  "-ac", "1", # Convert to mono
401
- "-af", "afftdn,dialoguenhance" if not util.is_linux() else "afftdn",
402
+ "-af", "afftdn,dialoguenhance" if not is_linux() else "afftdn",
402
403
  output_wav
403
404
  ]
404
405
  logger.debug(" ".join(command))
GameSentenceMiner/gsm.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import asyncio
2
- import shutil
3
2
  import sys
4
3
 
5
4
  from GameSentenceMiner.vad.result import VADResult
@@ -39,7 +38,7 @@ try:
39
38
  from GameSentenceMiner.web import texthooking_page
40
39
  from GameSentenceMiner.web.texthooking_page import run_text_hooker_page
41
40
  except Exception as e:
42
- from GameSentenceMiner.configuration import logger
41
+ from GameSentenceMiner.configuration import logger, is_linux, is_windows
43
42
  import time
44
43
  logger.info("Something bad happened during import/initialization, closing in 5 seconds")
45
44
  logger.exception(e)
@@ -74,7 +73,6 @@ class VideoToAudioHandler(FileSystemEventHandler):
74
73
 
75
74
  def process_replay(self, video_path):
76
75
  vad_trimmed_audio = ''
77
- print(video_path)
78
76
  if "previous.mkv" in video_path:
79
77
  os.remove(video_path)
80
78
  video_path = gsm_state.previous_replay
@@ -682,7 +680,7 @@ async def register_scene_switcher_callback():
682
680
 
683
681
  await obs.register_scene_change_callback(scene_switcher_callback)
684
682
 
685
- async def main(reloading=False):
683
+ async def async_main(reloading=False):
686
684
  global root, settings_window
687
685
  initialize(reloading)
688
686
  logger.info("Script started.")
@@ -721,11 +719,14 @@ async def main(reloading=False):
721
719
  except Exception as e:
722
720
  logger.error(f"Error stopping observer: {e}")
723
721
 
722
+ def main():
723
+ asyncio.run(async_main())
724
+
724
725
 
725
726
  if __name__ == "__main__":
726
727
  logger.info("Starting GSM")
727
728
  try:
728
- asyncio.run(main())
729
+ asyncio.run(async_main())
729
730
  except Exception as e:
730
731
  logger.exception(e)
731
732
  time.sleep(5)
@@ -2,14 +2,11 @@ import platform
2
2
 
3
3
  import requests
4
4
  from plyer import notification
5
-
6
- from GameSentenceMiner.util import is_windows
5
+ from GameSentenceMiner.configuration import logger, is_windows
7
6
 
8
7
  if is_windows():
9
8
  from win10toast import ToastNotifier
10
9
 
11
- from GameSentenceMiner.configuration import logger
12
-
13
10
  if is_windows():
14
11
  class MyToastNotifier(ToastNotifier):
15
12
  def __init__(self):
GameSentenceMiner/obs.py CHANGED
@@ -8,6 +8,7 @@ import psutil
8
8
 
9
9
  import obsws_python as obs
10
10
 
11
+ import GameSentenceMiner.configuration
11
12
  from GameSentenceMiner import util, configuration
12
13
  from GameSentenceMiner.configuration import *
13
14
  from GameSentenceMiner.model import *
@@ -140,7 +141,7 @@ async def connect_to_obs(retry_count=0):
140
141
  if not get_config().obs.enabled:
141
142
  return
142
143
 
143
- if util.is_windows():
144
+ if GameSentenceMiner.configuration.is_windows():
144
145
  get_obs_websocket_config_values()
145
146
 
146
147
  while True:
@@ -174,7 +175,7 @@ def connect_to_obs_sync(retry_count=0):
174
175
  if not get_config().obs.enabled or client:
175
176
  return
176
177
 
177
- if util.is_windows():
178
+ if GameSentenceMiner.configuration.is_windows():
178
179
  get_obs_websocket_config_values()
179
180
 
180
181
  while True:
GameSentenceMiner/util.py CHANGED
@@ -7,7 +7,6 @@ import subprocess
7
7
  import threading
8
8
  import time
9
9
  from datetime import datetime
10
- from sys import platform
11
10
 
12
11
  from rapidfuzz import process
13
12
 
@@ -131,12 +130,6 @@ def run_agent_and_hook(pname, agent_script):
131
130
  keep_running = False
132
131
 
133
132
 
134
- def is_linux():
135
- return platform == 'linux'
136
-
137
- def is_windows():
138
- return platform == 'win32'
139
-
140
133
  # def run_command(command, shell=False, input=None, capture_output=False, timeout=None, check=False, **kwargs):
141
134
  # # Use shell=True if the OS is Linux, otherwise shell=False
142
135
  # if is_linux():
@@ -258,8 +251,6 @@ TEXT_REPLACEMENTS_FILE = os.path.join(get_app_directory(), 'config', 'text_repla
258
251
  OCR_REPLACEMENTS_FILE = os.path.join(get_app_directory(), 'config', 'ocr_replacements.json')
259
252
  os.makedirs(os.path.dirname(TEXT_REPLACEMENTS_FILE), exist_ok=True)
260
253
 
261
- import urllib.request
262
-
263
254
  # if not os.path.exists(OCR_REPLACEMENTS_FILE):
264
255
  # url = "https://raw.githubusercontent.com/bpwhelan/GameSentenceMiner/refs/heads/main/electron-src/assets/ocr_replacements.json"
265
256
  # try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.9.2
3
+ Version: 2.9.3
4
4
  Summary: A tool for mining sentences from games.
5
5
  Author-email: Beangate <bpwhelan95@gmail.com>
6
6
  License: MIT License
@@ -1,19 +1,18 @@
1
1
  GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  GameSentenceMiner/anki.py,sha256=JnVfFkLpEfWaPfOLngU0PSQq4vrgWuhd_VLYZEBqNTY,14608
3
3
  GameSentenceMiner/config_gui.py,sha256=h4zz85gfhxSphaJ-IZSu9D4jR70mDlKecZ9JRCO5Noc,80927
4
- GameSentenceMiner/configuration.py,sha256=8CfdTJ0ROJrxyzNg3NaElAVS1bwchg1ih6XfhfDZy1g,25492
4
+ GameSentenceMiner/configuration.py,sha256=KKW6fmpxya4dmXx9cERFVrzsKCTw0vmZrF2HAJDURBU,25667
5
5
  GameSentenceMiner/electron_config.py,sha256=dGcPYCISPehXubYSzsDuI2Gl092MYK0u3bTnkL9Jh1Y,9787
6
- GameSentenceMiner/ffmpeg.py,sha256=zVmLJOsXpy71zKb0cLBPrXJ6YpjPVRJmH0uRfd5O30k,18299
6
+ GameSentenceMiner/ffmpeg.py,sha256=APa2vNdAgxYdG96_Z3Xdh1WqOiWaK6gTLJqzEvCMMeU,18323
7
7
  GameSentenceMiner/gametext.py,sha256=sll-6Pficd4ZXYy8yL8hBrEOSpfa53TOye7vtHHKFN4,6218
8
- GameSentenceMiner/gsm.py,sha256=_Mp_gZFomeFz9FTZqYEXIgqxbICYcAB06KUlFkClX5Q,29831
8
+ GameSentenceMiner/gsm.py,sha256=olG3BIWjbVHWTsRKmeDVE5X8XrgppWke73Fy1J15dxA,29868
9
9
  GameSentenceMiner/model.py,sha256=1lRyJFf_LND_4O16h8CWVqDfosLgr0ZS6ufBZ3qJHpY,5699
10
- GameSentenceMiner/notification.py,sha256=pXKoLfmRQLH55IQ5G6uxdMuczqX7D6l3ubVEY1e6hXg,2859
11
- GameSentenceMiner/obs.py,sha256=DoUJk00Gk0Idley7CEldfIobqJ9na2UBlCv7nclZO4s,14793
12
- GameSentenceMiner/obs_back.py,sha256=_N_UV7Nh5cyy3mnH5lOUOzhgZwHMACeFEuBo1Z-bNzg,10894
10
+ GameSentenceMiner/notification.py,sha256=fPTbZJG82YLsppjC0sByQ0SX3bSsvDWXzG498HBEGMY,2823
11
+ GameSentenceMiner/obs.py,sha256=OntCO14G2KjzMk6bztqhQWiDiNzjzPpL_KHKXasxJq4,14886
13
12
  GameSentenceMiner/package.py,sha256=YlS6QRMuVlm6mdXx0rlXv9_3erTGS21jaP3PNNWfAH0,1250
14
13
  GameSentenceMiner/ss_selector.py,sha256=csey9H3561-guRJcT6gQN6hXxvylP0CBI0dp2-kwo2Q,4446
15
14
  GameSentenceMiner/text_log.py,sha256=U2_g8THAYeexRiE2bLk_bCt_2ShiA8SQ9VdJsi4riHs,5181
16
- GameSentenceMiner/util.py,sha256=PrDNnxWiJZh1lGuwnp3DjWIlwbkVxweRTYWLtQk94Ao,9122
15
+ GameSentenceMiner/util.py,sha256=ZbK7i1UeOzKyc5WtCcttiGljR_stfu7qpnEpgqFBwro,8976
17
16
  GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
17
  GameSentenceMiner/ai/ai_prompting.py,sha256=xw8et6XNwQiDXOXZnw8iIntVSg8lni4YYZbgWsK7qDE,10013
19
18
  GameSentenceMiner/communication/__init__.py,sha256=_jGn9PJxtOAOPtJ2rI-Qu9hEHVZVpIvWlxKvqk91_zI,638
@@ -57,9 +56,9 @@ GameSentenceMiner/web/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
57
56
  GameSentenceMiner/web/templates/index.html,sha256=HZKiIjiGJV8PGQ9T2aLDUNSfJn71qOwbYCjbRuSIjpY,213583
58
57
  GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
59
58
  GameSentenceMiner/web/templates/utility.html,sha256=3flZinKNqUJ7pvrZk6xu__v67z44rXnaK7UTZ303R-8,16946
60
- gamesentenceminer-2.9.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
61
- gamesentenceminer-2.9.2.dist-info/METADATA,sha256=XVxZdTUhTDvmNprnr75Eqs1Cd-6QjFDQOjjn-CeLI0Y,7280
62
- gamesentenceminer-2.9.2.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
63
- gamesentenceminer-2.9.2.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
64
- gamesentenceminer-2.9.2.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
65
- gamesentenceminer-2.9.2.dist-info/RECORD,,
59
+ gamesentenceminer-2.9.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
60
+ gamesentenceminer-2.9.3.dist-info/METADATA,sha256=XlzEKeMFA9JKtiEU_pxShJbopSnIKKdL7cRsg25JxqQ,7280
61
+ gamesentenceminer-2.9.3.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
62
+ gamesentenceminer-2.9.3.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
63
+ gamesentenceminer-2.9.3.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
64
+ gamesentenceminer-2.9.3.dist-info/RECORD,,
@@ -1,309 +0,0 @@
1
- import os.path
2
- import subprocess
3
- import threading
4
- import time
5
- import psutil
6
-
7
- from obswebsocket import obsws, requests, events
8
- from obswebsocket.exceptions import ConnectionFailure
9
-
10
- from GameSentenceMiner import util, configuration
11
- from GameSentenceMiner.configuration import *
12
- from GameSentenceMiner.model import *
13
-
14
- client: obsws = None
15
- obs_process_pid = None
16
- # logging.getLogger('obswebsocket').setLevel(logging.CRITICAL)
17
- OBS_PID_FILE = os.path.join(configuration.get_app_directory(), 'obs-studio', 'obs_pid.txt')
18
-
19
- # REFERENCE: https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md
20
-
21
-
22
- def get_obs_path():
23
- return os.path.join(configuration.get_app_directory(), 'obs-studio/bin/64bit/obs64.exe')
24
-
25
- def is_process_running(pid):
26
- try:
27
- process = psutil.Process(pid)
28
- return 'obs' in process.exe()
29
- except (psutil.NoSuchProcess, psutil.AccessDenied, OSError):
30
- if os.path.exists(OBS_PID_FILE):
31
- os.remove(OBS_PID_FILE)
32
- return False
33
-
34
- def start_obs():
35
- global obs_process_pid
36
- if os.path.exists(OBS_PID_FILE):
37
- with open(OBS_PID_FILE, "r") as f:
38
- try:
39
- obs_process_pid = int(f.read().strip())
40
- if is_process_running(obs_process_pid):
41
- print(f"OBS is already running with PID: {obs_process_pid}")
42
- connect_to_obs()
43
- return obs_process_pid
44
- except ValueError:
45
- print("Invalid PID found in file. Launching new OBS instance.")
46
- except OSError:
47
- print("No process found with the stored PID. Launching new OBS instance.")
48
-
49
- obs_path = get_obs_path()
50
- if not os.path.exists(obs_path):
51
- print(f"OBS not found at {obs_path}. Please install OBS.")
52
- return None
53
- try:
54
- obs_process = subprocess.Popen([obs_path, '--disable-shutdown-check', '--portable', '--startreplaybuffer', ], cwd=os.path.dirname(obs_path))
55
- obs_process_pid = obs_process.pid
56
- connect_to_obs()
57
- with open(OBS_PID_FILE, "w") as f:
58
- f.write(str(obs_process_pid))
59
- print(f"OBS launched with PID: {obs_process_pid}")
60
- return obs_process_pid
61
- except Exception as e:
62
- print(f"Error launching OBS: {e}")
63
- return None
64
-
65
- def check_obs_folder_is_correct():
66
- obs_record_directory = get_record_directory()
67
- if obs_record_directory and os.path.normpath(obs_record_directory) != os.path.normpath(
68
- get_config().paths.folder_to_watch):
69
- logger.info("OBS Path Setting wrong, OBS Recording folder in GSM Config")
70
- get_config().paths.folder_to_watch = os.path.normpath(obs_record_directory)
71
- get_master_config().sync_shared_fields()
72
- save_full_config(get_master_config())
73
-
74
-
75
- def get_obs_websocket_config_values():
76
- config_path = os.path.join(get_app_directory(), 'obs-studio', 'config', 'obs-studio', 'plugin_config', 'obs-websocket', 'config.json')
77
-
78
- # Check if config file exists
79
- if not os.path.isfile(config_path):
80
- raise FileNotFoundError(f"OBS WebSocket config not found at {config_path}")
81
-
82
- # Read the JSON configuration
83
- with open(config_path, 'r') as file:
84
- config = json.load(file)
85
-
86
- # Extract values
87
- server_enabled = config.get("server_enabled", False)
88
- server_port = config.get("server_port", 7274) # Default to 4455 if not set
89
- server_password = config.get("server_password", None)
90
-
91
- if not server_enabled:
92
- logger.info("OBS WebSocket server is not enabled. Enabling it now... Restart OBS for changes to take effect.")
93
- config["server_enabled"] = True
94
-
95
- with open(config_path, 'w') as file:
96
- json.dump(config, file, indent=4)
97
-
98
- if get_config().obs.password == 'your_password':
99
- logger.info("OBS WebSocket password is not set. Setting it now...")
100
- full_config = get_master_config()
101
- full_config.get_config().obs.port = server_port
102
- full_config.get_config().obs.password = server_password
103
- full_config.sync_shared_fields()
104
- full_config.save()
105
- reload_config()
106
-
107
-
108
- connected = False
109
-
110
- def on_connect(obs):
111
- global connected
112
- logger.info("Reconnected to OBS WebSocket.")
113
- start_replay_buffer()
114
- connected = True
115
-
116
-
117
- def on_disconnect(obs):
118
- global connected
119
- logger.error("OBS Connection Lost!")
120
- connected = False
121
-
122
-
123
- def connect_to_obs(retry_count=0):
124
- global client
125
- if not get_config().obs.enabled or client:
126
- return
127
-
128
- if util.is_windows():
129
- get_obs_websocket_config_values()
130
-
131
- try:
132
- client = obsws(
133
- host=get_config().obs.host,
134
- port=get_config().obs.port,
135
- password=get_config().obs.password,
136
- authreconnect=1,
137
- on_connect=on_connect,
138
- on_disconnect=on_disconnect
139
- )
140
- client.connect()
141
- update_current_game()
142
- except ConnectionFailure as e:
143
- if retry_count % 5 == 0:
144
- logger.error(f"Failed to connect to OBS WebSocket: {e}. Retrying...")
145
- time.sleep(1)
146
- connect_to_obs(retry_count=retry_count + 1)
147
-
148
-
149
- # Disconnect from OBS WebSocket
150
- def disconnect_from_obs():
151
- global client
152
- if client:
153
- client.disconnect()
154
- client = None
155
- logger.info("Disconnected from OBS WebSocket.")
156
-
157
- def do_obs_call(request, from_dict=None, retry=3):
158
- connect_to_obs()
159
- for _ in range(retry + 1):
160
- try:
161
- response = client.call(request)
162
- if response and response.status:
163
- return from_dict(response.datain) if from_dict else response.datain
164
- time.sleep(0.3)
165
- except Exception as e:
166
- logger.error(f"Error calling OBS: {e}")
167
- if "socket is already closed" in str(e) or "object has no attribute" in str(e):
168
- time.sleep(0.3)
169
- else:
170
- return None
171
- return None
172
-
173
- def toggle_replay_buffer():
174
- try:
175
- do_obs_call(requests.ToggleReplayBuffer())
176
- logger.info("Replay buffer Toggled.")
177
- except Exception as e:
178
- logger.error(f"Error toggling buffer: {e}")
179
-
180
-
181
- # Start replay buffer
182
- def start_replay_buffer(retry=5):
183
- try:
184
- if not get_replay_buffer_status()['outputActive']:
185
- do_obs_call(requests.StartReplayBuffer(), retry=0)
186
- except Exception as e:
187
- if "socket is already closed" in str(e):
188
- if retry > 0:
189
- time.sleep(1)
190
- start_replay_buffer(retry - 1)
191
- else:
192
- logger.error(f"Error starting replay buffer: {e}")
193
-
194
- def get_replay_buffer_status():
195
- try:
196
- return do_obs_call(requests.GetReplayBufferStatus())
197
- except Exception as e:
198
- logger.error(f"Error getting replay buffer status: {e}")
199
-
200
-
201
- # Stop replay buffer
202
- def stop_replay_buffer():
203
- try:
204
- client.call(requests.StopReplayBuffer())
205
- logger.error("Replay buffer stopped.")
206
- except Exception as e:
207
- logger.error(f"Error stopping replay buffer: {e}")
208
-
209
- # Save the current replay buffer
210
- def save_replay_buffer():
211
- replay_buffer_started = do_obs_call(requests.GetReplayBufferStatus())['outputActive']
212
- if replay_buffer_started:
213
- client.call(requests.SaveReplayBuffer())
214
- logger.info("Replay buffer saved. If your log stops bere, make sure your obs output path matches \"Path To Watch\" in GSM settings.")
215
- else:
216
- logger.error("Replay Buffer is not active, could not save Replay Buffer!")
217
-
218
-
219
- def get_current_scene():
220
- try:
221
- return do_obs_call(requests.GetCurrentProgramScene(), SceneInfo.from_dict, retry=0).sceneName
222
- except Exception as e:
223
- logger.debug(f"Couldn't get scene: {e}")
224
- return ''
225
-
226
-
227
- def get_source_from_scene(scene_name):
228
- try:
229
- return do_obs_call(requests.GetSceneItemList(sceneName=scene_name), SceneItemsResponse.from_dict).sceneItems[0]
230
- except Exception as e:
231
- logger.error(f"Error getting source from scene: {e}")
232
- return ''
233
-
234
- def get_record_directory():
235
- try:
236
- return do_obs_call(requests.GetRecordDirectory(), RecordDirectory.from_dict).recordDirectory
237
- except Exception as e:
238
- logger.error(f"Error getting recording folder: {e}")
239
- return ''
240
-
241
- def get_obs_scenes():
242
- try:
243
- response: SceneListResponse = do_obs_call(requests.GetSceneList(), SceneListResponse.from_dict, retry=0)
244
- return response.scenes
245
- except Exception as e:
246
- logger.error(f"Error getting scenes: {e}")
247
- return None
248
-
249
- def register_scene_change_callback(callback):
250
- global client
251
- if not client:
252
- logger.error("OBS client is not connected.")
253
- return
254
-
255
- def on_scene_change(data):
256
- logger.info("Scene changed: " + str(data))
257
- scene_name = data.getSceneName()
258
- if scene_name:
259
- callback(scene_name)
260
-
261
- client.register(on_scene_change, events.CurrentProgramSceneChanged)
262
- logger.info("Scene change callback registered.")
263
-
264
-
265
- def get_screenshot(compression=-1):
266
- try:
267
- screenshot = util.make_unique_file_name(os.path.abspath(
268
- configuration.get_temporary_directory()) + '/screenshot.png')
269
- update_current_game()
270
- current_source = get_source_from_scene(get_current_game())
271
- current_source_name = current_source.sourceName
272
- if not current_source_name:
273
- logger.error("No active scene found.")
274
- return
275
- start = time.time()
276
- logger.debug(f"Current source name: {current_source_name}")
277
- response = client.call(requests.SaveSourceScreenshot(sourceName=current_source_name, imageFormat='png', imageFilePath=screenshot, imageCompressionQuality=compression))
278
- logger.debug(f"Screenshot response: {response}")
279
- logger.debug(f"Screenshot took {time.time() - start:.3f} seconds to save")
280
- return screenshot
281
- except Exception as e:
282
- logger.error(f"Error getting screenshot: {e}")
283
-
284
- def get_screenshot_base64():
285
- try:
286
- update_current_game()
287
- current_source = get_source_from_scene(get_current_game())
288
- current_source_name = current_source.sourceName
289
- if not current_source_name:
290
- logger.error("No active scene found.")
291
- return
292
- response = do_obs_call(requests.GetSourceScreenshot(sourceName=current_source_name, imageFormat='png', imageCompressionQuality=0))
293
- with open('screenshot_response.txt', 'wb') as f:
294
- f.write(str(response).encode())
295
- return response['imageData']
296
- except Exception as e:
297
- logger.error(f"Error getting screenshot: {e}")
298
-
299
- def update_current_game():
300
- configuration.current_game = get_current_scene()
301
-
302
-
303
- def get_current_game(sanitize=False):
304
- if not configuration.current_game:
305
- update_current_game()
306
-
307
- if sanitize:
308
- return util.sanitize_filename(configuration.current_game)
309
- return configuration.current_game