GameSentenceMiner 2.2.0__py3-none-any.whl → 2.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,229 @@
1
+ Metadata-Version: 2.2
2
+ Name: GameSentenceMiner
3
+ Version: 2.2.1
4
+ Summary: A tool for mining sentences from games. Update: Use ffprobe from GSM Directory
5
+ Author-email: Beangate <bpwhelan95@gmail.com>
6
+ License: MIT License
7
+ Project-URL: Homepage, https://github.com/bpwhelan/GameSentenceMiner
8
+ Project-URL: Repository, https://github.com/bpwhelan/GameSentenceMiner
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: requests~=2.32.3
15
+ Requires-Dist: watchdog~=5.0.2
16
+ Requires-Dist: DateTime~=5.5
17
+ Requires-Dist: pyperclip~=1.9.0
18
+ Requires-Dist: vosk~=0.3.45
19
+ Requires-Dist: soundfile~=0.12.1
20
+ Requires-Dist: toml~=0.10.2
21
+ Requires-Dist: psutil~=6.0.0
22
+ Requires-Dist: rapidfuzz~=3.9.7
23
+ Requires-Dist: obs-websocket-py~=1.0
24
+ Requires-Dist: plyer~=2.1.0
25
+ Requires-Dist: keyboard~=0.13.5
26
+ Requires-Dist: websockets~=13.0.1
27
+ Requires-Dist: stable-ts~=2.17.5
28
+ Requires-Dist: silero-vad~=5.1.2
29
+ Requires-Dist: ttkbootstrap~=1.10.1
30
+ Requires-Dist: dataclasses_json~=0.6.7
31
+ Requires-Dist: numpy
32
+ Requires-Dist: pystray
33
+ Requires-Dist: pywin32
34
+
35
+ # Game Sentence Miner
36
+
37
+ This project automates the recording of game sentence audio to help with Anki Card Creation.
38
+
39
+ This allows us to create cards from texthooker/yomitan, and automatically get screenshot and sentence audio from the
40
+ game we are playing.
41
+
42
+ Short Demo (Watch this first): https://www.youtube.com/watch?v=J2At52oWieU
43
+
44
+ Installation: https://www.youtube.com/watch?v=b-L4g9tA508
45
+
46
+ ## Features:
47
+
48
+ - **OBS Replay Buffer**: Constantly records the last X seconds of gameplay.
49
+ - **Voice Activity Detection**: Automatically cuts the end of the clip to the exact moment the voice ended.
50
+ - **Clipboard Interaction**: Automatically monitors the clipboard for dialogue events.
51
+ - **Websocket Listening**: Listens to a websocket uri for text-events from stuff like Agent/Textractor.
52
+ - **Hotkey Automation**: Single hotkey to trigger video recording, screenshot, and transcription.
53
+ - **1-Click Card Creation**: Monitors anki for new cards from Yomitan, and automatically gets audio from games.
54
+
55
+ ## Prerequisites
56
+
57
+ - [Python 3.11+](https://www.python.org/downloads/release/python-3119/)
58
+ - Important: 3.13 is [NOT supported](https://stackoverflow.com/questions/79175945/keyerror-version-installing-openai-whisper-on-python-3-13).
59
+
60
+ ---
61
+
62
+ ## 1. Installing and Running the Script
63
+
64
+ https://pypi.org/project/GameSentenceMiner/
65
+
66
+ Python + pip needs to be installed, make sure you install 3.11 or higher, since older versions may not be supported.
67
+
68
+ Install:
69
+ ```commandline
70
+ pip install gamesentenceminer
71
+ ```
72
+
73
+ Run:
74
+ ```commandline
75
+ gamesentenceminer
76
+ ```
77
+
78
+ On first run, this will download OBS and FFMPEG, and do some basic configuration. You will need to edit the GSM config for your specific Anki fields and whatnot.
79
+
80
+ ---
81
+
82
+ ## 2. Setting Up OBS Replay Buffer
83
+
84
+ 1. Go to Settings > Output > Replay Buffer, and make sure OBS Replay Buffer is enabled, I recommend setting it to 60 seconds, but shorter and longer buffers should also work.
85
+ 2. Set Scene/Source. I recommend using "Game Capture" with "Capture Audio" Enabled. And then mute Desktop/microphone
86
+ 1. If "Game Capture" Does not work, use "Window Capture".
87
+ 2. I recommend having a Scene PER Game, with the name of the scene labeled as the game, this makes it easier for the
88
+ script to know (with a config option) what game you are playing.
89
+ 3. In Output Settings, set "Recording Format" to mkv, and "Audio Encoder" to Opus. Alternate Audio Encoder settings are supported, but will be re-encoded to Opus by default.
90
+
91
+ Here are the Settings I use in OBS. Make sure the recordings folder is the same as the "folder_to_watch" in the config.
92
+ ![image](https://github.com/user-attachments/assets/0056816d-af3c-4a3c-bc6a-4aff5c28cadb)
93
+ ![image](https://github.com/user-attachments/assets/dd2f95a6-f546-41d9-8136-de7b1b035a5d)
94
+
95
+ ---
96
+
97
+ ## 3. Configuring the App.
98
+
99
+ ### Configuration GUI
100
+
101
+ The `GameSentenceMiner` project now includes a graphical interface to simplify configuration. With default values
102
+ already set, this GUI lets you adjust settings as needed. Here’s how to get started:
103
+
104
+ #### Running the Configuration GUI
105
+
106
+ To open the GUI, you have two options:
107
+
108
+ 1. **Tray Icon**: Right Click the Tray Icon and Click `Open Settings`
109
+
110
+ #### Default Settings and Customization
111
+
112
+ The GUI loads with default values for each setting, so if you’re just getting started, you may only need to change options
113
+ in the "path" config. If you make changes, remember to click **Save Settings** to apply them.
114
+ Please take a second to look through the config to see what is available, there is a lot of extra functionality hidden
115
+ behind config options.
116
+
117
+ ![image](https://github.com/user-attachments/assets/ffac9888-de0a-412b-817f-e22a55ce7b55)![image](https://github.com/user-attachments/assets/981c112a-1ddc-4e07-9c39-57fe46644ff5)![image](https://github.com/user-attachments/assets/29470a97-6013-4ca8-9059-48af735eb3a8)![image](https://github.com/user-attachments/assets/8e9c8f03-dc43-4822-a3c5-43f36ca65364)
118
+
119
+
120
+ ---
121
+
122
+
123
+ ## 4. One Click Card Creation
124
+
125
+ This is the flagship feature of this script, so here is a section explaining it. It is possible to do full 1-click card
126
+ creation with this tool + Yomitan/JL. The relevant settings are located in `Features` and `OBS` section in the config.
127
+
128
+ Demo: https://www.youtube.com/watch?v=9dmmXO2CGNw
129
+
130
+ Screenshots to help with setup (THIS IS ALREADY DONE FOR YOU IN NEWER VERSIONS):
131
+
132
+ ![image](https://github.com/user-attachments/assets/7de031e9-ce28-42eb-a8fd-0e60ef70dc3d)
133
+
134
+ ![image](https://github.com/user-attachments/assets/b0c70a1a-65b5-4fe7-a7e4-ccb0b9a5b249)
135
+
136
+ ![image](https://github.com/user-attachments/assets/4cf492eb-12a2-429f-aa0e-f87fc0fa6270)
137
+
138
+ ## 4. Example Process
139
+
140
+ 1. Start script: `gamesentenceminer`
141
+ 2. Start game
142
+ 3. Hook Game with Agent (or textractor) with clipboard enabled
143
+ 4. Create Anki Card with target word (through a texthooker page/Yomitan)
144
+ 5. When finished gaming, End script (not required)
145
+
146
+ Once the Anki card is created:
147
+
148
+ 1. **OBS** will save the last X seconds of gameplay.
149
+ 2. The Python script will trim the audio based on last clipboard event, and the end of voice line detected in VAD if
150
+ enabled.
151
+ 3. Will attempt to update the LAST anki card created.
152
+
153
+ ---
154
+
155
+ ## How to Update the Script
156
+
157
+ ### PIP Install (Preferred)
158
+
159
+ If you installed the script via pip, you can update it with the following command:
160
+ ```bash
161
+ pip install --upgrade gamesentenceminer
162
+ ```
163
+
164
+
165
+ ### Source (For if you want to make edits to the script)
166
+
167
+ I will probably remove this section at a later date. If you want to make edits to the script, you should know how to do this.
168
+
169
+ To ensure you always have the latest version of this script, you can use `git pull` to update your local repository with
170
+ the latest changes from the remote repository.
171
+
172
+ #### Step-by-Step Instructions
173
+
174
+ 1. Open your terminal and navigate to the directory where you cloned the repository:
175
+ ```bash
176
+ cd path/to/script
177
+ ```
178
+
179
+ 2. Run the following command to fetch and integrate the latest changes:
180
+ ```bash
181
+ git pull origin main
182
+ ```
183
+
184
+ - **`origin`** refers to the remote repository from which you cloned the code.
185
+ - **`main`** refers to the main branch of the repository. I~~~~f your default branch has a different name (
186
+ e.g., `master` or `dev`), replace `main` with that branch name.
187
+
188
+ 3. The `git pull` command will download and apply any updates from the remote repository to your local version.
189
+
190
+ ### Example:
191
+
192
+ ```bash
193
+ $ cd path/to/script
194
+ $ git pull origin master
195
+ ```
196
+
197
+ ---
198
+
199
+ ## Disclaimer/Troubleshooting
200
+
201
+ Every game/hook is different, so it's really impossible that any script can get it perfect every time. Also OBS is
202
+ sometimes a bit finicky if running for too long. If the audio timing is off, please first try some troubleshooting
203
+ steps before making an issue:
204
+
205
+ - Try Restarting OBS
206
+ - Make sure your hook is the best you can find. (Preferably it gives you the text RIGHT when the voice line starts)
207
+ - Try Adjusting Offset Configuration in `config.toml` to better match your situation. (i.e. if the hook is late, add a
208
+ negative beginning offset)
209
+ - Try using "Trim beginning" in `VAD` settings.
210
+
211
+ ### Setup Troubleshooting
212
+
213
+ Just going to continuously update this with issues that I have helped users with. Look here first if you have issues
214
+ setting it up.
215
+
216
+ - Make sure folder_to_watch is the same as your recordings path in OBS. It defaults to ~/Videos, but I recommend setting
217
+ it to ~/Videos/GSM.
218
+ - If using clipboard, make sure Agent/Textractor sending to clipboard is enabled.
219
+ - If using websocket, make sure the websocket server is running and the uri is correct in both GSM AND agent/textractor. Textractor uses a default of 6677, and I would recommend changing Agent to use 6677 as well.
220
+
221
+
222
+ ## Contact
223
+
224
+ If you run into issues ask in my [Discord](https://discord.gg/yP8Qse6bb8), or make an issue here.
225
+
226
+ ## Donations
227
+
228
+ If you've benefited from this or any of my other projects, please consider supporting my work
229
+ via [Github Sponsors](https://github.com/sponsors/bpwhelan) or [Ko-fi.](https://ko-fi.com/beangate)
@@ -1,15 +1,15 @@
1
1
  src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- src/anki.py,sha256=wf1_iUVHrEnzNtK2uL8FJBtAwihiwO_WkmgvqLExf5Q,9089
2
+ src/anki.py,sha256=FfOxvozC4LBzhexiyY_uDSH3wgxzf4Uqvf8DZyyGlsQ,9138
3
3
  src/config_gui.py,sha256=oY397FO5WYHluPNOtfed3Eakw3TMpVStCaoEU60jw00,48963
4
- src/configuration.py,sha256=QVVe8_Pkiyxahgbabf0dpBeY1oIktypvOzBvlwyLEhI,14035
5
- src/ffmpeg.py,sha256=9WtP_ajm-nGWPRbuPMJAdnybsKyuAq8Uc_npK4NqGJE,10633
6
- src/gametext.py,sha256=3HN1imfcmecfGstqDjfKBZJL02tJfrZsZBTphXLVGh8,4010
7
- src/gsm.py,sha256=ip4NuMHwAgK_lC9-Y1fUGXYj_SAf-laNPY_nSsmLRYQ,16246
4
+ src/configuration.py,sha256=Cw5vUF74ZKcSinbciNMiLKpMO8M6sf-0THokHO_31nE,14010
5
+ src/ffmpeg.py,sha256=-P0ysDrBefYpu8JOV6-tu52zqg2wDMwVqSVeO5iFQxc,10836
6
+ src/gametext.py,sha256=EGFGzNrkLX_YdUWbksE7bf_4P7O-zcbdhsgubpUt5xw,4025
7
+ src/gsm.py,sha256=DTVkA-MxFq3zsxH1wl1TJPz-sQIrjZdxCtzMsE9YYW0,16250
8
8
  src/model.py,sha256=oh8VVT8T1UKekbmP6MGNgQ8jIuQ_7Rg4GPzDCn2kJo8,1999
9
- src/notification.py,sha256=sWgIIXhaB9WV1K_oQGf5-IR6q3dakae_QS-RuIvbcEs,1939
10
- src/obs.py,sha256=6XIsIRntr63cdpfNS2CanUWr3KyMHoQ8PxfKLZzSkVY,6495
9
+ src/notification.py,sha256=4lteRvzizUb081t_87DpophOQceBnNerNq3ppq5POH8,1996
10
+ src/obs.py,sha256=SAP-_JB-37ZjZIbpWs9-MYPJHtT0BpKLD-KQuvneHtg,6538
11
11
  src/package_updater.py,sha256=Yel-KWBHr5kgIJpHM1A1w5tcx_gDqrLrK0pqXRhinlI,1472
12
- src/util.py,sha256=eTMD5oqsDwem5C7GUG6-x3AQ884a1WLaJ1VVJgeHmA4,4491
12
+ src/util.py,sha256=QRJMSEtnLaT4c2avQ0gZNn1oPtDfBbqcE8zuFHTzbd8,4569
13
13
  src/downloader/Untitled_json.py,sha256=RUUl2bbbCpUDUUS0fP0tdvf5FngZ7ILdA_J5TFYAXUQ,15272
14
14
  src/downloader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  src/downloader/download_tools.py,sha256=m1UV8EvWJdNn2cz11BCUI2mth46RhbVdVHXYYC318V8,6378
@@ -17,8 +17,8 @@ src/vad/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  src/vad/silero_trim.py,sha256=rcHzSNjAm_liiKBWsdywpIC1oUrM00I7kxmomXCQ-Io,1521
18
18
  src/vad/vosk_helper.py,sha256=4oaI7uesINu1sO7PA0-M4qSOmiEneRRN4Ae8ztmYCBE,5798
19
19
  src/vad/whisper_helper.py,sha256=N8-FZMIjLb5CiFeyY8pz050v7RguU39B6tnvsPTbSmc,3383
20
- GameSentenceMiner-2.2.0.dist-info/METADATA,sha256=LZH1a7NVyGPQTuHhed4yTGLtYV0K2nw6OV2epwDRa2Q,13408
21
- GameSentenceMiner-2.2.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
22
- GameSentenceMiner-2.2.0.dist-info/entry_points.txt,sha256=hzWrryQzGBdqbVqzUIBbVZvvh7MBTUYvWiIs95QrfHE,51
23
- GameSentenceMiner-2.2.0.dist-info/top_level.txt,sha256=74rtVfumQlgAPzR5_2CgYN24MB0XARCg0t-gzk6gTrM,4
24
- GameSentenceMiner-2.2.0.dist-info/RECORD,,
20
+ GameSentenceMiner-2.2.1.dist-info/METADATA,sha256=QZzBzJ6OpsODRoGaY52g3VQOQEfhKLhrlsmNmdKpVqs,9157
21
+ GameSentenceMiner-2.2.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
22
+ GameSentenceMiner-2.2.1.dist-info/entry_points.txt,sha256=hzWrryQzGBdqbVqzUIBbVZvvh7MBTUYvWiIs95QrfHE,51
23
+ GameSentenceMiner-2.2.1.dist-info/top_level.txt,sha256=74rtVfumQlgAPzR5_2CgYN24MB0XARCg0t-gzk6gTrM,4
24
+ GameSentenceMiner-2.2.1.dist-info/RECORD,,
src/anki.py CHANGED
@@ -168,15 +168,15 @@ def get_cards_by_sentence(sentence):
168
168
  card_ids = invoke("findCards", query=query)
169
169
 
170
170
  if not card_ids:
171
- print(f"Didn't find any cards matching query:\n{query}")
171
+ logger.warning(f"Didn't find any cards matching query:\n{query}")
172
172
  return {}
173
173
  if len(card_ids) > 1:
174
- print(f'Found more than 1, and not updating cards for query: \n{query}')
174
+ logger.warning(f'Found more than 1, and not updating cards for query: \n{query}')
175
175
  return {}
176
176
 
177
177
  last_notes = invoke('notesInfo', notes=[card_ids[0]])[0]
178
178
 
179
- print(f"Found Card to backfill!: {card_ids[0]}")
179
+ logger.info(f"Found Card to backfill!: {card_ids[0]}")
180
180
 
181
181
  return last_notes
182
182
 
@@ -188,7 +188,7 @@ def check_for_new_cards():
188
188
  try:
189
189
  current_note_ids = get_note_ids()
190
190
  except Exception as e:
191
- print(f"Error fetching Anki notes: {e}")
191
+ logger.error(f"Error fetching Anki notes: {e}")
192
192
  return
193
193
  new_card_ids = current_note_ids - previous_note_ids
194
194
  if new_card_ids and not first_run:
@@ -205,16 +205,16 @@ def update_new_card():
205
205
 
206
206
  use_prev_audio = util.use_previous_audio
207
207
  if util.lock.locked():
208
- print("Audio still being Trimmed, Card Queued!")
208
+ logger.info("Audio still being Trimmed, Card Queued!")
209
209
  use_prev_audio = True
210
210
  with util.lock:
211
- print(f"use previous audio: {use_prev_audio}")
211
+ logger.info(f"use previous audio: {use_prev_audio}")
212
212
  if get_config().obs.get_game_from_scene:
213
213
  obs.update_current_game()
214
214
  if use_prev_audio:
215
215
  update_anki_card(last_card, note=get_initial_card_info(last_card), reuse_audio=True)
216
216
  else:
217
- print("New card(s) detected!")
217
+ logger.info("New card(s) detected!")
218
218
  obs.save_replay_buffer()
219
219
 
220
220
 
src/configuration.py CHANGED
@@ -305,13 +305,11 @@ def load_config():
305
305
  config = ProfileConfig.from_dict(config_file)
306
306
  new_config = Config(configs = {DEFAULT_CONFIG : config}, current_profile=DEFAULT_CONFIG)
307
307
 
308
- print(new_config)
309
-
310
308
  with open(config_path, 'w') as file:
311
309
  json.dump(new_config.to_dict(), file, indent=4)
312
310
  return new_config
313
311
  except json.JSONDecodeError as e:
314
- print(f"Error parsing config.json: {e}")
312
+ logger.error(f"Error parsing config.json: {e}")
315
313
  return None
316
314
  elif os.path.exists('config.toml'):
317
315
  config = ProfileConfig().load_from_toml('config.toml')
@@ -334,7 +332,7 @@ def get_config():
334
332
  config = config_instance.get_config()
335
333
 
336
334
  if config.features.backfill_audio and config.features.full_auto:
337
- print("Cannot have backfill_audio and obs_full_auto_mode turned on at the same time!")
335
+ logger.error("Cannot have backfill_audio and obs_full_auto_mode turned on at the same time!")
338
336
  exit(1)
339
337
 
340
338
  # print(config_instance.get_config())
src/ffmpeg.py CHANGED
@@ -9,6 +9,9 @@ from src.util import *
9
9
  def get_ffmpeg_path():
10
10
  return os.path.join(get_app_directory(), "ffmpeg", "ffmpeg.exe") if util.is_windows() else "ffmpeg"
11
11
 
12
+ def get_ffprobe_path():
13
+ return os.path.join(get_app_directory(), "ffmpeg", "ffprobe.exe") if util.is_windows() else "ffprobe"
14
+
12
15
  ffmpeg_base_command_list = [get_ffmpeg_path(), "-hide_banner", "-loglevel", "error", '-nostdin']
13
16
 
14
17
 
@@ -90,7 +93,7 @@ def process_image(image_file):
90
93
 
91
94
  def get_audio_codec(video_path):
92
95
  command = [
93
- "ffprobe",
96
+ f"{get_ffprobe_path()}",
94
97
  "-v", "error",
95
98
  "-select_streams", "a:0",
96
99
  "-show_entries", "stream=codec_name",
@@ -151,12 +154,13 @@ def get_audio_and_trim(video_path, line_time, next_line_time):
151
154
 
152
155
  def get_video_duration(file_path):
153
156
  ffprobe_command = [
154
- "ffprobe",
157
+ f"{get_ffprobe_path()}",
155
158
  "-v", "error",
156
159
  "-show_entries", "format=duration",
157
160
  "-of", "json",
158
161
  file_path
159
162
  ]
163
+ logger.debug(" ".join(ffprobe_command))
160
164
  result = subprocess.run(ffprobe_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
161
165
  duration_info = json.loads(result.stdout)
162
166
  return float(duration_info["format"]["duration"]) # Return the duration in seconds
src/gametext.py CHANGED
@@ -51,7 +51,7 @@ async def listen_websocket():
51
51
  try:
52
52
  async with websockets.connect(f'ws://{get_config().general.websocket_uri}', ping_interval=None) as websocket:
53
53
  if reconnecting:
54
- print(f"Texthooker WebSocket connected Successfully!")
54
+ logger.info(f"Texthooker WebSocket connected Successfully!")
55
55
  reconnecting = False
56
56
  while True:
57
57
  message = await websocket.recv()
@@ -71,7 +71,7 @@ async def listen_websocket():
71
71
 
72
72
  except (websockets.ConnectionClosed, ConnectionError) as e:
73
73
  if not reconnecting:
74
- print(f"Texthooker WebSocket connection lost: {e}. Attempting to Reconnect...")
74
+ logger.warning(f"Texthooker WebSocket connection lost: {e}. Attempting to Reconnect...")
75
75
  reconnecting = True
76
76
  await asyncio.sleep(5)
77
77
 
src/gsm.py CHANGED
@@ -100,8 +100,7 @@ class VideoToAudioHandler(FileSystemEventHandler):
100
100
  except Exception as e:
101
101
  logger.error(f"Card failed to update! Maybe it was removed? {e}")
102
102
  except FileNotFoundError as f:
103
- print(f)
104
- print("Something went wrong with processing, anki card not updated")
103
+ logger.error("Something went wrong with processing, anki card not updated")
105
104
  except Exception as e:
106
105
  logger.error(f"Some error was hit catching to allow further work to be done: {e}", exc_info=1)
107
106
  if get_config().paths.remove_video and os.path.exists(video_path):
@@ -232,7 +231,7 @@ def open_log():
232
231
  """Open log file with the default application."""
233
232
  log_file_path = get_log_path()
234
233
  if not os.path.exists(log_file_path):
235
- print("Log file not found!")
234
+ logger.error("Log file not found!")
236
235
  return
237
236
 
238
237
  if sys.platform.startswith("win"): # Windows
@@ -242,13 +241,13 @@ def open_log():
242
241
  elif sys.platform.startswith("linux"): # Linux
243
242
  subprocess.call(["xdg-open", log_file_path])
244
243
  else:
245
- print("Unsupported platform!")
246
- print("Log opened.")
244
+ logger.error("Unsupported platform!")
245
+ logger.info("Log opened.")
247
246
 
248
247
 
249
248
  def exit_program(icon, item):
250
249
  """Exit the application."""
251
- print("Exiting...")
250
+ logger.info("Exiting...")
252
251
  icon.stop()
253
252
  cleanup()
254
253
 
src/notification.py CHANGED
@@ -1,6 +1,8 @@
1
1
  import requests
2
2
  from plyer import notification
3
3
 
4
+ from src.configuration import logger
5
+
4
6
 
5
7
  def open_anki_card(note_id):
6
8
  url = "http://localhost:8765"
@@ -17,11 +19,11 @@ def open_anki_card(note_id):
17
19
  try:
18
20
  response = requests.post(url, json=data, headers=headers)
19
21
  if response.status_code == 200:
20
- print(f"Opened Anki note with ID {note_id}")
22
+ logger.info(f"Opened Anki note with ID {note_id}")
21
23
  else:
22
- print(f"Failed to open Anki note with ID {note_id}")
24
+ logger.error(f"Failed to open Anki note with ID {note_id}")
23
25
  except Exception as e:
24
- print(f"Error connecting to AnkiConnect: {e}")
26
+ logger.info(f"Error connecting to AnkiConnect: {e}")
25
27
 
26
28
 
27
29
  def send_notification(tango):
src/obs.py CHANGED
@@ -111,9 +111,9 @@ def disconnect_from_obs():
111
111
  def toggle_replay_buffer():
112
112
  try:
113
113
  client.call(requests.ToggleReplayBuffer())
114
- print("Replay buffer Toggled.")
114
+ logger.info("Replay buffer Toggled.")
115
115
  except Exception as e:
116
- print(f"Error toggling buffer: {e}")
116
+ logger.error(f"Error toggling buffer: {e}")
117
117
 
118
118
 
119
119
  # Start replay buffer
@@ -122,16 +122,16 @@ def start_replay_buffer():
122
122
  client.call(requests.GetVersion())
123
123
  client.call(requests.StartReplayBuffer())
124
124
  except Exception as e:
125
- print(f"Error starting replay buffer: {e}")
125
+ logger.error(f"Error starting replay buffer: {e}")
126
126
 
127
127
 
128
128
  # Stop replay buffer
129
129
  def stop_replay_buffer():
130
130
  try:
131
131
  client.call(requests.StopReplayBuffer())
132
- print("Replay buffer stopped.")
132
+ logger.error("Replay buffer stopped.")
133
133
  except Exception as e:
134
- print(f"Error stopping replay buffer: {e}")
134
+ logger.error(f"Error stopping replay buffer: {e}")
135
135
 
136
136
 
137
137
  # Save the current replay buffer
@@ -144,7 +144,7 @@ def save_replay_buffer():
144
144
  else:
145
145
  logger.error("Replay Buffer is not active, could not save Replay Buffer!")
146
146
  except Exception as e:
147
- print(f"Error saving replay buffer: {e}")
147
+ logger.error(f"Error saving replay buffer: {e}")
148
148
 
149
149
 
150
150
  def get_current_scene():
@@ -153,7 +153,7 @@ def get_current_scene():
153
153
  scene_info = SceneInfo.from_dict(response.datain)
154
154
  return scene_info.sceneName
155
155
  except Exception as e:
156
- print(f"Couldn't get scene: {e}")
156
+ logger.error(f"Couldn't get scene: {e}")
157
157
  return ''
158
158
 
159
159
 
@@ -161,10 +161,9 @@ def get_source_from_scene(scene_name):
161
161
  try:
162
162
  response = client.call(requests.GetSceneItemList(sceneName=scene_name))
163
163
  scene_list = SceneItemsResponse.from_dict(response.datain)
164
- print(scene_list)
165
164
  return scene_list.sceneItems[0]
166
165
  except Exception as e:
167
- print(f"Error getting source from scene: {e}")
166
+ logger.error(f"Error getting source from scene: {e}")
168
167
  return ''
169
168
 
170
169
 
@@ -176,13 +175,13 @@ def get_screenshot():
176
175
  current_source = get_source_from_scene(get_current_game())
177
176
  current_source_name = current_source.sourceName
178
177
  if not current_source_name:
179
- print("No active scene found.")
178
+ logger.error("No active scene found.")
180
179
  return
181
180
  client.call(
182
181
  requests.SaveSourceScreenshot(sourceName=current_source_name, imageFormat='png', imageFilePath=screenshot))
183
182
  return screenshot
184
183
  except Exception as e:
185
- print(f"Error getting screenshot: {e}")
184
+ logger.error(f"Error getting screenshot: {e}")
186
185
 
187
186
 
188
187
  def update_current_game():
src/util.py CHANGED
@@ -9,6 +9,8 @@ from sys import platform
9
9
 
10
10
  from rapidfuzz import process
11
11
 
12
+ from src.configuration import logger
13
+
12
14
  SCRIPTS_DIR = r"E:\Japanese Stuff\agent-v0.1.4-win32-x64\data\scripts"
13
15
 
14
16
  # Global variables to control script execution
@@ -56,7 +58,7 @@ def get_file_modification_time(file_path):
56
58
  def get_process_id_by_title(game_title):
57
59
  powershell_command = f"Get-Process | Where-Object {{$_.MainWindowTitle -like '*{game_title}*'}} | Select-Object -First 1 -ExpandProperty Id"
58
60
  process_id = subprocess.check_output(["powershell", "-Command", powershell_command], text=True).strip()
59
- print(f"Process ID for {game_title}: {process_id}")
61
+ logger.info(f"Process ID for {game_title}: {process_id}")
60
62
  return process_id
61
63
 
62
64
 
@@ -101,21 +103,21 @@ def find_script_for_game(game_title):
101
103
  best_script, matched_game_name, confidence = find_most_similar_script(game_title, steam_scripts)
102
104
 
103
105
  if best_script:
104
- print(f"Found Script: {best_script}")
106
+ logger.info(f"Found Script: {best_script}")
105
107
  return best_script
106
108
  else:
107
- print("No similar script found.")
109
+ logger.warning("No similar script found.")
108
110
 
109
111
 
110
112
  def run_agent_and_hook(pname, agent_script):
111
113
  command = f'agent --script=\"{agent_script}\" --pname={pname}'
112
- print("Running and Hooking Agent!")
114
+ logger.info("Running and Hooking Agent!")
113
115
  try:
114
116
  dos_process = subprocess.Popen(command, shell=True)
115
117
  dos_process.wait() # Wait for the process to complete
116
- print("Agent script finished or closed.")
118
+ logger.info("Agent script finished or closed.")
117
119
  except Exception as e:
118
- print(f"Error occurred while running agent script: {e}")
120
+ logger.error(f"Error occurred while running agent script: {e}")
119
121
 
120
122
  keep_running = False
121
123
 
@@ -1,348 +0,0 @@
1
- Metadata-Version: 2.2
2
- Name: GameSentenceMiner
3
- Version: 2.2.0
4
- Summary: A tool for mining sentences from games.
5
- Author-email: Beangate <bpwhelan95@gmail.com>
6
- License: MIT License
7
- Project-URL: Homepage, https://github.com/bpwhelan/GameSentenceMiner
8
- Project-URL: Repository, https://github.com/bpwhelan/GameSentenceMiner
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.11
13
- Description-Content-Type: text/markdown
14
- Requires-Dist: requests~=2.32.3
15
- Requires-Dist: watchdog~=5.0.2
16
- Requires-Dist: DateTime~=5.5
17
- Requires-Dist: pyperclip~=1.9.0
18
- Requires-Dist: vosk~=0.3.45
19
- Requires-Dist: soundfile~=0.12.1
20
- Requires-Dist: toml~=0.10.2
21
- Requires-Dist: psutil~=6.0.0
22
- Requires-Dist: rapidfuzz~=3.9.7
23
- Requires-Dist: obs-websocket-py~=1.0
24
- Requires-Dist: plyer~=2.1.0
25
- Requires-Dist: keyboard~=0.13.5
26
- Requires-Dist: websockets~=13.0.1
27
- Requires-Dist: stable-ts~=2.17.5
28
- Requires-Dist: silero-vad~=5.1.2
29
- Requires-Dist: ttkbootstrap~=1.10.1
30
- Requires-Dist: dataclasses_json~=0.6.7
31
- Requires-Dist: numpy
32
- Requires-Dist: pystray
33
- Requires-Dist: pywin32
34
-
35
- # Sentence Mining Game Audio Trim Helper
36
-
37
- ## WARNING
38
-
39
- This project is in process of a major overhaul, and this README will be updated soon with the new process.
40
-
41
- ---
42
-
43
- This project automates the recording of game sentence audio to help with Anki Card Creation.
44
-
45
- This allows us to create cards from texthooker/yomitan, and automatically get screenshot and sentence audio from the
46
- game we are playing.
47
-
48
- ## Features:
49
-
50
- - **Voice Activity Detection**: Automatically cuts the end of the clip to the exact moment the voice ended.
51
- - **OBS Replay Buffer**: Constantly records the last X seconds of gameplay.
52
- - **Clipboard Interaction**: Automatically monitors the clipboard for dialogue events.
53
- - **Websocket Listening**: Listens to a websocket uri for text-events from stuff like Agent/Textractor.
54
- - **Hotkey Automation**: Single hotkey to trigger video recording, screenshot, and transcription.
55
- - **1-Click Card Creation**: Monitors anki for new cards from Yomitan, and automatically gets audio from games.
56
-
57
- ## Prerequisites
58
-
59
- - [Python 3.11+](https://www.python.org/downloads/)
60
- - [OBS Studio](https://obsproject.com/)
61
-
62
- ---
63
-
64
- ### Quick Disclaimer/Troubleshooting
65
-
66
- Every game/hook is different, so it's really impossible that any script can get it perfect everytime. Also OBS is
67
- sometimes a bit finnicky if running for too long. If the audio timing is off, please first try some troubleshooting
68
- steps before making an issue:
69
-
70
- - Try Restarting OBS
71
- - Make sure your hook is the best you can find. (Preferrably it gives you the text RIGHT when the voiceline starts)
72
- - Try Adjusting Offset Configuration in `config.toml` to better match your situation. (i.e. if the hook is late, add a
73
- negative beginning offset)
74
- - Try using "Trim beginning" in `VAD` settings.
75
-
76
- #### Setup Troubleshooting
77
-
78
- Just going to continuously update this with issues that I have helped users with. Look here first if you have issues
79
- setting it up.
80
-
81
- - Make sure folder_to_watch is the same as your recordings path in OBS. It defaults to ~/Videos, but I recommend setting
82
- it to ~/Videos/OBS.
83
- - if it says something about a missing library, attempt to run `pip install -r requirements.txt` again
84
- - If using clipboard, make sure agent/textractor sending to clipboard is enabled.
85
-
86
- ## 1. Setting Up OBS 60-Second Replay Buffer
87
-
88
- 1. **Install OBS Studio**: Download and install OBS from [here](https://obsproject.com/).
89
- 2. **Enable Replay Buffer**:
90
- 1. Open OBS and navigate to **Settings → Output → Replay Buffer**.
91
- 2. Enable the **Replay Buffer** and set the duration to **60 seconds**, this can be lower, or higher, but 60 works
92
- for a very simple setup.
93
- 3. **Set a Hotkey for the Replay Buffer**:
94
- 1. Go to **Settings → Hotkeys** and find **Save Replay Buffer**.
95
- 2. Assign a hotkey for saving the replay.
96
- 4. Set Scene/Source. I recommend using "Game Capture" with "Capture Audio" Enabled. And then mute Desktop/microphone
97
- 1. If "Game Capture" Does not work, use "Window Capture".
98
- 2. I recommend having a Scene PER Game, with the name of the scene labeled as the game, this makes it easier for the
99
- script to know (with a config option) what game you are playing.
100
- 5. In Output Settings, set "Recording Format" to mkv, and "Audio Encoder" to Opus. Alternative Settings may be supported
101
- at a later date.
102
- 6. **Set up obs websocket** (HIGHLY RECOMMENDED see #5)
103
- 1. Can allow my script to automatically start (and stop) the replay buffer, as well as automatically add
104
- audio/screenshot to card created from yomi.
105
-
106
- Here are the Settings I use in OBS. Make sure the recordings folder is the same as the "folder_to_watch" in the config.
107
- ![image](https://github.com/user-attachments/assets/0056816d-af3c-4a3c-bc6a-4aff5c28cadb)
108
- ![image](https://github.com/user-attachments/assets/dd2f95a6-f546-41d9-8136-de7b1b035a5d)
109
-
110
-
111
- ---
112
-
113
- ## 2. Configuring the App.
114
-
115
- ### Configuration GUI
116
-
117
- The `GameSentenceMiner` project now includes a graphical interface to simplify configuration. With default values
118
- already set, this GUI lets you adjust settings as needed. Here’s how to get started:
119
-
120
- #### Running the Configuration GUI
121
-
122
- To open the GUI, you have two options:
123
-
124
- 1. **Tray Icon**: Right Click the Tray Icon and Click `Open Settings`
125
- 2. **Directly Run the Script**: Open a terminal in your project directory and enter:
126
- \\\bash
127
- python config_gui.py
128
- \\\
129
-
130
- #### Default Settings and Customization
131
-
132
- The GUI loads with default values for each setting, so if you’re just getting started, you may only need to change stuff
133
- in the "path" config. If you make changes, remember to click **Save Settings** to apply them.
134
- Please take a second to look through the config to see what is available, there is a lot of extra functionality hidden
135
- behind config options.
136
-
137
- ![image](https://github.com/user-attachments/assets/ffac9888-de0a-412b-817f-e22a55ce7b55)![image](https://github.com/user-attachments/assets/981c112a-1ddc-4e07-9c39-57fe46644ff5)![image](https://github.com/user-attachments/assets/29470a97-6013-4ca8-9059-48af735eb3a8)![image](https://github.com/user-attachments/assets/8e9c8f03-dc43-4822-a3c5-43f36ca65364)
138
-
139
-
140
-
141
-
142
-
143
- ---
144
-
145
- ## 2. Configuring `config.toml` **DEPRECATED** Only used if running an older version.
146
-
147
- I redid the config parsing cause `config.py` is not ideal, especially when distributing a script via git.
148
-
149
- Your `config.toml` file allows you to configure key settings for the automation process, file paths, and other behavior.
150
- Here are the configurable options:
151
-
152
- Duplicate/rename config_EXAMPLE.toml to get started
153
-
154
- ```toml
155
- # Path configurations
156
- [paths]
157
- folder_to_watch = "~/Videos/OBS"
158
- audio_destination = "~/Videos/OBS/Audio/"
159
- screenshot_destination = "~/Videos/OBS/SS/"
160
-
161
- # Anki Fields
162
- [anki]
163
- url = 'http://127.0.0.1:8765'
164
- sentence_field = "Sentence"
165
- sentence_audio_field = "SentenceAudio"
166
- picture_field = "Picture"
167
- word_field = "Word"
168
- current_game = "Japanese Game"
169
- custom_tags = ['JapaneseGameMiner', "Test Another Tag"] # leave Empty if you dont want to add tags
170
- add_game_tag = true
171
- polling_rate = 200 # This is how often the script asks anki if it has new cards. Change at your own peril.
172
-
173
- # Feature Flags
174
- [features]
175
- do_vosk_postprocessing = true
176
- update_anki = true
177
- remove_video = true
178
- remove_screenshot = false
179
- remove_audio = false
180
- notify_on_update = true
181
- open_anki_edit = false
182
- backfill_audio = false # Strictly to fill audio for cards that you already have in your anki db. CANNOT BE USED WITH FULL_AUTO_MODE
183
-
184
- # Vosk Model
185
- [vosk]
186
- url = "https://alphacephei.com/vosk/models/vosk-model-small-ja-0.22.zip"
187
- # If you have a high-performance PC, with 16GB+ of RAM, you can uncomment and use this model:
188
- # url = "https://alphacephei.com/vosk/models/vosk-model-ja-0.22.zip"
189
- log-level = -1
190
-
191
- [screenshot]
192
- width = 0 # Desired Width of Screenshot, 0 to disable scaling (Default 0)
193
- quality = 85 # Quality of image, 100 is lossless (Default 85)
194
- extension = "webp" # Codec of screenshot, Recommend Keeping this as webp (Default webp)
195
-
196
- [audio]
197
- extension = "opus" # Desired Extension/codec of Trimmed Audio, (Default opus)
198
- beginning_offset = 0.0 # Negative Value = More time at the beginning (i.e. -1 is 1 extra second at the beginning)
199
- end_offset = 0.5 # Positive Value = More time at the end (i.e. 1 is 1 extra second at the end)
200
- vosk_trim_beginning = false # Only change If you run into issues with clipboard timing, add a negative beginning_offset as well, Warning: You may end up with audio from previous line depending on your setup!
201
- reset_hotkey = 'f4' # Hotkey to initiate Offset Updater.
202
-
203
- [obs]
204
- enabled = true
205
- start_buffer = true
206
- full_auto_mode = false # Automatically Create Cards when you Create in Yomi. REQUIRED for multi-card-per-voiceline
207
- host = "localhost"
208
- port = 4455
209
- password = "your_password_here"
210
- get_game_from_scene = false
211
-
212
- [websocket]
213
- enabled = true # Recommended/Default, with this enabled, this script does not interact with your clipboard at all.
214
- uri = 'localhost:6677'
215
-
216
- #[anki_custom_fields]
217
- #IsTargetedSentenceCard = 1
218
- #Comment = "Nice!"
219
- ```
220
-
221
- ---
222
-
223
- ## 3. Install Requirements
224
-
225
- If you know what you are doing, do this in a venv, but I'm not going to explain that here.
226
-
227
- `pip install -r requirements.txt`
228
-
229
- ## 4. Installing FFmpeg
230
-
231
- To run this script, you will need to have **FFmpeg** installed. If you don't have FFmpeg installed on your system, you
232
- can easily install it via **Chocolatey** (Preferred), or install it yourself and ensure it's in the PATH.
233
-
234
- #### Step-by-Step Instructions:
235
-
236
- 1. First, ensure you have **Chocolatey** installed. If you don't have it installed, follow the instructions on
237
- the [Chocolatey installation page](https://chocolatey.org/install) or run the following command in an **elevated**
238
- PowerShell window (run as Administrator):
239
-
240
- ```bash
241
- Set-ExecutionPolicy Bypass -Scope Process -Force; `
242
- [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; `
243
- iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
244
- ```
245
-
246
- 2. Once Chocolatey is installed, open a **new** PowerShell or Command Prompt window (with administrator rights).
247
-
248
- 3. Run the following command to install FFmpeg:
249
-
250
- ```bash
251
- choco install ffmpeg
252
- ```
253
-
254
- 4. After the installation is complete, verify that FFmpeg is correctly installed by running the following command:
255
-
256
- ```bash
257
- ffmpeg -version
258
- ```
259
-
260
- If the installation was successful, you should see the version information for FFmpeg.
261
-
262
- Now you're ready to use FFmpeg in the script!
263
-
264
- ---
265
-
266
- ## 5. One Click Card Creation
267
-
268
- This is the flagship feature of this script, so here is a section explaining it. It is possible to do full 1-click card
269
- creation with this tool + Yomitan. The relevant settings are located in `Features` and `OBS` section in the config.
270
-
271
- Demo: https://www.youtube.com/watch?v=9dmmXO2CGNw
272
-
273
- Screenshots to help with setup:
274
-
275
- ![image](https://github.com/user-attachments/assets/7de031e9-ce28-42eb-a8fd-0e60ef70dc3d)
276
-
277
- ![image](https://github.com/user-attachments/assets/b0c70a1a-65b5-4fe7-a7e4-ccb0b9a5b249)
278
-
279
- ![image](https://github.com/user-attachments/assets/4cf492eb-12a2-429f-aa0e-f87fc0fa6270)
280
-
281
- ## 6. Example Process
282
-
283
- 1. Start game
284
- 2. Hook Game with Agent (or textractor) with clipboard enabled
285
- 3. start script: `python -m src.GameSentenceMiner.gsm`
286
- 1. Create Anki Card with target word (through a texthooker page/Yomitan)
287
- 2. (If full-auto-mode not on) Trigger Hotkey to record replay buffer
288
- 4. When finished gaming, end script
289
-
290
- Once the hotkey is triggered:
291
-
292
- 1. **OBS** will save the last X seconds of gameplay.
293
- 2. The Python script will trim the audio based on last clipboard event, and the end of voiceline detected in Vosk if
294
- enabled.
295
- 3. Will attempt to update the LAST anki card created.
296
-
297
- ---
298
-
299
- ## How to Update the Script
300
-
301
- ### Updater (Preferred)
302
-
303
- There is now an Update script included! running `python update.py` in the directory will attempt to update your scripts
304
- to the latest release. If you have made changes to any of the files, they will be safely backed up before being
305
- replaced.
306
-
307
- ---
308
-
309
- ### Manual
310
-
311
- To ensure you always have the latest version of this script, you can use `git pull` to update your local repository with
312
- the latest changes from the remote repository.
313
-
314
- #### Step-by-Step Instructions
315
-
316
- 1. Open your terminal and navigate to the directory where you cloned the repository:
317
- ```bash
318
- cd path/to/script
319
- ```
320
-
321
- 2. Run the following command to fetch and integrate the latest changes:
322
- ```bash
323
- git pull origin main
324
- ```
325
-
326
- - **`origin`** refers to the remote repository from which you cloned the code.
327
- - **`main`** refers to the main branch of the repository. If your default branch has a different name (
328
- e.g., `master` or `dev`), replace `main` with that branch name.
329
-
330
- 3. The `git pull` command will download and apply any updates from the remote repository to your local version.
331
-
332
- ### Example:
333
-
334
- ```bash
335
- $ cd path/to/script
336
- $ git pull origin master
337
- ```
338
-
339
- ---
340
-
341
- ## Contact
342
-
343
- If you run into issues ask in my [discord](https://discord.gg/yP8Qse6bb8), or make an issue here.
344
-
345
- ## Donations
346
-
347
- If you've benefited from this or any of my other projects, please consider supporting my work
348
- via [Github Sponsors](https://github.com/sponsors/bpwhelan) or [Ko-fi.](https://ko-fi.com/beangate)