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.
- GameSentenceMiner-2.2.1.dist-info/METADATA +229 -0
- {GameSentenceMiner-2.2.0.dist-info → GameSentenceMiner-2.2.1.dist-info}/RECORD +13 -13
- src/anki.py +7 -7
- src/configuration.py +2 -4
- src/ffmpeg.py +6 -2
- src/gametext.py +2 -2
- src/gsm.py +5 -6
- src/notification.py +5 -3
- src/obs.py +10 -11
- src/util.py +8 -6
- GameSentenceMiner-2.2.0.dist-info/METADATA +0 -348
- {GameSentenceMiner-2.2.0.dist-info → GameSentenceMiner-2.2.1.dist-info}/WHEEL +0 -0
- {GameSentenceMiner-2.2.0.dist-info → GameSentenceMiner-2.2.1.dist-info}/entry_points.txt +0 -0
- {GameSentenceMiner-2.2.0.dist-info → GameSentenceMiner-2.2.1.dist-info}/top_level.txt +0 -0
@@ -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
|
+

|
93
|
+

|
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
|
+

|
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
|
+

|
133
|
+
|
134
|
+

|
135
|
+
|
136
|
+

|
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=
|
2
|
+
src/anki.py,sha256=FfOxvozC4LBzhexiyY_uDSH3wgxzf4Uqvf8DZyyGlsQ,9138
|
3
3
|
src/config_gui.py,sha256=oY397FO5WYHluPNOtfed3Eakw3TMpVStCaoEU60jw00,48963
|
4
|
-
src/configuration.py,sha256=
|
5
|
-
src/ffmpeg.py,sha256
|
6
|
-
src/gametext.py,sha256=
|
7
|
-
src/gsm.py,sha256=
|
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=
|
10
|
-
src/obs.py,sha256=
|
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=
|
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.
|
21
|
-
GameSentenceMiner-2.2.
|
22
|
-
GameSentenceMiner-2.2.
|
23
|
-
GameSentenceMiner-2.2.
|
24
|
-
GameSentenceMiner-2.2.
|
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
|
-
|
171
|
+
logger.warning(f"Didn't find any cards matching query:\n{query}")
|
172
172
|
return {}
|
173
173
|
if len(card_ids) > 1:
|
174
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
208
|
+
logger.info("Audio still being Trimmed, Card Queued!")
|
209
209
|
use_prev_audio = True
|
210
210
|
with util.lock:
|
211
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
"
|
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
|
-
"
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
246
|
-
|
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
|
-
|
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
|
-
|
22
|
+
logger.info(f"Opened Anki note with ID {note_id}")
|
21
23
|
else:
|
22
|
-
|
24
|
+
logger.error(f"Failed to open Anki note with ID {note_id}")
|
23
25
|
except Exception as e:
|
24
|
-
|
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
|
-
|
114
|
+
logger.info("Replay buffer Toggled.")
|
115
115
|
except Exception as e:
|
116
|
-
|
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
|
-
|
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
|
-
|
132
|
+
logger.error("Replay buffer stopped.")
|
133
133
|
except Exception as e:
|
134
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
106
|
+
logger.info(f"Found Script: {best_script}")
|
105
107
|
return best_script
|
106
108
|
else:
|
107
|
-
|
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
|
-
|
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
|
-
|
118
|
+
logger.info("Agent script finished or closed.")
|
117
119
|
except Exception as e:
|
118
|
-
|
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
|
-

|
108
|
-

|
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
|
-

|
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
|
-

|
276
|
-
|
277
|
-

|
278
|
-
|
279
|
-

|
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)
|
File without changes
|
File without changes
|
File without changes
|