GameSentenceMiner 2.4.9__py3-none-any.whl → 2.4.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.
@@ -103,6 +103,7 @@ class ConfigApp:
103
103
  config = ProfileConfig(
104
104
  general=General(
105
105
  use_websocket=self.websocket_enabled.get(),
106
+ use_clipboard=self.clipboard_enabled.get(),
106
107
  websocket_uri=self.websocket_uri.get(),
107
108
  open_config_on_startup=self.open_config_on_startup.get(),
108
109
  texthook_replacement_regex=self.texthook_replacement_regex.get()
@@ -191,6 +192,10 @@ class ConfigApp:
191
192
  messagebox.showerror("Configuration Error", "Cannot have Full Auto and Backfill mode on at the same time! Note: Backfill is a very niche workflow.")
192
193
  return
193
194
 
195
+ if not config.general.use_websocket and not config.general.use_clipboard:
196
+ messagebox.showerror("Configuration Error", "Cannot have both Clipboard and Websocket Disabled.")
197
+ return
198
+
194
199
  current_profile = self.profile_combobox.get()
195
200
  prev_config = self.master_config.get_config()
196
201
  if profile_change:
@@ -256,13 +261,20 @@ class ConfigApp:
256
261
  general_frame = ttk.Frame(self.notebook)
257
262
  self.notebook.add(general_frame, text='General')
258
263
 
259
- ttk.Label(general_frame, text="Websocket Enabled (Clipboard Disabled):").grid(row=self.current_row, column=0, sticky='W')
264
+ ttk.Label(general_frame, text="Websocket Enabled:").grid(row=self.current_row, column=0, sticky='W')
260
265
  self.websocket_enabled = tk.BooleanVar(value=self.settings.general.use_websocket)
261
266
  ttk.Checkbutton(general_frame, variable=self.websocket_enabled).grid(row=self.current_row, column=1,
262
267
  sticky='W')
263
268
  self.add_label_and_increment_row(general_frame, "Enable or disable WebSocket communication. Enabling this will disable the clipboard monitor. RESTART REQUIRED.",
264
269
  row=self.current_row, column=2)
265
270
 
271
+ ttk.Label(general_frame, text="Clipboard Enabled:").grid(row=self.current_row, column=0, sticky='W')
272
+ self.clipboard_enabled = tk.BooleanVar(value=self.settings.general.use_clipboard)
273
+ ttk.Checkbutton(general_frame, variable=self.clipboard_enabled).grid(row=self.current_row, column=1,
274
+ sticky='W')
275
+ self.add_label_and_increment_row(general_frame, "Enable to allow GSM to see clipboard for text and line timing.",
276
+ row=self.current_row, column=2)
277
+
266
278
  ttk.Label(general_frame, text="Websocket URI:").grid(row=self.current_row, column=0, sticky='W')
267
279
  self.websocket_uri = ttk.Entry(general_frame)
268
280
  self.websocket_uri.insert(0, self.settings.general.websocket_uri)
@@ -888,6 +900,7 @@ class ConfigApp:
888
900
 
889
901
 
890
902
  if __name__ == '__main__':
891
- window = ConfigApp()
903
+ root = ttk.Window(themename='darkly')
904
+ window = ConfigApp(root)
892
905
  window.show()
893
906
  window.window.mainloop()
@@ -39,6 +39,7 @@ current_game = ''
39
39
  @dataclass
40
40
  class General:
41
41
  use_websocket: bool = True
42
+ use_clipboard: bool = True
42
43
  websocket_uri: str = 'localhost:6677'
43
44
  open_config_on_startup: bool = False
44
45
  texthook_replacement_regex: str = ""
@@ -63,7 +64,7 @@ class Anki:
63
64
  sentence_field: str = "Sentence"
64
65
  sentence_audio_field: str = "SentenceAudio"
65
66
  picture_field: str = "Picture"
66
- word_field: str = 'Word'
67
+ word_field: str = 'Expression'
67
68
  previous_sentence_field: str = ''
68
69
  previous_image_field: str = ''
69
70
  custom_tags: List[str] = None # Initialize to None and set it in __post_init__
@@ -123,7 +124,7 @@ class Audio:
123
124
  class OBS:
124
125
  enabled: bool = True
125
126
  open_obs: bool = True
126
- close_obs: bool = False
127
+ close_obs: bool = True
127
128
  host: str = "localhost"
128
129
  port: int = 4455
129
130
  password: str = "your_password"
@@ -236,6 +237,7 @@ class ProfileConfig:
236
237
  def restart_required(self, previous):
237
238
  previous: ProfileConfig
238
239
  if any([previous.general.use_websocket != self.general.use_websocket,
240
+ previous.general.use_clipboard != self.general.use_clipboard,
239
241
  previous.general.websocket_uri != self.general.websocket_uri,
240
242
  previous.paths.folder_to_watch != self.paths.folder_to_watch,
241
243
  previous.obs.open_obs != self.obs.open_obs,
@@ -49,7 +49,7 @@ def get_screenshot(video_file, time_from_end):
49
49
  return output_image
50
50
 
51
51
 
52
- def get_screenshot_time(video_path, game_line):
52
+ def get_screenshot_time(video_path, game_line, default_beginning=False):
53
53
  if game_line:
54
54
  line_time = game_line.time
55
55
  else:
@@ -64,7 +64,13 @@ def get_screenshot_time(video_path, game_line):
64
64
  time_from_end = file_length - total_seconds - get_config().screenshot.seconds_after_line
65
65
 
66
66
  if time_from_end < 0 or time_from_end > file_length:
67
- raise ValueError("Calculated screenshot time is out of bounds for trimmed video.")
67
+ logger.error(
68
+ "Calculated screenshot time is out of bounds for trimmed video")
69
+ if default_beginning:
70
+ logger.info("Defaulting to using the beginning of the Replay Buffer")
71
+ return file_length - 1.0
72
+ logger.info("Defaulting to using the end of the Replay Buffer")
73
+ return 0
68
74
 
69
75
  return time_from_end
70
76
 
@@ -21,6 +21,7 @@ current_line_after_regex = ''
21
21
  current_line_time = datetime.now()
22
22
 
23
23
  reconnecting = False
24
+ websocket_connected = False
24
25
  multi_mine_event_bus: Callable[[str, datetime], None] = None
25
26
 
26
27
  @dataclass
@@ -89,6 +90,9 @@ class ClipboardMonitor(threading.Thread):
89
90
  current_line = pyperclip.paste()
90
91
 
91
92
  while True:
93
+ if websocket_connected:
94
+ time.sleep(1)
95
+ continue
92
96
  current_clipboard = pyperclip.paste()
93
97
 
94
98
  if current_clipboard != current_line:
@@ -98,13 +102,14 @@ class ClipboardMonitor(threading.Thread):
98
102
 
99
103
 
100
104
  async def listen_websocket():
101
- global current_line, current_line_time, line_history, reconnecting
105
+ global current_line, current_line_time, line_history, reconnecting, websocket_connected
102
106
  while True:
103
107
  try:
104
108
  async with websockets.connect(f'ws://{get_config().general.websocket_uri}', ping_interval=None) as websocket:
105
109
  if reconnecting:
106
- logger.info(f"Texthooker WebSocket connected Successfully!")
110
+ logger.info(f"Texthooker WebSocket connected Successfully!" + " Disabling Clipboard Monitor." if get_config().general.use_clipboard else "")
107
111
  reconnecting = False
112
+ websocket_connected = True
108
113
  while True:
109
114
  message = await websocket.recv()
110
115
 
@@ -117,8 +122,9 @@ async def listen_websocket():
117
122
  if current_clipboard != current_line:
118
123
  handle_new_text_event(current_clipboard)
119
124
  except (websockets.ConnectionClosed, ConnectionError) as e:
125
+ websocket_connected = False
120
126
  if not reconnecting:
121
- logger.warning(f"Texthooker WebSocket connection lost: {e}. IF USING CLIPBOARD, WEBSOCKET NEEDS TO BE TURNED OFF IN SETTINGS. Attempting to Reconnect...")
127
+ logger.warning(f"Texthooker WebSocket connection lost, Defaulting to clipboard if enabled. Attempting to Reconnect...")
122
128
  reconnecting = True
123
129
  await asyncio.sleep(5)
124
130
 
@@ -150,15 +156,19 @@ def start_text_monitor(send_to_mine_event_bus):
150
156
  global multi_mine_event_bus
151
157
  multi_mine_event_bus = send_to_mine_event_bus
152
158
  if get_config().general.use_websocket:
153
- text_thread = threading.Thread(target=run_websocket_listener, daemon=True)
154
- else:
155
- text_thread = ClipboardMonitor()
156
- text_thread.start()
159
+ threading.Thread(target=run_websocket_listener, daemon=True).start()
160
+ if get_config().general.use_clipboard:
161
+ if get_config().general.use_websocket:
162
+ logger.info("Both WebSocket and Clipboard monitoring are enabled. WebSocket will take precedence if connected.")
163
+ ClipboardMonitor().start()
157
164
 
158
165
 
159
166
  def similar(a, b):
160
167
  return SequenceMatcher(None, a, b).ratio()
161
168
 
169
+ def one_contains_the_other(a, b):
170
+ return a in b or b in a
171
+
162
172
 
163
173
  def get_text_event(last_note) -> GameLine:
164
174
  lines = line_history.values
@@ -166,6 +176,9 @@ def get_text_event(last_note) -> GameLine:
166
176
  if not last_note:
167
177
  return lines[-1]
168
178
 
179
+ if not lines:
180
+ raise Exception("No lines in history. Text is required from either clipboard or websocket for GSM to work. Please check your setup/config.")
181
+
169
182
  sentence = last_note['fields'][get_config().anki.sentence_field]['value']
170
183
  if not sentence:
171
184
  return lines[-1]
@@ -173,7 +186,7 @@ def get_text_event(last_note) -> GameLine:
173
186
  for line in reversed(lines):
174
187
  similarity = similar(remove_html_tags(sentence), line.text)
175
188
  logger.debug(f"Comparing: {remove_html_tags(sentence)} with {line.text} - Similarity: {similarity}")
176
- if similarity >= 0.60 or line.text in remove_html_tags(sentence):
189
+ if similarity >= 0.60 or one_contains_the_other(line.text, remove_html_tags(sentence)):
177
190
  return line
178
191
 
179
192
  logger.debug("Couldn't find a match in history, using last event")
@@ -193,21 +206,21 @@ def get_line_and_future_lines(last_note):
193
206
  logger.debug(f"Comparing: {remove_html_tags(sentence)} with {line.text} - Similarity: {similarity}")
194
207
  if found:
195
208
  found_lines.append(line.text)
196
- if similarity >= 0.60 or line.text in remove_html_tags(sentence): # 80% similarity threshold
209
+ if similarity >= 0.60 or one_contains_the_other(line.text, remove_html_tags(sentence)): # 80% similarity threshold
197
210
  found = True
198
211
  found_lines.append(line.text)
199
212
  return found_lines
200
213
 
201
214
  def get_mined_line(last_note, lines):
202
215
  if not last_note:
203
- return lines[0]
216
+ return lines[-1]
204
217
 
205
218
  sentence = last_note['fields'][get_config().anki.sentence_field]['value']
206
- for line2 in lines:
207
- similarity = similar(remove_html_tags(sentence), line2.text)
208
- if similarity >= 0.60 or line2.text in remove_html_tags(sentence):
209
- return line2
210
- return lines[0]
219
+ for line in lines:
220
+ similarity = similar(remove_html_tags(sentence), line.text)
221
+ if similarity >= 0.60 or one_contains_the_other(line.text, remove_html_tags(sentence)):
222
+ return line
223
+ return lines[-1]
211
224
 
212
225
 
213
226
  def get_time_of_line(line):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: GameSentenceMiner
3
- Version: 2.4.9
3
+ Version: 2.4.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
@@ -1,10 +1,10 @@
1
1
  GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  GameSentenceMiner/anki.py,sha256=YSz5gUTsKOdbogwHKtgFM7v7pREjdAwl7A0Wa_CXnKg,10918
3
- GameSentenceMiner/config_gui.py,sha256=6DPJjzP-cHVU9q4j9UP1uF1z0o0z-mq0cuNqjZSIHdo,52566
4
- GameSentenceMiner/configuration.py,sha256=bvU2XCOjCtHiTtCI1t5r986QznEKfTmH--i3YPE1Q-Y,15072
3
+ GameSentenceMiner/config_gui.py,sha256=s0U0Sj_WvrJ8GecSyj33tWMh-OyOcZ5yY1ddEc1cgwg,53456
4
+ GameSentenceMiner/configuration.py,sha256=_VxzRHCJPW2iJTW6gheD1d6-oaUbUQQnLsIiHjgjNcY,15186
5
5
  GameSentenceMiner/electron_messaging.py,sha256=fBk9Ipo0jg2OZwYaKe1Qsm05P2ftrdTRGgFYob7ZA-k,139
6
- GameSentenceMiner/ffmpeg.py,sha256=vLBmQgkh23BS2vE5u7bli_1_3wFN5cB3VCgrAZ1VAL8,11197
7
- GameSentenceMiner/gametext.py,sha256=VjWNdjHwWXWIwNOfYxud7EwIyg7t6zZ3IkmMhh8Vc0c,6819
6
+ GameSentenceMiner/ffmpeg.py,sha256=vkRvhsuXCL8-tGynobdLBnw4qNHUhTC33ITCCnjfZLM,11468
7
+ GameSentenceMiner/gametext.py,sha256=YXjaZF130lkzU-I2dpZ5HoNrj3GSCG-7X9B04IFnetI,7551
8
8
  GameSentenceMiner/gsm.py,sha256=E9Hpbyzrv8FEFGBs-4Hf60m3sk0ps3OQdvB1n42AhRU,20164
9
9
  GameSentenceMiner/model.py,sha256=oh8VVT8T1UKekbmP6MGNgQ8jIuQ_7Rg4GPzDCn2kJo8,1999
10
10
  GameSentenceMiner/notification.py,sha256=WBaQWoPNhW4XqdPBUmxPBgjk0ngzH_4v9zMQ-XQAKC8,2010
@@ -19,9 +19,9 @@ GameSentenceMiner/vad/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
19
19
  GameSentenceMiner/vad/silero_trim.py,sha256=syDJX_KbFmdyFFtnQqYTD0tICsUCJizYhs-atPgXtxA,1549
20
20
  GameSentenceMiner/vad/vosk_helper.py,sha256=HifeXKbEMrs81ZuuGxS67yAghu8TMXUP6Oan9i9dTxw,5938
21
21
  GameSentenceMiner/vad/whisper_helper.py,sha256=bpR1HVnJRn9H5u8XaHBqBJ6JwIjzqn-Fajps8QmQ4zc,3411
22
- gamesentenceminer-2.4.9.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
23
- gamesentenceminer-2.4.9.dist-info/METADATA,sha256=S1bYWp1jUPpluIsgLldFPvvQydDmOH2Ue6aMqZ2Ca9M,5387
24
- gamesentenceminer-2.4.9.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
25
- gamesentenceminer-2.4.9.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
26
- gamesentenceminer-2.4.9.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
27
- gamesentenceminer-2.4.9.dist-info/RECORD,,
22
+ gamesentenceminer-2.4.12.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
23
+ gamesentenceminer-2.4.12.dist-info/METADATA,sha256=fqK1YCT402ptWC4idyZaIqjbCQ38jGDAZGJCuVJ1TAE,5388
24
+ gamesentenceminer-2.4.12.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
25
+ gamesentenceminer-2.4.12.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
26
+ gamesentenceminer-2.4.12.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
27
+ gamesentenceminer-2.4.12.dist-info/RECORD,,