GameSentenceMiner 2.14.8__tar.gz → 2.14.10__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 (84) hide show
  1. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/config_gui.py +19 -10
  2. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/gsm.py +68 -8
  3. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/locales/en_us.json +4 -0
  4. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/locales/ja_jp.json +4 -0
  5. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/locales/zh_cn.json +4 -0
  6. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/obs.py +12 -8
  7. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/ocr/owocr_helper.py +1 -1
  8. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/owocr/owocr/run.py +15 -14
  9. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/configuration.py +1 -0
  10. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/notification.py +11 -1
  11. gamesentenceminer-2.14.10/GameSentenceMiner/util/win10toast/__init__.py +154 -0
  12. gamesentenceminer-2.14.10/GameSentenceMiner/util/win10toast/__main__.py +22 -0
  13. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner.egg-info/PKG-INFO +1 -2
  14. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner.egg-info/SOURCES.txt +2 -0
  15. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner.egg-info/requires.txt +0 -1
  16. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/PKG-INFO +1 -2
  17. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/pyproject.toml +2 -3
  18. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/__init__.py +0 -0
  19. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/ai/__init__.py +0 -0
  20. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/ai/ai_prompting.py +0 -0
  21. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/anki.py +0 -0
  22. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/assets/__init__.py +0 -0
  23. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/assets/icon.png +0 -0
  24. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/assets/icon128.png +0 -0
  25. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/assets/icon256.png +0 -0
  26. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/assets/icon32.png +0 -0
  27. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/assets/icon512.png +0 -0
  28. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/assets/icon64.png +0 -0
  29. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/assets/pickaxe.png +0 -0
  30. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/gametext.py +0 -0
  31. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/ocr/__init__.py +0 -0
  32. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/ocr/gsm_ocr_config.py +0 -0
  33. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/ocr/ocrconfig.py +0 -0
  34. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/ocr/owocr_area_selector.py +0 -0
  35. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/ocr/ss_picker.py +0 -0
  36. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/owocr/owocr/__init__.py +0 -0
  37. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/owocr/owocr/__main__.py +0 -0
  38. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/owocr/owocr/config.py +0 -0
  39. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/owocr/owocr/lens_betterproto.py +0 -0
  40. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/owocr/owocr/ocr.py +0 -0
  41. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py +0 -0
  42. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/tools/__init__.py +0 -0
  43. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/tools/audio_offset_selector.py +0 -0
  44. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/tools/ss_selector.py +0 -0
  45. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/tools/window_transparency.py +0 -0
  46. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/__init__.py +0 -0
  47. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/communication/__init__.py +0 -0
  48. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/communication/send.py +0 -0
  49. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/communication/websocket.py +0 -0
  50. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/db.py +0 -0
  51. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/downloader/Untitled_json.py +0 -0
  52. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/downloader/__init__.py +0 -0
  53. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/downloader/download_tools.py +0 -0
  54. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/downloader/oneocr_dl.py +0 -0
  55. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/electron_config.py +0 -0
  56. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/ffmpeg.py +0 -0
  57. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/get_overlay_coords.py +0 -0
  58. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/gsm_utils.py +0 -0
  59. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/model.py +0 -0
  60. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/util/text_log.py +0 -0
  61. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/vad.py +0 -0
  62. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/__init__.py +0 -0
  63. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/service.py +0 -0
  64. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/static/__init__.py +0 -0
  65. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/static/apple-touch-icon.png +0 -0
  66. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/static/favicon-96x96.png +0 -0
  67. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/static/favicon.ico +0 -0
  68. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/static/favicon.svg +0 -0
  69. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/static/site.webmanifest +0 -0
  70. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/static/style.css +0 -0
  71. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/static/web-app-manifest-192x192.png +0 -0
  72. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/static/web-app-manifest-512x512.png +0 -0
  73. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/templates/__init__.py +0 -0
  74. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/templates/index.html +0 -0
  75. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/templates/text_replacements.html +0 -0
  76. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/templates/utility.html +0 -0
  77. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/web/texthooking_page.py +0 -0
  78. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner/wip/__init___.py +0 -0
  79. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner.egg-info/dependency_links.txt +0 -0
  80. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner.egg-info/entry_points.txt +0 -0
  81. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/GameSentenceMiner.egg-info/top_level.txt +0 -0
  82. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/LICENSE +0 -0
  83. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/README.md +0 -0
  84. {gamesentenceminer-2.14.8 → gamesentenceminer-2.14.10}/setup.cfg +0 -0
@@ -212,6 +212,7 @@ class ConfigApp:
212
212
 
213
213
  self.create_vars()
214
214
  self.create_tabs()
215
+ self.get_online_models()
215
216
  self.notebook.bind("<<NotebookTabChanged>>", self.on_profiles_tab_selected)
216
217
 
217
218
  button_frame = ttk.Frame(self.window)
@@ -285,6 +286,7 @@ class ConfigApp:
285
286
  self.obs_close_obs_value = tk.BooleanVar(value=self.settings.obs.close_obs)
286
287
  self.obs_get_game_from_scene_name_value = tk.BooleanVar(value=self.settings.obs.get_game_from_scene)
287
288
  self.obs_minimum_replay_size_value = tk.StringVar(value=str(self.settings.obs.minimum_replay_size))
289
+ self.obs_turn_off_output_check_value = tk.BooleanVar(value=self.settings.obs.turn_off_output_check)
288
290
 
289
291
  # Paths Settings
290
292
  self.folder_to_watch_value = tk.StringVar(value=self.settings.paths.folder_to_watch)
@@ -567,7 +569,8 @@ class ConfigApp:
567
569
  port=int(self.obs_port_value.get()),
568
570
  password=self.obs_password_value.get(),
569
571
  get_game_from_scene=self.obs_get_game_from_scene_name_value.get(),
570
- minimum_replay_size=int(self.obs_minimum_replay_size_value.get())
572
+ minimum_replay_size=int(self.obs_minimum_replay_size_value.get()),
573
+ turn_off_output_check=self.obs_turn_off_output_check_value.get()
571
574
  ),
572
575
  hotkeys=Hotkeys(
573
576
  reset_line=self.reset_line_hotkey_value.get(),
@@ -1721,6 +1724,14 @@ class ConfigApp:
1721
1724
  ttk.Entry(obs_frame, textvariable=self.obs_minimum_replay_size_value).grid(row=self.current_row, column=1, sticky='EW', pady=2)
1722
1725
  self.current_row += 1
1723
1726
 
1727
+ turn_off_output_check_i18n = obs_i18n.get('turn_off_output_check', {})
1728
+ HoverInfoLabelWidget(obs_frame, text=turn_off_output_check_i18n.get('label', '...'),
1729
+ tooltip=turn_off_output_check_i18n.get('tooltip', '...'),
1730
+ row=self.current_row, column=0)
1731
+ ttk.Checkbutton(obs_frame, variable=self.obs_turn_off_output_check_value, bootstyle="round-toggle").grid(
1732
+ row=self.current_row, column=1, sticky='W', pady=2)
1733
+ self.current_row += 1
1734
+
1724
1735
  self.add_reset_button(obs_frame, "obs", self.current_row, 0, self.create_obs_tab)
1725
1736
 
1726
1737
  for col in range(3): obs_frame.grid_columnconfigure(col, weight=0)
@@ -1968,9 +1979,7 @@ class ConfigApp:
1968
1979
  self.groq_models_combobox = ttk.Combobox(ai_frame, textvariable=self.groq_model_value, values=RECOMMENDED_GROQ_MODELS, state="readonly")
1969
1980
  self.groq_models_combobox.grid(row=self.current_row, column=1, sticky='EW', pady=2)
1970
1981
  self.current_row += 1
1971
-
1972
- self.get_online_models()
1973
-
1982
+
1974
1983
  groq_key_i18n = ai_i18n.get('groq_api_key', {})
1975
1984
  HoverInfoLabelWidget(ai_frame, text=groq_key_i18n.get('label', '...'), tooltip=groq_key_i18n.get('tooltip', '...'),
1976
1985
  row=self.current_row, column=0)
@@ -1980,8 +1989,6 @@ class ConfigApp:
1980
1989
  groq_apikey_entry.bind("<Return>", lambda e, row=self.current_row: self.get_online_models())
1981
1990
  self.current_row += 1
1982
1991
 
1983
-
1984
-
1985
1992
  openai_url_i18n = ai_i18n.get('openai_url', {})
1986
1993
  HoverInfoLabelWidget(ai_frame, text=openai_url_i18n.get('label', '...'), tooltip=openai_url_i18n.get('tooltip', '...'),
1987
1994
  row=self.current_row, column=0)
@@ -1994,6 +2001,7 @@ class ConfigApp:
1994
2001
 
1995
2002
  self.openai_model_options = []
1996
2003
  self.update_models_element(ai_frame, self.current_row)
2004
+ # threading.Thread(target=self.update_models_element, args=(ai_frame, self.current_row)).start()
1997
2005
  self.current_row += 1
1998
2006
 
1999
2007
 
@@ -2050,7 +2058,7 @@ class ConfigApp:
2050
2058
  def get_online_models(self):
2051
2059
  ai_models = AIModelsTable.one()
2052
2060
 
2053
- def get_models_thread():
2061
+ def get_models():
2054
2062
  groq_models = get_groq_models()
2055
2063
  gemini_models = get_gemini_models()
2056
2064
  AIModelsTable.update_models(gemini_models, groq_models)
@@ -2099,12 +2107,13 @@ class ConfigApp:
2099
2107
  if ai_models and ai_models.gemini_models and ai_models.groq_models:
2100
2108
  if time.time() - ai_models.last_updated > 3600 * 6:
2101
2109
  print("AI models are outdated, fetching new ones.")
2102
- threading.Thread(target=get_models_thread, daemon=True).start()
2110
+ threading.Thread(target=get_models, daemon=True).start()
2103
2111
  self.gemini_model_combobox['values'] = ai_models.gemini_models
2104
2112
  self.groq_models_combobox['values'] = ai_models.groq_models
2105
2113
  else:
2106
2114
  print("No AI models found, fetching new ones.")
2107
- threading.Thread(target=get_models_thread, daemon=True).start()
2115
+ threading.Thread(target=get_models, daemon=True).start()
2116
+ # get_models()
2108
2117
 
2109
2118
  def update_models_element(self, frame, row):
2110
2119
  if hasattr(self, 'last_url') and self.last_url == self.open_ai_url_value.get().strip():
@@ -2114,7 +2123,7 @@ class ConfigApp:
2114
2123
  if self.open_ai_url_value.get().strip() != "" and any(c in self.open_ai_url_value.get() for c in ["localhost", "127.0.0.1"]):
2115
2124
  import openai
2116
2125
  # get models from openai compatible url
2117
- client = openai.Client(api_key=self.settings.ai.open_ai_api_key, base_url=self.open_ai_url_value.get().strip())
2126
+ client = openai.Client(api_key=self.settings.ai.open_ai_api_key, base_url=self.open_ai_url_value.get().strip(), timeout=1)
2118
2127
  try:
2119
2128
  models = client.models.list()
2120
2129
  if models:
@@ -34,27 +34,85 @@ try:
34
34
  from watchdog.observers import Observer
35
35
  import psutil
36
36
 
37
+ start_time = time.time()
38
+ from GameSentenceMiner.util.configuration import *
39
+ logger.debug(f"[Import] configuration: {time.time() - start_time:.3f}s")
40
+
41
+ start_time = time.time()
37
42
  from GameSentenceMiner.util.model import VADResult
43
+ logger.debug(f"[Import] VADResult model: {time.time() - start_time:.3f}s")
44
+
45
+ start_time = time.time()
38
46
  from GameSentenceMiner.vad import vad_processor
47
+ logger.debug(f"[Import] vad_processor: {time.time() - start_time:.3f}s")
48
+
49
+ start_time = time.time()
39
50
  from GameSentenceMiner.util.downloader.download_tools import download_obs_if_needed, download_ffmpeg_if_needed
51
+ logger.debug(f"[Import] download_tools (download_obs_if_needed, download_ffmpeg_if_needed): {time.time() - start_time:.3f}s")
52
+
53
+ start_time = time.time()
40
54
  from GameSentenceMiner.util.communication.send import send_restart_signal
55
+ logger.debug(f"[Import] send_restart_signal: {time.time() - start_time:.3f}s")
56
+
57
+ start_time = time.time()
41
58
  from GameSentenceMiner.util.gsm_utils import wait_for_stable_file, make_unique_file_name, run_new_thread
59
+ logger.debug(f"[Import] gsm_utils (wait_for_stable_file, make_unique_file_name, run_new_thread): {time.time() - start_time:.3f}s")
60
+
61
+ start_time = time.time()
42
62
  from GameSentenceMiner import anki
63
+ logger.debug(f"[Import] anki: {time.time() - start_time:.3f}s")
64
+
65
+ start_time = time.time()
43
66
  from GameSentenceMiner import config_gui
67
+ logger.debug(f"[Import] config_gui: {time.time() - start_time:.3f}s")
68
+
69
+ start_time = time.time()
44
70
  from GameSentenceMiner.util import configuration, notification, ffmpeg
71
+ logger.debug(f"[Import] util (configuration, notification, ffmpeg): {time.time() - start_time:.3f}s")
72
+
73
+ start_time = time.time()
45
74
  from GameSentenceMiner import gametext
75
+ logger.debug(f"[Import] gametext: {time.time() - start_time:.3f}s")
76
+
77
+ start_time = time.time()
46
78
  from GameSentenceMiner import obs
79
+ logger.debug(f"[Import] obs: {time.time() - start_time:.3f}s")
80
+
81
+ start_time = time.time()
47
82
  from GameSentenceMiner.util.communication import Message
48
- from GameSentenceMiner.util.communication.websocket import connect_websocket, register_websocket_message_handler, \
49
- FunctionName
50
- from GameSentenceMiner.util.configuration import *
83
+ logger.debug(f"[Import] Message: {time.time() - start_time:.3f}s")
84
+
85
+ start_time = time.time()
86
+ from GameSentenceMiner.util.communication.websocket import connect_websocket, register_websocket_message_handler, FunctionName
87
+ logger.debug(f"[Import] websocket (connect_websocket, register_websocket_message_handler, FunctionName): {time.time() - start_time:.3f}s")
88
+
89
+ start_time = time.time()
51
90
  from GameSentenceMiner.util.ffmpeg import get_audio_and_trim, get_video_timings, get_ffmpeg_path
91
+ logger.debug(f"[Import] util.ffmpeg (get_audio_and_trim, get_video_timings, get_ffmpeg_path): {time.time() - start_time:.3f}s")
92
+
93
+ start_time = time.time()
52
94
  from GameSentenceMiner.obs import check_obs_folder_is_correct
95
+ logger.debug(f"[Import] obs.check_obs_folder_is_correct: {time.time() - start_time:.3f}s")
96
+
97
+ start_time = time.time()
53
98
  from GameSentenceMiner.util.text_log import GameLine, get_text_event, get_mined_line, get_all_lines, game_log
99
+ logger.debug(f"[Import] util.text_log (GameLine, get_text_event, get_mined_line, get_all_lines, game_log): {time.time() - start_time:.3f}s")
100
+
101
+ start_time = time.time()
54
102
  from GameSentenceMiner.util import *
103
+ logger.debug(f"[Import] util *: {time.time() - start_time:.3f}s")
104
+
105
+ start_time = time.time()
55
106
  from GameSentenceMiner.web import texthooking_page
107
+ logger.debug(f"[Import] web.texthooking_page: {time.time() - start_time:.3f}s")
108
+
109
+ start_time = time.time()
56
110
  from GameSentenceMiner.web.service import handle_texthooker_button, set_get_audio_from_video_callback
111
+ logger.debug(f"[Import] web.service (handle_texthooker_button, set_get_audio_from_video_callback): {time.time() - start_time:.3f}s")
112
+
113
+ start_time = time.time()
57
114
  from GameSentenceMiner.web.texthooking_page import run_text_hooker_page
115
+ logger.debug(f"[Import] web.texthooking_page.run_text_hooker_page: {time.time() - start_time:.3f}s")
58
116
  except Exception as e:
59
117
  from GameSentenceMiner.util.configuration import logger, is_linux, is_windows
60
118
  handle_error_in_initialization(e)
@@ -583,7 +641,7 @@ def handle_websocket_message(message: Message):
583
641
  f"unknown message from electron websocket: {message.to_json()}")
584
642
 
585
643
 
586
- def post_init2():
644
+ def initialize_text_monitor():
587
645
  asyncio.run(gametext.start_text_monitor())
588
646
 
589
647
 
@@ -668,8 +726,8 @@ async def async_main(reloading=False):
668
726
  try:
669
727
  global root, settings_window
670
728
  initialize(reloading)
671
- logger.info("Script started.")
672
729
  root = ttk.Window(themename='darkly')
730
+ start_time = time.time()
673
731
  settings_window = config_gui.ConfigApp(root)
674
732
  initialize_async()
675
733
  observer = Observer()
@@ -678,10 +736,12 @@ async def async_main(reloading=False):
678
736
  observer.start()
679
737
  if is_windows():
680
738
  register_hotkeys()
681
-
682
- run_new_thread(post_init2)
739
+
740
+ run_new_thread(initialize_text_monitor)
683
741
  run_new_thread(run_text_hooker_page)
684
- run_new_thread(async_loop)
742
+ run_new_thread(async_loop).join()
743
+
744
+ logger.info("Initialization complete. Happy Mining! がんばれ!")
685
745
 
686
746
  # await check_if_script_is_running()
687
747
  # await log_current_pid()
@@ -415,6 +415,10 @@
415
415
  "min_replay_size": {
416
416
  "label": "Minimum Replay Size (KB):",
417
417
  "tooltip": "Minimum Replay Size for OBS Replays in KB. If Replay is Under this, Audio/Screenshot Will not be grabbed."
418
+ },
419
+ "turn_off_output_check": {
420
+ "label": "Turn Off Output Check:",
421
+ "tooltip": "Disable the video output Replay Buffer Check."
418
422
  }
419
423
  },
420
424
  "profiles": {
@@ -414,6 +414,10 @@
414
414
  "min_replay_size": {
415
415
  "label": "最小リプレイサイズ (KB):",
416
416
  "tooltip": "このサイズ未満のリプレイは処理しません。"
417
+ },
418
+ "turn_off_output_check": {
419
+ "label": "出力チェックを無効化:",
420
+ "tooltip": "動画出力のリプレイバッファチェックを無効化します。"
417
421
  }
418
422
  },
419
423
  "profiles": {
@@ -415,6 +415,10 @@
415
415
  "min_replay_size": {
416
416
  "label": "最小回放大小 (KB):",
417
417
  "tooltip": "OBS 录像回放的最小大小(KB)。如果回放小于此大小,将不抓取音频/截图。"
418
+ },
419
+ "turn_off_output_check": {
420
+ "label": "关闭输出检查:",
421
+ "tooltip": "禁用视频输出的回放缓冲区检查。"
418
422
  }
419
423
  },
420
424
  "profiles": {
@@ -24,13 +24,14 @@ logging.getLogger("obsws_python").setLevel(logging.CRITICAL)
24
24
  connecting = False
25
25
 
26
26
  class OBSConnectionManager(threading.Thread):
27
- def __init__(self):
27
+ def __init__(self, check_output=True):
28
28
  super().__init__()
29
29
  self.daemon = True
30
30
  self.running = True
31
31
  self.check_connection_interval = 1
32
32
  self.said_no_to_replay_buffer = False
33
33
  self.counter = 0
34
+ self.check_output = check_output
34
35
 
35
36
  def run(self):
36
37
  while self.running:
@@ -42,13 +43,16 @@ class OBSConnectionManager(threading.Thread):
42
43
  logger.info(f"OBS WebSocket not connected. Attempting to reconnect... {e}")
43
44
  gsm_status.obs_connected = False
44
45
  asyncio.run(connect_to_obs())
45
- if self.counter % 5 == 0:
46
+ if self.counter % 5 == 0 and not get_config().obs.turn_off_output_check and self.check_output:
46
47
  replay_buffer_status = get_replay_buffer_status()
47
48
  if replay_buffer_status and self.said_no_to_replay_buffer:
48
49
  self.said_no_to_replay_buffer = False
49
50
  self.counter = 0
50
51
  if gsm_status.obs_connected and not replay_buffer_status and not self.said_no_to_replay_buffer:
51
- self.check_output()
52
+ try:
53
+ self.check_output()
54
+ except Exception as e:
55
+ pass
52
56
  self.counter += 1
53
57
 
54
58
  def stop(self):
@@ -172,7 +176,7 @@ def get_obs_websocket_config_values():
172
176
  full_config.save()
173
177
  reload_config()
174
178
 
175
- async def connect_to_obs(retry=5):
179
+ async def connect_to_obs(retry=5, check_output=True):
176
180
  global client, obs_connection_manager, event_client, connecting
177
181
  if is_windows():
178
182
  get_obs_websocket_config_values()
@@ -195,7 +199,7 @@ async def connect_to_obs(retry=5):
195
199
  gsm_status.obs_connected = True
196
200
  logger.info("Connected to OBS WebSocket.")
197
201
  if not obs_connection_manager:
198
- obs_connection_manager = OBSConnectionManager()
202
+ obs_connection_manager = OBSConnectionManager(check_output=check_output)
199
203
  obs_connection_manager.start()
200
204
  update_current_game()
201
205
  break # Exit the loop once connected
@@ -211,7 +215,7 @@ async def connect_to_obs(retry=5):
211
215
  retry -= 1
212
216
  connecting = False
213
217
 
214
- def connect_to_obs_sync(retry=2):
218
+ def connect_to_obs_sync(retry=2, check_output=True):
215
219
  global client, obs_connection_manager, event_client
216
220
  if is_windows():
217
221
  get_obs_websocket_config_values()
@@ -231,7 +235,7 @@ def connect_to_obs_sync(retry=2):
231
235
  timeout=1,
232
236
  )
233
237
  if not obs_connection_manager:
234
- obs_connection_manager = OBSConnectionManager()
238
+ obs_connection_manager = OBSConnectionManager(check_output=check_output)
235
239
  obs_connection_manager.start()
236
240
  update_current_game()
237
241
  logger.info("Connected to OBS WebSocket.")
@@ -293,7 +297,7 @@ def get_replay_buffer_status():
293
297
  try:
294
298
  return client.get_replay_buffer_status().output_active
295
299
  except Exception as e:
296
- logger.warning(f"Error getting replay buffer status: {e}")
300
+ logger.debug(f"Error getting replay buffer status: {e}")
297
301
  return None
298
302
 
299
303
  def stop_replay_buffer():
@@ -584,7 +584,7 @@ if __name__ == "__main__":
584
584
  keep_newline = args.keep_newline
585
585
  obs_ocr = args.obs_ocr
586
586
 
587
- obs.connect_to_obs_sync(retry=0)
587
+ obs.connect_to_obs_sync(retry=0, check_output=False)
588
588
 
589
589
  # Start config change checker thread
590
590
  config_check_thread = ConfigChangeCheckThread()
@@ -832,8 +832,8 @@ def set_last_image(image):
832
832
  last_image.close()
833
833
  except Exception:
834
834
  pass
835
- # last_image = image
836
- last_image = apply_adaptive_threshold_filter(image)
835
+ last_image = image
836
+ # last_image = apply_adaptive_threshold_filter(image)
837
837
 
838
838
 
839
839
  def are_images_identical(img1, img2):
@@ -1113,6 +1113,7 @@ class OBSScreenshotThread(threading.Thread):
1113
1113
  print(e)
1114
1114
  logger.info(
1115
1115
  f"An unexpected error occurred during OBS Capture : {e}", exc_info=True)
1116
+ time.sleep(.5)
1116
1117
  continue
1117
1118
 
1118
1119
 
@@ -1757,18 +1758,18 @@ def run(read_from=None,
1757
1758
  logger.debug(f"Could not determine if image is empty: {e}")
1758
1759
 
1759
1760
  # Compare images, but only if it's one box, multiple boxes skews results way too much and produces false positives
1760
- if ocr_config and len(ocr_config.rectangles) < 2:
1761
- if are_images_similar(img, last_image):
1762
- logger.info("Captured screenshot is similar to the last one, sleeping.")
1763
- if time.time() - last_result_time > 10:
1764
- sleep_time_to_add += .005
1765
- continue
1766
- else:
1767
- if are_images_identical(img, last_image):
1768
- logger.info("Captured screenshot is identical to the last one, sleeping.")
1769
- if time.time() - last_result_time > 10:
1770
- sleep_time_to_add += .005
1771
- continue
1761
+ # if ocr_config and len(ocr_config.rectangles) < 2:
1762
+ # if are_images_similar(img, last_image):
1763
+ # logger.info("Captured screenshot is similar to the last one, sleeping.")
1764
+ # if time.time() - last_result_time > 10:
1765
+ # sleep_time_to_add += .005
1766
+ # continue
1767
+ # else:
1768
+ if are_images_identical(img, last_image):
1769
+ logger.info("Captured screenshot is identical to the last one, sleeping.")
1770
+ if time.time() - last_result_time > 10:
1771
+ sleep_time_to_add += .005
1772
+ continue
1772
1773
 
1773
1774
  res, text = process_and_write_results(img, write_to, last_result, filtering, notify,
1774
1775
  ocr_start_time=ocr_start_time, furigana_filter_sensitivity=get_ocr_furigana_filter_sensitivity())
@@ -531,6 +531,7 @@ class OBS:
531
531
  password: str = "your_password"
532
532
  get_game_from_scene: bool = True
533
533
  minimum_replay_size: int = 0
534
+ turn_off_output_check: bool = False
534
535
 
535
536
 
536
537
  @dataclass_json
@@ -3,7 +3,7 @@ from plyer import notification
3
3
  from GameSentenceMiner.util.configuration import logger, is_windows
4
4
 
5
5
  if is_windows():
6
- from win10toast import ToastNotifier
6
+ from GameSentenceMiner.util.win10toast import ToastNotifier
7
7
 
8
8
  if is_windows():
9
9
  class MyToastNotifier(ToastNotifier):
@@ -145,3 +145,13 @@ def send_error_notification(message):
145
145
  message=message,
146
146
  timeout=5 # Notification disappears after 5 seconds
147
147
  )
148
+
149
+
150
+ if __name__ == "__main__":
151
+ send_note_updated("TestTango")
152
+ send_screenshot_updated("TestTango")
153
+ send_screenshot_saved("C:/Screenshots/test.png")
154
+ send_audio_generated_notification("C:/Audio/test.mp3")
155
+ send_check_obs_notification("Replay buffer not active")
156
+ send_error_no_anki_update()
157
+ send_error_notification("Custom error message for testing")
@@ -0,0 +1,154 @@
1
+ from __future__ import absolute_import
2
+ from __future__ import print_function
3
+ from __future__ import unicode_literals
4
+
5
+ __all__ = ['ToastNotifier']
6
+
7
+ # #############################################################################
8
+ # ########## Libraries #############
9
+ # ##################################
10
+ # standard library
11
+ import logging
12
+ import threading
13
+ from os import path
14
+ from time import sleep
15
+
16
+ # 3rd party modules
17
+ from win32api import GetModuleHandle
18
+ from win32api import PostQuitMessage
19
+ from win32con import CW_USEDEFAULT
20
+ from win32con import IDI_APPLICATION
21
+ from win32con import IMAGE_ICON
22
+ from win32con import LR_DEFAULTSIZE
23
+ from win32con import LR_LOADFROMFILE
24
+ from win32con import WM_DESTROY
25
+ from win32con import WM_USER
26
+ from win32con import WS_OVERLAPPED
27
+ from win32con import WS_SYSMENU
28
+ from win32gui import CreateWindow
29
+ from win32gui import DestroyWindow
30
+ from win32gui import LoadIcon
31
+ from win32gui import LoadImage
32
+ from win32gui import NIF_ICON
33
+ from win32gui import NIF_INFO
34
+ from win32gui import NIF_MESSAGE
35
+ from win32gui import NIF_TIP
36
+ from win32gui import NIM_ADD
37
+ from win32gui import NIM_DELETE
38
+ from win32gui import NIM_MODIFY
39
+ from win32gui import RegisterClass
40
+ from win32gui import UnregisterClass
41
+ from win32gui import Shell_NotifyIcon
42
+ from win32gui import UpdateWindow
43
+ from win32gui import WNDCLASS
44
+
45
+ # ############################################################################
46
+ # ########### Classes ##############
47
+ # ##################################
48
+
49
+
50
+ class ToastNotifier(object):
51
+ """Create a Windows 10 toast notification.
52
+
53
+ from: https://github.com/jithurjacob/Windows-10-Toast-Notifications
54
+ """
55
+
56
+ def __init__(self):
57
+ """Initialize."""
58
+ self._thread = None
59
+
60
+ def _show_toast(self, title, msg,
61
+ icon_path, duration):
62
+ """Notification settings.
63
+
64
+ :title: notification title
65
+ :msg: notification message
66
+ :icon_path: path to the .ico file to custom notification
67
+ :duration: delay in seconds before notification self-destruction
68
+ """
69
+ message_map = {WM_DESTROY: self.on_destroy, }
70
+
71
+ # Register the window class.
72
+ self.wc = WNDCLASS()
73
+ self.hinst = self.wc.hInstance = GetModuleHandle(None)
74
+ self.wc.lpszClassName = str("PythonTaskbar") # must be a string
75
+ self.wc.lpfnWndProc = message_map # could also specify a wndproc.
76
+ try:
77
+ self.classAtom = RegisterClass(self.wc)
78
+ except:
79
+ pass #not sure of this
80
+ style = WS_OVERLAPPED | WS_SYSMENU
81
+ self.hwnd = CreateWindow(self.classAtom, "Taskbar", style,
82
+ 0, 0, CW_USEDEFAULT,
83
+ CW_USEDEFAULT,
84
+ 0, 0, self.hinst, None)
85
+ UpdateWindow(self.hwnd)
86
+
87
+ # icon
88
+ if icon_path is not None:
89
+ icon_path = path.realpath(icon_path)
90
+ else:
91
+ icon_path = path.join(path.dirname(path.abspath(__file__)), 'data', 'python.ico')
92
+ icon_flags = LR_LOADFROMFILE | LR_DEFAULTSIZE
93
+ try:
94
+ hicon = LoadImage(self.hinst, icon_path,
95
+ IMAGE_ICON, 0, 0, icon_flags)
96
+ except Exception as e:
97
+ logging.error("Some trouble with the icon ({}): {}"
98
+ .format(icon_path, e))
99
+ hicon = LoadIcon(0, IDI_APPLICATION)
100
+
101
+ # Taskbar icon
102
+ flags = NIF_ICON | NIF_MESSAGE | NIF_TIP
103
+ nid = (self.hwnd, 0, flags, WM_USER + 20, hicon, "Tooltip")
104
+ Shell_NotifyIcon(NIM_ADD, nid)
105
+ Shell_NotifyIcon(NIM_MODIFY, (self.hwnd, 0, NIF_INFO,
106
+ WM_USER + 20,
107
+ hicon, "Balloon Tooltip", msg, 200,
108
+ title))
109
+ # take a rest then destroy
110
+ sleep(duration)
111
+ DestroyWindow(self.hwnd)
112
+ UnregisterClass(self.wc.lpszClassName, None)
113
+ return None
114
+
115
+ def show_toast(self, title="Notification", msg="Here comes the message",
116
+ icon_path=None, duration=5, threaded=False):
117
+ """Notification settings.
118
+
119
+ :title: notification title
120
+ :msg: notification message
121
+ :icon_path: path to the .ico file to custom notification
122
+ :duration: delay in seconds before notification self-destruction
123
+ """
124
+ if not threaded:
125
+ self._show_toast(title, msg, icon_path, duration)
126
+ else:
127
+ if self.notification_active():
128
+ # We have an active notification, let is finish so we don't spam them
129
+ return False
130
+
131
+ self._thread = threading.Thread(target=self._show_toast, args=(title, msg, icon_path, duration))
132
+ self._thread.start()
133
+ return True
134
+
135
+ def notification_active(self):
136
+ """See if we have an active notification showing"""
137
+ if self._thread != None and self._thread.is_alive():
138
+ # We have an active notification, let is finish we don't spam them
139
+ return True
140
+ return False
141
+
142
+ def on_destroy(self, hwnd, msg, wparam, lparam):
143
+ """Clean after notification ended.
144
+
145
+ :hwnd:
146
+ :msg:
147
+ :wparam:
148
+ :lparam:
149
+ """
150
+ nid = (self.hwnd, 0)
151
+ Shell_NotifyIcon(NIM_DELETE, nid)
152
+ PostQuitMessage(0)
153
+
154
+ return 0
@@ -0,0 +1,22 @@
1
+ from win10toast import ToastNotifier
2
+ import time
3
+
4
+ # #############################################################################
5
+ # ###### Stand alone program ########
6
+ # ###################################
7
+ if __name__ == "__main__":
8
+ # Example
9
+ toaster = ToastNotifier()
10
+ toaster.show_toast(
11
+ "Hello World!!!",
12
+ "Python is 10 seconds awsm!",
13
+ duration=10)
14
+ toaster.show_toast(
15
+ "Example two",
16
+ "This notification is in it's own thread!",
17
+ icon_path=None,
18
+ duration=5,
19
+ threaded=True
20
+ )
21
+ # Wait for threaded notification to finish
22
+ while toaster.notification_active(): time.sleep(0.1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.14.8
3
+ Version: 2.14.10
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
@@ -30,7 +30,6 @@ Requires-Dist: silero-vad~=5.1.2
30
30
  Requires-Dist: ttkbootstrap~=1.10.1
31
31
  Requires-Dist: dataclasses_json~=0.6.7
32
32
  Requires-Dist: win10toast; sys_platform == "win32"
33
- Requires-Dist: numpy==2.2.6
34
33
  Requires-Dist: pystray
35
34
  Requires-Dist: pywin32; sys_platform == "win32"
36
35
  Requires-Dist: pygetwindow; sys_platform == "win32"
@@ -61,6 +61,8 @@ GameSentenceMiner/util/downloader/Untitled_json.py
61
61
  GameSentenceMiner/util/downloader/__init__.py
62
62
  GameSentenceMiner/util/downloader/download_tools.py
63
63
  GameSentenceMiner/util/downloader/oneocr_dl.py
64
+ GameSentenceMiner/util/win10toast/__init__.py
65
+ GameSentenceMiner/util/win10toast/__main__.py
64
66
  GameSentenceMiner/web/__init__.py
65
67
  GameSentenceMiner/web/service.py
66
68
  GameSentenceMiner/web/texthooking_page.py
@@ -13,7 +13,6 @@ stable-ts-whisperless
13
13
  silero-vad~=5.1.2
14
14
  ttkbootstrap~=1.10.1
15
15
  dataclasses_json~=0.6.7
16
- numpy==2.2.6
17
16
  pystray
18
17
  flask
19
18
  groq
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.14.8
3
+ Version: 2.14.10
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
@@ -30,7 +30,6 @@ Requires-Dist: silero-vad~=5.1.2
30
30
  Requires-Dist: ttkbootstrap~=1.10.1
31
31
  Requires-Dist: dataclasses_json~=0.6.7
32
32
  Requires-Dist: win10toast; sys_platform == "win32"
33
- Requires-Dist: numpy==2.2.6
34
33
  Requires-Dist: pystray
35
34
  Requires-Dist: pywin32; sys_platform == "win32"
36
35
  Requires-Dist: pygetwindow; sys_platform == "win32"
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "GameSentenceMiner"
10
- version = "2.14.8"
10
+ version = "2.14.10"
11
11
  description = "A tool for mining sentences from games. Update: Overlay?"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.10"
@@ -40,7 +40,6 @@ dependencies = [
40
40
  "ttkbootstrap~=1.10.1",
41
41
  "dataclasses_json~=0.6.7",
42
42
  "win10toast; sys_platform == 'win32'",
43
- "numpy==2.2.6",
44
43
  "pystray",
45
44
  "pywin32; sys_platform == 'win32'",
46
45
  "pygetwindow; sys_platform == 'win32'",
@@ -67,7 +66,7 @@ dependencies = [
67
66
  gamesentenceminer = "GameSentenceMiner.gsm:main"
68
67
 
69
68
  [tool.setuptools]
70
- packages = ["GameSentenceMiner", "GameSentenceMiner.util", 'GameSentenceMiner.util.downloader', "GameSentenceMiner.util.communication", "GameSentenceMiner.ai", "GameSentenceMiner.ocr", "GameSentenceMiner.owocr.owocr", "GameSentenceMiner.web", "GameSentenceMiner.web.static", "GameSentenceMiner.web.templates", "GameSentenceMiner.wip", "GameSentenceMiner.tools"]
69
+ packages = { find = { include = ["GameSentenceMiner*"] } }
71
70
 
72
71
  [tool.setuptools.package-data]
73
72
  "GameSentenceMiner.web" = ["static/*", "templates/*"]