GameSentenceMiner 2.16.13__tar.gz → 2.17.1__tar.gz

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.
Files changed (103) hide show
  1. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/anki.py +7 -2
  2. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/config_gui.py +7 -1
  3. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/gametext.py +6 -4
  4. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/gsm.py +11 -10
  5. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/obs.py +42 -7
  6. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/ocr/owocr_area_selector.py +1 -0
  7. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/ocr/owocr_helper.py +10 -7
  8. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/owocr/owocr/run.py +9 -3
  9. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/ffmpeg.py +5 -2
  10. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/vad.py +5 -2
  11. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/database_api.py +4 -4
  12. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/js/shared.js +4 -4
  13. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner.egg-info/PKG-INFO +1 -1
  14. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/PKG-INFO +1 -1
  15. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/pyproject.toml +3 -3
  16. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/__init__.py +0 -0
  17. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/ai/__init__.py +0 -0
  18. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/ai/ai_prompting.py +0 -0
  19. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/assets/__init__.py +0 -0
  20. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/assets/icon.png +0 -0
  21. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/assets/icon128.png +0 -0
  22. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/assets/icon256.png +0 -0
  23. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/assets/icon32.png +0 -0
  24. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/assets/icon512.png +0 -0
  25. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/assets/icon64.png +0 -0
  26. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/assets/pickaxe.png +0 -0
  27. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/locales/en_us.json +0 -0
  28. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/locales/ja_jp.json +0 -0
  29. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/locales/zh_cn.json +0 -0
  30. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/ocr/__init__.py +0 -0
  31. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/ocr/gsm_ocr_config.py +0 -0
  32. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/ocr/ocrconfig.py +0 -0
  33. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/ocr/ss_picker.py +0 -0
  34. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/owocr/owocr/__init__.py +0 -0
  35. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/owocr/owocr/__main__.py +0 -0
  36. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/owocr/owocr/config.py +0 -0
  37. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/owocr/owocr/lens_betterproto.py +0 -0
  38. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/owocr/owocr/ocr.py +0 -0
  39. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py +0 -0
  40. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/tools/__init__.py +0 -0
  41. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/tools/audio_offset_selector.py +0 -0
  42. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/tools/furigana_filter_preview.py +0 -0
  43. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/tools/ss_selector.py +0 -0
  44. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/tools/window_transparency.py +0 -0
  45. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/__init__.py +0 -0
  46. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/communication/__init__.py +0 -0
  47. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/communication/send.py +0 -0
  48. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/communication/websocket.py +0 -0
  49. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/configuration.py +0 -0
  50. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/db.py +0 -0
  51. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/downloader/Untitled_json.py +0 -0
  52. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/downloader/__init__.py +0 -0
  53. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/downloader/download_tools.py +0 -0
  54. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/downloader/oneocr_dl.py +0 -0
  55. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/electron_config.py +0 -0
  56. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/get_overlay_coords.py +0 -0
  57. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/gsm_utils.py +0 -0
  58. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/model.py +0 -0
  59. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/notification.py +0 -0
  60. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/text_log.py +0 -0
  61. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/win10toast/__init__.py +0 -0
  62. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/util/win10toast/__main__.py +0 -0
  63. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/__init__.py +0 -0
  64. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/events.py +0 -0
  65. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/gsm_websocket.py +0 -0
  66. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/service.py +0 -0
  67. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/__init__.py +0 -0
  68. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/apple-touch-icon.png +0 -0
  69. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/css/kanji-grid.css +0 -0
  70. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/css/search.css +0 -0
  71. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/css/shared.css +0 -0
  72. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/css/stats.css +0 -0
  73. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/favicon-96x96.png +0 -0
  74. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/favicon.ico +0 -0
  75. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/favicon.svg +0 -0
  76. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/js/anki_stats.js +0 -0
  77. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/js/database.js +0 -0
  78. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/js/kanji-grid.js +0 -0
  79. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/js/search.js +0 -0
  80. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/js/stats.js +0 -0
  81. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/site.webmanifest +0 -0
  82. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/style.css +0 -0
  83. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/web-app-manifest-192x192.png +0 -0
  84. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/static/web-app-manifest-512x512.png +0 -0
  85. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/stats.py +0 -0
  86. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/templates/anki_stats.html +0 -0
  87. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/templates/components/navigation.html +0 -0
  88. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/templates/components/theme-styles.html +0 -0
  89. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/templates/database.html +0 -0
  90. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/templates/index.html +0 -0
  91. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/templates/search.html +0 -0
  92. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/templates/stats.html +0 -0
  93. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/templates/utility.html +0 -0
  94. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/web/texthooking_page.py +0 -0
  95. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner/wip/__init___.py +0 -0
  96. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner.egg-info/SOURCES.txt +0 -0
  97. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner.egg-info/dependency_links.txt +0 -0
  98. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner.egg-info/entry_points.txt +0 -0
  99. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner.egg-info/requires.txt +0 -0
  100. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/GameSentenceMiner.egg-info/top_level.txt +0 -0
  101. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/LICENSE +0 -0
  102. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/README.md +0 -0
  103. {gamesentenceminer-2.16.13 → gamesentenceminer-2.17.1}/setup.cfg +0 -0
@@ -1,4 +1,8 @@
1
1
  import copy
2
+ import json
3
+ import os
4
+ import shutil
5
+ import threading
2
6
  from pathlib import Path
3
7
  import queue
4
8
  import time
@@ -15,8 +19,8 @@ from GameSentenceMiner.util.db import GameLinesTable
15
19
  from GameSentenceMiner.util.gsm_utils import make_unique, sanitize_filename, wait_for_stable_file, remove_html_and_cloze_tags, combine_dialogue, \
16
20
  run_new_thread, open_audio_in_external
17
21
  from GameSentenceMiner.util import ffmpeg, notification
18
- from GameSentenceMiner.util.configuration import *
19
- from GameSentenceMiner.util.configuration import get_config
22
+ from GameSentenceMiner.util.configuration import get_config, AnkiUpdateResult, logger, anki_results, gsm_status, \
23
+ gsm_state
20
24
  from GameSentenceMiner.util.model import AnkiCard
21
25
  from GameSentenceMiner.util.text_log import get_all_lines, get_text_event, get_mined_line, lines_match
22
26
  from GameSentenceMiner.obs import get_current_game
@@ -490,6 +494,7 @@ def update_card_from_same_sentence(last_card, lines, game_line):
490
494
  card_queue.pop(0)
491
495
  logger.error(f"Error saving replay buffer: {e}")
492
496
  return
497
+ return
493
498
  anki_result = anki_results[game_line.id]
494
499
  if anki_result.success:
495
500
  note, last_card = get_initial_card_info(last_card, lines)
@@ -1,7 +1,9 @@
1
1
  import asyncio
2
2
  import copy
3
3
  import json
4
+ import os
4
5
  import subprocess
6
+ import sys
5
7
  import time
6
8
  import tkinter as tk
7
9
  from tkinter import filedialog, messagebox, simpledialog, scrolledtext, font
@@ -12,7 +14,11 @@ import ttkbootstrap as ttk
12
14
  from GameSentenceMiner import obs
13
15
  from GameSentenceMiner.util import configuration
14
16
  from GameSentenceMiner.util.communication.send import send_restart_signal
15
- from GameSentenceMiner.util.configuration import *
17
+ from GameSentenceMiner.util.configuration import Config, Locale, logger, CommonLanguages, ProfileConfig, General, Paths, \
18
+ Anki, Features, Screenshot, Audio, OBS, Hotkeys, VAD, Overlay, Ai, Advanced, OverlayEngine, get_app_directory, \
19
+ get_config, is_beangate, AVAILABLE_LANGUAGES, WHSIPER_LARGE, WHISPER_TINY, WHISPER_BASE, WHISPER_SMALL, \
20
+ WHISPER_MEDIUM, WHISPER_TURBO, SILERO, WHISPER, OFF, gsm_state, DEFAULT_CONFIG, get_latest_version, \
21
+ get_current_version, AI_GEMINI, AI_GROQ, AI_OPENAI, save_full_config, get_default_anki_media_collection_path
16
22
  from GameSentenceMiner.util.db import AIModelsTable
17
23
  from GameSentenceMiner.util.downloader.download_tools import download_ocenaudio_if_needed
18
24
 
@@ -1,5 +1,7 @@
1
1
  import asyncio
2
+ import json
2
3
  import re
4
+ from datetime import datetime
3
5
 
4
6
  import pyperclip
5
7
  import requests
@@ -7,12 +9,11 @@ import websockets
7
9
  from websockets import InvalidStatus
8
10
  from rapidfuzz import fuzz
9
11
 
10
-
12
+ from GameSentenceMiner.util.configuration import get_config, gsm_status, logger, gsm_state
11
13
  from GameSentenceMiner.util.db import GameLinesTable
12
14
  from GameSentenceMiner.util.gsm_utils import do_text_replacements, TEXT_REPLACEMENTS_FILE, run_new_thread
13
- from GameSentenceMiner.util.configuration import *
14
- from GameSentenceMiner.util.text_log import *
15
15
  from GameSentenceMiner import obs
16
+ from GameSentenceMiner.util.text_log import add_line, get_text_log
16
17
  from GameSentenceMiner.web.texthooking_page import add_event_to_texthooker, overlay_server_thread
17
18
 
18
19
  from GameSentenceMiner.util.get_overlay_coords import OverlayProcessor
@@ -83,6 +84,7 @@ async def listen_websockets():
83
84
  line_time = None
84
85
  while True:
85
86
  message = await websocket.recv()
87
+ message_received_time = datetime.now()
86
88
  if not message:
87
89
  continue
88
90
  logger.debug(message)
@@ -97,7 +99,7 @@ async def listen_websockets():
97
99
  logger.info
98
100
  if current_clipboard != current_line:
99
101
  try:
100
- await handle_new_text_event(current_clipboard, line_time if line_time else None)
102
+ await handle_new_text_event(current_clipboard, line_time if line_time else message_received_time)
101
103
  except Exception as e:
102
104
  logger.error(f"Error handling new text event: {e}", exc_info=True)
103
105
  except (websockets.ConnectionClosed, ConnectionError, InvalidStatus, ConnectionResetError, Exception) as e:
@@ -1,10 +1,11 @@
1
- import tempfile
2
- import time
3
1
  import asyncio
2
+ import os
3
+ import shutil
4
4
  import subprocess
5
5
  import sys
6
-
7
- import os
6
+ import tempfile
7
+ import threading
8
+ import time
8
9
  import warnings
9
10
 
10
11
  import requests
@@ -42,7 +43,10 @@ try:
42
43
  import psutil
43
44
 
44
45
  start_time = time.time()
45
- from GameSentenceMiner.util.configuration import *
46
+ from GameSentenceMiner.util.configuration import logger, gsm_state, get_config, anki_results, AnkiUpdateResult, \
47
+ get_temporary_directory, get_log_path, get_master_config, switch_profile_and_save, get_app_directory, gsm_status, \
48
+ is_windows, is_linux
49
+
46
50
  logger.debug(f"[Import] configuration: {time.time() - start_time:.3f}s")
47
51
 
48
52
  start_time = time.time()
@@ -113,10 +117,6 @@ try:
113
117
  logger.debug(
114
118
  f"[Import] util.text_log (GameLine, get_text_event, get_mined_line, get_all_lines, game_log): {time.time() - start_time:.3f}s")
115
119
 
116
- start_time = time.time()
117
- from GameSentenceMiner.util import *
118
- logger.debug(f"[Import] util *: {time.time() - start_time:.3f}s")
119
-
120
120
  start_time = time.time()
121
121
  from GameSentenceMiner.web import texthooking_page
122
122
  logger.debug(
@@ -675,6 +675,7 @@ def initialize_async():
675
675
 
676
676
 
677
677
  def handle_websocket_message(message: Message):
678
+ logger.info(f"WebSocket Message Received: {message.to_json()}")
678
679
  try:
679
680
  match FunctionName(message.function):
680
681
  case FunctionName.QUIT:
@@ -683,7 +684,7 @@ def handle_websocket_message(message: Message):
683
684
  case FunctionName.QUIT_OBS:
684
685
  close_obs()
685
686
  case FunctionName.START_OBS:
686
- obs.start_obs()
687
+ obs.start_obs(force_restart=not gsm_status.obs_connected)
687
688
  case FunctionName.OPEN_SETTINGS:
688
689
  open_settings()
689
690
  case FunctionName.OPEN_TEXTHOOKER:
@@ -6,6 +6,7 @@ import threading
6
6
  import time
7
7
  import logging
8
8
  import contextlib
9
+ import shutil
9
10
 
10
11
  import psutil
11
12
 
@@ -164,38 +165,57 @@ class OBSConnectionManager(threading.Thread):
164
165
  self.counter = 0
165
166
  return
166
167
  start_replay_buffer()
167
-
168
+
168
169
  def get_obs_path():
169
170
  return os.path.join(configuration.get_app_directory(), 'obs-studio/bin/64bit/obs64.exe')
170
171
 
171
172
  def is_process_running(pid):
172
173
  try:
173
174
  process = psutil.Process(pid)
174
- return 'obs' in process.exe()
175
+ return 'obs' in process.exe().lower()
175
176
  except (psutil.NoSuchProcess, psutil.AccessDenied, OSError):
176
177
  if os.path.exists(OBS_PID_FILE):
177
178
  os.remove(OBS_PID_FILE)
178
179
  return False
179
180
 
180
- def start_obs():
181
+ def start_obs(force_restart=False):
181
182
  global obs_process_pid
182
183
  if os.path.exists(OBS_PID_FILE):
183
184
  with open(OBS_PID_FILE, "r") as f:
184
185
  try:
185
186
  obs_process_pid = int(f.read().strip())
186
187
  if is_process_running(obs_process_pid):
187
- print(f"OBS is already running with PID: {obs_process_pid}")
188
- return obs_process_pid
188
+ if force_restart:
189
+ try:
190
+ process = psutil.Process(obs_process_pid)
191
+ process.terminate()
192
+ process.wait(timeout=10)
193
+ print("OBS process terminated for restart.")
194
+ except Exception as e:
195
+ print(f"Error terminating OBS process: {e}")
196
+ else:
197
+ return obs_process_pid
189
198
  except ValueError:
190
199
  print("Invalid PID found in file. Launching new OBS instance.")
191
200
  except OSError:
192
- print("No process found with the stored PID. Launching new OBS instance.")
201
+ print("No process found with the stored PID. Launching new OBS instance.")
193
202
 
194
203
  obs_path = get_obs_path()
195
204
  if not os.path.exists(obs_path):
196
205
  print(f"OBS not found at {obs_path}. Please install OBS.")
197
206
  return None
198
207
  try:
208
+ sentinel_folder = os.path.join(configuration.get_app_directory(), 'obs-studio', 'config', 'obs-studio', '.sentinel')
209
+ if os.path.exists(sentinel_folder):
210
+ try:
211
+ if os.path.isdir(sentinel_folder):
212
+ shutil.rmtree(sentinel_folder)
213
+ else:
214
+ os.remove(sentinel_folder)
215
+ print(f"Deleted sentinel folder: {sentinel_folder}")
216
+ except Exception as e:
217
+ print(f"Failed to delete sentinel folder: {e}")
218
+
199
219
  obs_process = subprocess.Popen([obs_path, '--disable-shutdown-check', '--portable', '--startreplaybuffer', ], cwd=os.path.dirname(obs_path))
200
220
  obs_process_pid = obs_process.pid
201
221
  with open(OBS_PID_FILE, "w") as f:
@@ -663,8 +683,23 @@ def main():
663
683
  update_current_game()
664
684
  print("Testing `get_current_game`:", get_current_game())
665
685
  disconnect_from_obs()
686
+
687
+ def create_scene():
688
+ with connection_pool.get_client() as client:
689
+ # Extract fields from request_json
690
+ request_json = r'{"sceneName":"SILENT HILL f","inputName":"SILENT HILL f - Capture","inputKind":"window_capture","inputSettings":{"mode":"window","window":"SILENT HILL f :UnrealWindow:SHf-Win64-Shipping.exe","capture_audio":true,"cursor":false,"method":"2"}}'
691
+ request_dict = json.loads(request_json)
692
+ scene_name = request_dict.get('sceneName')
693
+ input_name = request_dict.get('inputName')
694
+ input_kind = request_dict.get('inputKind')
695
+ input_settings = request_dict.get('inputSettings')
696
+ input_settings['method'] = 2
697
+ # Remove sceneName from request_dict if needed for create_input
698
+ request_dict.pop('sceneName', None)
699
+ response = client.create_input(inputName=input_name, inputKind=input_kind, sceneName=scene_name, inputSettings=input_settings, sceneItemEnabled=True)
666
700
 
667
701
  if __name__ == '__main__':
668
702
  logging.basicConfig(level=logging.INFO)
669
703
  connect_to_obs_sync()
670
- set_fit_to_screen_for_scene_items(get_current_scene())
704
+ # set_fit_to_screen_for_scene_items(get_current_scene())
705
+ create_scene()
@@ -328,6 +328,7 @@ class ScreenSelector:
328
328
  "How to Use:\n"
329
329
  " • Left Click + Drag: Create a capture area (green).\n"
330
330
  " • Shift + Left Click + Drag: Create an exclusion area (orange).\n"
331
+ " • Ctrl + Left Click + Drag: Create a secondary (menu) area (purple).\n"
331
332
  " • Right-Click on a box: Delete it.\n\n"
332
333
  "Hotkeys:\n"
333
334
  " • Ctrl + S: Save and Quit\n"
@@ -19,13 +19,15 @@ from PIL import Image
19
19
  from rapidfuzz import fuzz
20
20
 
21
21
  from GameSentenceMiner import obs
22
- from GameSentenceMiner.util.electron_config import *
23
22
  from GameSentenceMiner.ocr.ss_picker import ScreenCropper
24
23
  from GameSentenceMiner.owocr.owocr.run import TextFiltering
25
24
  from GameSentenceMiner.util.configuration import get_config, get_app_directory, get_temporary_directory
26
25
  from GameSentenceMiner.ocr.gsm_ocr_config import OCRConfig, has_config_changed, set_dpi_awareness, get_window, get_ocr_config_path
27
- from GameSentenceMiner.owocr.owocr import screen_coordinate_picker, run
28
- from GameSentenceMiner.util.gsm_utils import sanitize_filename, do_text_replacements, OCR_REPLACEMENTS_FILE
26
+ from GameSentenceMiner.owocr.owocr import run
27
+ from GameSentenceMiner.util.electron_config import get_ocr_ocr2, get_ocr_send_to_clipboard, get_ocr_scan_rate, \
28
+ has_ocr_config_changed, reload_electron_config, get_ocr_two_pass_ocr, get_ocr_optimize_second_scan, \
29
+ get_ocr_language, get_ocr_manual_ocr_hotkey
30
+ from GameSentenceMiner.util.gsm_utils import sanitize_filename
29
31
  import threading
30
32
  import time
31
33
 
@@ -420,7 +422,7 @@ def get_ocr2_image(crop_coords, og_image: Image.Image, ocr2_engine=None):
420
422
  Logic is unchanged, but code is refactored for clarity and maintainability.
421
423
  """
422
424
  def return_original_image():
423
- logger.info("Returning original image for OCR2 (no cropping or optimization).")
425
+ logger.debug("Returning original image for OCR2 (no cropping or optimization).")
424
426
  if not crop_coords or not get_ocr_optimize_second_scan():
425
427
  return og_image
426
428
  x1, y1, x2, y2 = crop_coords
@@ -546,9 +548,10 @@ def add_ss_hotkey(ss_hotkey="ctrl+shift+g"):
546
548
  ocr_config = get_ocr_config()
547
549
  img = obs.get_screenshot_PIL(compression=80, img_format="jpg")
548
550
  ocr_config.scale_to_custom_size(img.width, img.height)
549
- img = run.apply_ocr_config_to_image(img, ocr_config, is_secondary=True)
550
- do_second_ocr("", datetime.now(), img, TextFiltering(lang=get_ocr_language()), ignore_furigana_filter=True, ignore_previous_result=True)
551
-
551
+ for rectangle in [rectangle for rectangle in ocr_config.rectangles if rectangle.is_secondary]:
552
+ new_img = run.apply_ocr_config_to_image(img, ocr_config, is_secondary=True, rectangles=[rectangle])
553
+ do_second_ocr("", datetime.now(), new_img, TextFiltering(lang=get_ocr_language()), ignore_furigana_filter=True, ignore_previous_result=True)
554
+
552
555
  filtering = TextFiltering(lang=get_ocr_language())
553
556
  cropper = ScreenCropper()
554
557
  def capture():
@@ -1,6 +1,11 @@
1
+ import os
2
+ import sys
3
+
1
4
  from GameSentenceMiner.ocr.gsm_ocr_config import set_dpi_awareness, get_scene_ocr_config
2
- from GameSentenceMiner.util.electron_config import * # noqa: F403
3
5
  from GameSentenceMiner.util.gsm_utils import do_text_replacements, OCR_REPLACEMENTS_FILE
6
+ from GameSentenceMiner.util.electron_config import get_ocr_language, get_ocr_requires_open_window, \
7
+ has_ocr_config_changed, reload_electron_config, get_ocr_scan_rate, get_ocr_two_pass_ocr, get_ocr_keep_newline, \
8
+ get_ocr_ocr1, get_furigana_filter_sensitivity
4
9
 
5
10
  try:
6
11
  import win32gui
@@ -1135,14 +1140,15 @@ def scale_down_width_height(width, height):
1135
1140
  return width, height
1136
1141
 
1137
1142
 
1138
- def apply_ocr_config_to_image(img, ocr_config, is_secondary=False):
1143
+ def apply_ocr_config_to_image(img, ocr_config, is_secondary=False, rectangles=None):
1139
1144
  for rectangle in ocr_config.rectangles:
1140
1145
  if rectangle.is_excluded:
1141
1146
  left, top, width, height = rectangle.coordinates
1142
1147
  draw = ImageDraw.Draw(img)
1143
1148
  draw.rectangle((left, top, left + width, top + height), fill=(0, 0, 0, 0))
1144
1149
 
1145
- rectangles = [r for r in ocr_config.rectangles if not r.is_excluded and r.is_secondary == is_secondary]
1150
+ if not rectangles:
1151
+ rectangles = [r for r in ocr_config.rectangles if not r.is_excluded and r.is_secondary == is_secondary]
1146
1152
 
1147
1153
  # Sort top to bottom
1148
1154
  if rectangles:
@@ -1,13 +1,16 @@
1
+ import json
2
+ import os
1
3
  import subprocess
4
+ import sys
2
5
  import tempfile
3
6
  import time
4
7
  from pathlib import Path
5
8
 
6
9
  from GameSentenceMiner import obs
10
+ from GameSentenceMiner.util.configuration import get_app_directory, is_windows, logger, get_config, \
11
+ get_temporary_directory, gsm_state, is_linux
7
12
  from GameSentenceMiner.util.gsm_utils import make_unique_file_name, get_file_modification_time
8
13
  from GameSentenceMiner.util import configuration
9
- from GameSentenceMiner.util.configuration import *
10
- from GameSentenceMiner.util.model import VADResult
11
14
  from GameSentenceMiner.util.text_log import initial_time
12
15
 
13
16
 
@@ -1,11 +1,14 @@
1
+ import json
2
+ import logging
3
+ import os
4
+ import shutil
1
5
  import tempfile
2
6
  import time
3
7
  import warnings
4
- import requests
5
8
  from abc import abstractmethod, ABC
6
9
 
7
10
  from GameSentenceMiner.util import configuration, ffmpeg
8
- from GameSentenceMiner.util.configuration import *
11
+ from GameSentenceMiner.util.configuration import get_config, get_temporary_directory, logger, SILERO, WHISPER
9
12
  from GameSentenceMiner.util.ffmpeg import get_audio_length
10
13
  from GameSentenceMiner.util.gsm_utils import make_unique_file_name, run_new_thread
11
14
  from GameSentenceMiner.util.model import VADResult
@@ -335,8 +335,8 @@ def register_database_api_routes(app):
335
335
  if afk_timer is not None:
336
336
  try:
337
337
  afk_timer = int(afk_timer)
338
- if afk_timer < 30 or afk_timer > 600:
339
- return jsonify({'error': 'AFK timer must be between 30 and 600 seconds'}), 400
338
+ if afk_timer < 0 or afk_timer > 600:
339
+ return jsonify({'error': 'AFK timer must be between 0 and 600 seconds'}), 400
340
340
  settings_to_update['afk_timer_seconds'] = afk_timer
341
341
  except (ValueError, TypeError):
342
342
  return jsonify({'error': 'AFK timer must be a valid integer'}), 400
@@ -344,8 +344,8 @@ def register_database_api_routes(app):
344
344
  if session_gap is not None:
345
345
  try:
346
346
  session_gap = int(session_gap)
347
- if session_gap < 300 or session_gap > 7200:
348
- return jsonify({'error': 'Session gap must be between 300 and 7200 seconds (5 minutes to 2 hours)'}), 400
347
+ if session_gap < 0 or session_gap > 7200:
348
+ return jsonify({'error': 'Session gap must be between 0 and 7200 seconds (0 to 2 hours)'}), 400
349
349
  settings_to_update['session_gap_seconds'] = session_gap
350
350
  except (ValueError, TypeError):
351
351
  return jsonify({'error': 'Session gap must be a valid integer'}), 400
@@ -392,8 +392,8 @@ class SettingsManager {
392
392
 
393
393
  if (this.afkTimerInput) {
394
394
  const afkTimer = parseInt(this.afkTimerInput.value);
395
- if (isNaN(afkTimer) || afkTimer < 30 || afkTimer > 600) {
396
- this.showError('AFK timer must be between 30 and 600 seconds');
395
+ if (isNaN(afkTimer) || afkTimer < 0 || afkTimer > 600) {
396
+ this.showError('AFK timer must be between 0 and 600 seconds');
397
397
  return;
398
398
  }
399
399
  settings.afk_timer_seconds = afkTimer;
@@ -401,8 +401,8 @@ class SettingsManager {
401
401
 
402
402
  if (this.sessionGapInput) {
403
403
  const sessionGap = parseInt(this.sessionGapInput.value);
404
- if (isNaN(sessionGap) || sessionGap < 300 || sessionGap > 7200) {
405
- this.showError('Session gap must be between 300 and 7200 seconds');
404
+ if (isNaN(sessionGap) || sessionGap < 0 || sessionGap > 7200) {
405
+ this.showError('Session gap must be between 0 and 7200 seconds (0 to 2 hours)');
406
406
  return;
407
407
  }
408
408
  settings.session_gap_seconds = sessionGap;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.16.13
3
+ Version: 2.17.1
4
4
  Summary: A tool for mining sentences from games. Update: Overlay?
5
5
  Author-email: Beangate <bpwhelan95@gmail.com>
6
6
  License: MIT License
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.16.13
3
+ Version: 2.17.1
4
4
  Summary: A tool for mining sentences from games. Update: Overlay?
5
5
  Author-email: Beangate <bpwhelan95@gmail.com>
6
6
  License: MIT License
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "GameSentenceMiner"
10
- version = "2.16.13"
10
+ version = "2.17.1"
11
11
  description = "A tool for mining sentences from games. Update: Overlay?"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.10"
@@ -61,8 +61,8 @@ dependencies = [
61
61
  ]
62
62
 
63
63
  # This creates a command-line script named `gamesentenceminer` that will
64
- # call the `main()` function defined in the packages __main__.py (or wherever
65
- # youve placed your entry function). Adjust the import path as needed.
64
+ # call the `main()` function defined in the package's __main__.py (or wherever
65
+ # you've placed your entry function). Adjust the import path as needed.
66
66
  [project.scripts]
67
67
  gamesentenceminer = "GameSentenceMiner.gsm:main"
68
68