GameSentenceMiner 2.9.4__py3-none-any.whl → 2.9.6__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.
Files changed (42) hide show
  1. GameSentenceMiner/ai/ai_prompting.py +3 -3
  2. GameSentenceMiner/anki.py +16 -14
  3. GameSentenceMiner/config_gui.py +22 -7
  4. GameSentenceMiner/gametext.py +5 -5
  5. GameSentenceMiner/gsm.py +25 -67
  6. GameSentenceMiner/obs.py +7 -8
  7. GameSentenceMiner/ocr/owocr_area_selector.py +1 -1
  8. GameSentenceMiner/ocr/owocr_helper.py +30 -13
  9. GameSentenceMiner/owocr/owocr/ocr.py +0 -2
  10. GameSentenceMiner/owocr/owocr/run.py +1 -1
  11. GameSentenceMiner/{communication → util/communication}/__init__.py +1 -1
  12. GameSentenceMiner/{communication → util/communication}/send.py +1 -1
  13. GameSentenceMiner/{communication → util/communication}/websocket.py +2 -2
  14. GameSentenceMiner/{configuration.py → util/configuration.py} +6 -0
  15. GameSentenceMiner/{downloader → util/downloader}/download_tools.py +3 -3
  16. GameSentenceMiner/{electron_config.py → util/electron_config.py} +1 -1
  17. GameSentenceMiner/{ffmpeg.py → util/ffmpeg.py} +18 -10
  18. GameSentenceMiner/{util.py → util/gsm_utils.py} +4 -31
  19. GameSentenceMiner/{model.py → util/model.py} +1 -1
  20. GameSentenceMiner/{notification.py → util/notification.py} +3 -5
  21. GameSentenceMiner/{package.py → util/package.py} +1 -2
  22. GameSentenceMiner/{ss_selector.py → util/ss_selector.py} +5 -4
  23. GameSentenceMiner/{text_log.py → util/text_log.py} +3 -3
  24. GameSentenceMiner/vad.py +344 -0
  25. GameSentenceMiner/web/texthooking_page.py +15 -10
  26. {gamesentenceminer-2.9.4.dist-info → gamesentenceminer-2.9.6.dist-info}/METADATA +2 -3
  27. gamesentenceminer-2.9.6.dist-info/RECORD +67 -0
  28. GameSentenceMiner/vad/groq_trim.py +0 -82
  29. GameSentenceMiner/vad/result.py +0 -21
  30. GameSentenceMiner/vad/silero_trim.py +0 -52
  31. GameSentenceMiner/vad/vad_utils.py +0 -13
  32. GameSentenceMiner/vad/vosk_helper.py +0 -158
  33. GameSentenceMiner/vad/whisper_helper.py +0 -105
  34. gamesentenceminer-2.9.4.dist-info/RECORD +0 -72
  35. /GameSentenceMiner/{downloader → util}/__init__.py +0 -0
  36. /GameSentenceMiner/{downloader → util/downloader}/Untitled_json.py +0 -0
  37. /GameSentenceMiner/{vad → util/downloader}/__init__.py +0 -0
  38. /GameSentenceMiner/{downloader → util/downloader}/oneocr_dl.py +0 -0
  39. {gamesentenceminer-2.9.4.dist-info → gamesentenceminer-2.9.6.dist-info}/WHEEL +0 -0
  40. {gamesentenceminer-2.9.4.dist-info → gamesentenceminer-2.9.6.dist-info}/entry_points.txt +0 -0
  41. {gamesentenceminer-2.9.4.dist-info → gamesentenceminer-2.9.6.dist-info}/licenses/LICENSE +0 -0
  42. {gamesentenceminer-2.9.4.dist-info → gamesentenceminer-2.9.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,67 @@
1
+ GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ GameSentenceMiner/anki.py,sha256=CuzVqzuFtZnbMbU2Zk-sxNGwSgyCpv5RLL7lOOX0Meg,14972
3
+ GameSentenceMiner/config_gui.py,sha256=r-ASCXVNS4Io6Ej3svwC8aJEWc9Rc7u-pzfsAwD4ru8,82079
4
+ GameSentenceMiner/gametext.py,sha256=mM-gw1d7c2EEvMUznaAevTQFLswNZavCuxMXhA9pV4g,6251
5
+ GameSentenceMiner/gsm.py,sha256=SHvT3JZlYpZgKeJnVXrtk8ve4ubiM7YPv-9FDF7rVM4,27724
6
+ GameSentenceMiner/obs.py,sha256=jdAKQFnXlviMupRUKBuK68Q1u8yEZNKBgFnvIq1hhnc,14810
7
+ GameSentenceMiner/vad.py,sha256=Gk_VthD7mDp3-wM_S6bEv8ykGmqzCDbbcRiaEBzAE_o,14835
8
+ GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ GameSentenceMiner/ai/ai_prompting.py,sha256=tPDiTHlrfZul0hlvEFgZS4V_6oaHkVb-4v79Sd4gtlM,10018
10
+ GameSentenceMiner/assets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ GameSentenceMiner/assets/icon.png,sha256=9GRL8uXUAgkUSlvbm9Pv9o2poFVRGdW6s2ub_DeUD9M,937624
12
+ GameSentenceMiner/assets/icon128.png,sha256=l90j7biwdz5ahwOd5wZ-406ryEV9Pan93dquJQ3e1CI,18395
13
+ GameSentenceMiner/assets/icon256.png,sha256=JEW46wOrG1KR-907rvFaEdNbPtj5gu0HJmG7qUnIHxQ,51874
14
+ GameSentenceMiner/assets/icon32.png,sha256=Kww0hU_qke9_22wBuO_Nq0Dv2SfnOLwMhCyGgbgXdg8,6089
15
+ GameSentenceMiner/assets/icon512.png,sha256=HxUj2GHjyQsk8NV433256UxU9phPhtjCY-YB_7W4sqs,192487
16
+ GameSentenceMiner/assets/icon64.png,sha256=N8xgdZXvhqVQP9QUK3wX5iqxX9LxHljD7c-Bmgim6tM,9301
17
+ GameSentenceMiner/assets/pickaxe.png,sha256=VfIGyXyIZdzEnVcc4PmG3wszPMO1W4KCT7Q_nFK6eSE,1403829
18
+ GameSentenceMiner/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=fEQ2o2NXksGRHpueO8c4TfAp75GEdAtAr1ngTFOsdpg,2257
20
+ GameSentenceMiner/ocr/ocrconfig.py,sha256=_tY8mjnzHMJrLS8E5pHqYXZjMuLoGKYgJwdhYgN-ny4,6466
21
+ GameSentenceMiner/ocr/owocr_area_selector.py,sha256=71trzwz9Isyy-kN9mLS8vIX-giC8Lkin4slLXaxudac,47162
22
+ GameSentenceMiner/ocr/owocr_helper.py,sha256=FQXk5PSCS9gWtcgoIFsPxjVELUwA4Dg1hEX83902K0Q,18114
23
+ GameSentenceMiner/owocr/owocr/__init__.py,sha256=opjBOyGGyEqZCE6YdZPnyt7nVfiwyELHsXA0jAsjm14,25
24
+ GameSentenceMiner/owocr/owocr/__main__.py,sha256=XQaqZY99EKoCpU-gWQjNbTs7Kg17HvBVE7JY8LqIE0o,157
25
+ GameSentenceMiner/owocr/owocr/config.py,sha256=qM7kISHdUhuygGXOxmgU6Ef2nwBShrZtdqu4InDCViE,8103
26
+ GameSentenceMiner/owocr/owocr/lens_betterproto.py,sha256=oNoISsPilVVRBBPVDtb4-roJtAhp8ZAuFTci3TGXtMc,39141
27
+ GameSentenceMiner/owocr/owocr/ocr.py,sha256=y8RHHaJw8M4BG4CbbtIw0DrV8KP9RjbJNJxjM5v91oU,42236
28
+ GameSentenceMiner/owocr/owocr/run.py,sha256=jFN7gYYriHgfqORJiBTz8mPkQsDJ6ZugA0_ATWUxk-U,54750
29
+ GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=Na6XStbQBtpQUSdbN3QhEswtKuU1JjReFk_K8t5ezQE,3395
30
+ GameSentenceMiner/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ GameSentenceMiner/util/configuration.py,sha256=Qk5V4HA2FJbdMTa9jYZfNVQ_4rzvjbUtJgPejwQ3vwk,25856
32
+ GameSentenceMiner/util/electron_config.py,sha256=ZZf54QifdNHbII-JDsMZmdT8nTyrq-7gVvalyLRecfw,9792
33
+ GameSentenceMiner/util/ffmpeg.py,sha256=qaCXkfK2fd-1NRqbm7burrdBYgnGx07kBuyenee8Mtk,18697
34
+ GameSentenceMiner/util/gsm_utils.py,sha256=RoOTvWCVpmfYA7fLDdIPcgH1c6TZK4jDZq98BectPhg,8272
35
+ GameSentenceMiner/util/model.py,sha256=iDtLTfR6D-ZC0gCiDqYno6-gA6Z07PZTM4B5MAA6xZI,5704
36
+ GameSentenceMiner/util/notification.py,sha256=euTnnNDJm0izr0Z5AhZGV2wrrioCASeKUtm5aZFO5zU,3462
37
+ GameSentenceMiner/util/package.py,sha256=u1ym5z869lw5EHvIviC9h9uH97bzUXSXXA8KIn8rUvk,1157
38
+ GameSentenceMiner/util/ss_selector.py,sha256=ATgwDXi4-TLv0hB21NV79FZnXgidiM0z7TgvO7eBnhw,4472
39
+ GameSentenceMiner/util/text_log.py,sha256=XOq8tpJUpNa-mKJPui40P5aUTX2yzMHPnHgJ2obagw0,5201
40
+ GameSentenceMiner/util/communication/__init__.py,sha256=xh__yn2MhzXi9eLi89PeZWlJPn-cbBSjskhi1BRraXg,643
41
+ GameSentenceMiner/util/communication/send.py,sha256=Wki9qIY2CgYnuHbmnyKVIYkcKAN_oYS4up93XMikBaI,222
42
+ GameSentenceMiner/util/communication/websocket.py,sha256=gPgxA2R2U6QZJjPqbUgODC87gtacPhmuC8lCprIkSmA,3287
43
+ GameSentenceMiner/util/downloader/Untitled_json.py,sha256=RUUl2bbbCpUDUUS0fP0tdvf5FngZ7ILdA_J5TFYAXUQ,15272
44
+ GameSentenceMiner/util/downloader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
+ GameSentenceMiner/util/downloader/download_tools.py,sha256=mvnOjDHFlV1AbjHaNI7mdnC5_CH5k3N4n1ezqzzbzGA,8139
46
+ GameSentenceMiner/util/downloader/oneocr_dl.py,sha256=o3ANp5IodEQoQ8GPcJdg9Y8JzA_lictwnebFPwwUZVk,10144
47
+ GameSentenceMiner/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
+ GameSentenceMiner/web/texthooking_page.py,sha256=RR70Vgde3wNHarQHbB-LBbEP-z95vRD5rtlW0GgdjmQ,15037
49
+ GameSentenceMiner/web/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
+ GameSentenceMiner/web/static/apple-touch-icon.png,sha256=OcMI8af_68DA_tweOsQ5LytTyMwm7-hPW07IfrOVgEs,46132
51
+ GameSentenceMiner/web/static/favicon-96x96.png,sha256=lOePzjiKl1JY2J1kT_PMdyEnrlJmi5GWbmXJunM12B4,16502
52
+ GameSentenceMiner/web/static/favicon.ico,sha256=7d25r_FBqRSNsAoEHpSzNoT7zyVt2DJRLNDNq_HYoX8,15086
53
+ GameSentenceMiner/web/static/favicon.svg,sha256=x305AP6WlXGtrXIZlaQspdLmwteoFYUoe5FyJ9MYlJ8,11517
54
+ GameSentenceMiner/web/static/site.webmanifest,sha256=kaeNT-FjFt-T7JGzOhXH7YSqsrDeiplZ2kDxCN_CFU4,436
55
+ GameSentenceMiner/web/static/style.css,sha256=bPZK0NVMuyRl5NNDuT7ZTzVLKlvSsdmeVHmAW4y5FM0,7001
56
+ GameSentenceMiner/web/static/web-app-manifest-192x192.png,sha256=EfSNnBmsSaLfESbkGfYwbKzcjKOdzuWo18ABADfN974,51117
57
+ GameSentenceMiner/web/static/web-app-manifest-512x512.png,sha256=wyqgCWCrLEUxSRXmaA3iJEESd-vM-ZmlTtZFBY4V8Pk,230819
58
+ GameSentenceMiner/web/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
+ GameSentenceMiner/web/templates/index.html,sha256=HZKiIjiGJV8PGQ9T2aLDUNSfJn71qOwbYCjbRuSIjpY,213583
60
+ GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
61
+ GameSentenceMiner/web/templates/utility.html,sha256=3flZinKNqUJ7pvrZk6xu__v67z44rXnaK7UTZ303R-8,16946
62
+ gamesentenceminer-2.9.6.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
63
+ gamesentenceminer-2.9.6.dist-info/METADATA,sha256=dXyoIXRhD6Qg7iCyRhGgoWjgR9PbS40yCmltnqTJZAA,7250
64
+ gamesentenceminer-2.9.6.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
65
+ gamesentenceminer-2.9.6.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
66
+ gamesentenceminer-2.9.6.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
67
+ gamesentenceminer-2.9.6.dist-info/RECORD,,
@@ -1,82 +0,0 @@
1
- import os
2
- import tempfile
3
- import time
4
-
5
- from groq import Groq
6
-
7
- # Assuming these are available from GameSentenceMiner
8
- from GameSentenceMiner import configuration, ffmpeg
9
- from GameSentenceMiner.configuration import get_config, logger, GROQ # Import specific functions/objects
10
- from GameSentenceMiner.vad.result import VADResult
11
- from GameSentenceMiner.vad.vad_utils import get_audio_length
12
-
13
- # Initialize Groq Client
14
- client = Groq(api_key=get_config().ai.groq_api_key)
15
-
16
- def detect_voice_with_groq(input_audio_path):
17
- """
18
- Detects voice activity and extracts speech timestamps using the Groq Whisper API.
19
- """
20
- try:
21
- with open(input_audio_path, "rb") as file:
22
- transcription = client.audio.transcriptions.create(
23
- file=(os.path.basename(input_audio_path), file.read()),
24
- model="whisper-large-v3-turbo",
25
- response_format="verbose_json",
26
- language=get_config().vad.language,
27
- temperature=0.0,
28
- timestamp_granularities=["segment"],
29
- prompt=f"Start detecting speech from the first spoken word. If there is music or background noise, ignore it completely. Be very careful to not hallucinate on silence. If the transcription is anything but language:{get_config().vad.language}, ignore it completely. If the end of the audio seems like the start of a new sentence, ignore it completely.",
30
- )
31
-
32
- logger.debug(transcription)
33
-
34
- # print(transcription)
35
-
36
- speech_segments = transcription.segments if hasattr(transcription, 'segments') else []
37
- # print(f"Groq speech segments: {speech_segments}")
38
-
39
- audio_length = get_audio_length(input_audio_path)
40
- # print(f"FFPROBE Length of input audio: {audio_length}")
41
-
42
- return speech_segments, audio_length
43
- except Exception as e:
44
- logger.error(f"Error detecting voice with Groq: {e}")
45
- return [], 0.0
46
-
47
- def process_audio_with_groq(input_audio, output_audio, game_line):
48
- """
49
- Processes an audio file by detecting voice activity using Groq Whisper API,
50
- trimming the audio based on detected speech timestamps, and saving the trimmed audio.
51
- """
52
- start = time.time()
53
- voice_activity, audio_length = detect_voice_with_groq(input_audio)
54
- logger.info(f"Processing time for Groq: {time.time() - start:.2f} seconds")
55
-
56
- if not voice_activity:
57
- logger.info(f"No voice activity detected in {input_audio}")
58
- return VADResult(False, 0, 0, GROQ)
59
-
60
- start_time = voice_activity[0]['start']
61
- end_time = voice_activity[-1]['end']
62
-
63
- # Logic to potentially use the second-to-last timestamp if a next game line is expected
64
- # and there's a significant pause before the very last segment.
65
- if (game_line and hasattr(game_line, 'next') and game_line.next and
66
- len(voice_activity) > 1 and
67
- (voice_activity[-1]['start'] - voice_activity[-2]['end']) > 3.0):
68
- end_time = voice_activity[-2]['end']
69
- logger.info("Using the second last timestamp for trimming due to game_line.next and significant pause.")
70
-
71
- # Apply offsets from configuration, ensuring times are within valid bounds
72
- final_start_time = max(0, start_time + get_config().vad.beginning_offset)
73
- final_end_time = min(audio_length, end_time + get_config().audio.end_offset)
74
-
75
- logger.debug(f"Trimming {input_audio} from {final_start_time:.2f}s to {final_end_time:.2f}s into {output_audio}")
76
-
77
- ffmpeg.trim_audio(input_audio, final_start_time, final_end_time, output_audio)
78
-
79
- return VADResult(True, final_start_time, final_end_time, GROQ)
80
-
81
- # Example usage (uncomment and modify with your actual file paths for testing)
82
- # process_audio_with_groq("tmp6x81cy27.opus", "tmp6x81cy27_trimmed_groq.opus", None)
@@ -1,21 +0,0 @@
1
- from GameSentenceMiner.configuration import get_config
2
-
3
-
4
- class VADResult:
5
- def __init__(self, success: bool, start: float, end: float, model: str):
6
- self.success = success
7
- self.start = start
8
- self.end = end
9
- self.model = model
10
-
11
- def __repr__(self):
12
- return f"VADResult(success={self.success}, start={self.start}, end={self.end}, model={self.model})"
13
-
14
- def trim_successful_string(self):
15
- if self.success:
16
- if get_config().vad.trim_beginning:
17
- return f"Trimmed audio from {self.start:.2f} to {self.end:.2f} seconds using {self.model}."
18
- else:
19
- return f"Trimmed end of audio to {self.end:.2f} seconds using {self.model}."
20
- else:
21
- return f"Failed to trim audio using {self.model}."
@@ -1,52 +0,0 @@
1
- import tempfile
2
-
3
- from silero_vad import load_silero_vad, read_audio, get_speech_timestamps
4
-
5
- from GameSentenceMiner import configuration, ffmpeg
6
- from GameSentenceMiner.configuration import *
7
- from GameSentenceMiner.vad.result import VADResult
8
- from GameSentenceMiner.vad.vad_utils import get_audio_length
9
-
10
- # Silero VAD setup
11
- vad_model = load_silero_vad()
12
-
13
-
14
- # Use Silero to detect voice activity with timestamps in the audio
15
- def detect_voice_with_silero(input_audio):
16
- # Convert the audio to 16kHz mono WAV
17
- temp_wav = tempfile.NamedTemporaryFile(dir=configuration.get_temporary_directory(), suffix='.wav').name
18
- ffmpeg.convert_audio_to_wav(input_audio, temp_wav)
19
-
20
- # Load the audio and detect speech timestamps
21
- wav = read_audio(temp_wav)
22
- speech_timestamps = get_speech_timestamps(wav, vad_model, return_seconds=True)
23
-
24
- logger.debug(speech_timestamps)
25
-
26
- # Return the speech timestamps (start and end in seconds)
27
- return speech_timestamps, len(wav) / 16000
28
-
29
-
30
- # Example usage of Silero with trimming
31
- def process_audio_with_silero(input_audio, output_audio, game_line):
32
- voice_activity, audio_length = detect_voice_with_silero(input_audio)
33
-
34
- if not voice_activity:
35
- return VADResult(False, 0, 0, SILERO)
36
-
37
- # Trim based on the first and last speech detected
38
- start_time = voice_activity[0]['start'] if voice_activity else 0
39
- if game_line and game_line.next and len(voice_activity) > 1 and 0 > audio_length - voice_activity[-1]['start'] + get_config().audio.beginning_offset:
40
- # and (voice_activity[-1]['start'] - voice_activity[-2]['end']) > 3.0):
41
- end_time = voice_activity[-2]['end']
42
- logger.info("Using the second last timestamp for trimming")
43
- else:
44
- end_time = voice_activity[-1]['end'] if voice_activity else 0
45
-
46
- # Trim the audio using FFmpeg
47
- ffmpeg.trim_audio(input_audio, start_time + get_config().vad.beginning_offset, end_time + get_config().audio.end_offset, output_audio)
48
- return VADResult(True, start_time + get_config().vad.beginning_offset, end_time + get_config().audio.end_offset, SILERO)
49
-
50
-
51
- # process_audio_with_silero("tmp6x81cy27.opus", "tmp6x81cy27_trimmed.opus", None)
52
- # print(detect_voice_with_silero("tmp6x81cy27.opus"))
@@ -1,13 +0,0 @@
1
- import subprocess
2
-
3
- from GameSentenceMiner.ffmpeg import get_ffprobe_path
4
-
5
-
6
- def get_audio_length(path):
7
- result = subprocess.run(
8
- [get_ffprobe_path(), "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", path],
9
- stdout=subprocess.PIPE,
10
- stderr=subprocess.PIPE,
11
- text=True
12
- )
13
- return float(result.stdout.strip())
@@ -1,158 +0,0 @@
1
- import tarfile
2
- import tempfile
3
- import zipfile
4
-
5
- import numpy as np
6
- import requests
7
- import soundfile as sf
8
- import vosk
9
-
10
- from GameSentenceMiner import configuration, ffmpeg
11
- from GameSentenceMiner.configuration import *
12
- from GameSentenceMiner.vad.result import VADResult
13
-
14
- ffmpeg_base_command_list = ["ffmpeg", "-hide_banner", "-loglevel", "error"]
15
- vosk.SetLogLevel(-1)
16
- vosk_model_path = ''
17
- vosk_model = None
18
-
19
-
20
- # Function to download and cache the Vosk model
21
- def download_and_cache_vosk_model(model_dir="vosk_model_cache"):
22
- # Ensure the cache directory exists
23
- if not os.path.exists(os.path.join(get_app_directory(), model_dir)):
24
- os.makedirs(os.path.join(get_app_directory(), model_dir))
25
-
26
- # Extract the model name from the URL
27
- model_filename = get_config().vad.vosk_url.split("/")[-1]
28
- model_path = os.path.join(get_app_directory(), model_dir, model_filename)
29
-
30
- # If the model is already downloaded, skip the download
31
- if not os.path.exists(model_path):
32
- logger.info(
33
- f"Downloading the Vosk model from {get_config().vad.vosk_url}... This will take a while if using large model, ~1G")
34
- response = requests.get(get_config().vad.vosk_url, stream=True)
35
- with open(model_path, "wb") as file:
36
- for chunk in response.iter_content(chunk_size=8192):
37
- if chunk:
38
- file.write(chunk)
39
- logger.info("Download complete.")
40
-
41
- # Extract the model if it's a zip or tar file
42
- model_extract_path = os.path.join(get_app_directory(), model_dir, "vosk_model")
43
- if not os.path.exists(model_extract_path):
44
- logger.info("Extracting the Vosk model...")
45
- if model_filename.endswith(".zip"):
46
- with zipfile.ZipFile(model_path, "r") as zip_ref:
47
- zip_ref.extractall(model_extract_path)
48
- elif model_filename.endswith(".tar.gz"):
49
- with tarfile.open(model_path, "r:gz") as tar_ref:
50
- tar_ref.extractall(model_extract_path)
51
- else:
52
- logger.info("Unknown archive format. Model extraction skipped.")
53
- logger.info(f"Model extracted to {model_extract_path}.")
54
- else:
55
- logger.info(f"Model already extracted at {model_extract_path}.")
56
-
57
- # Return the path to the actual model folder inside the extraction directory
58
- extracted_folders = os.listdir(model_extract_path)
59
- if extracted_folders:
60
- actual_model_folder = os.path.join(model_extract_path,
61
- extracted_folders[0]) # Assuming the first folder is the model
62
- return actual_model_folder
63
- else:
64
- return model_extract_path # In case there's no subfolder, return the extraction path directly
65
-
66
-
67
- # Use Vosk to detect voice activity with timestamps in the audio
68
- def detect_voice_with_vosk(input_audio):
69
- global vosk_model_path, vosk_model
70
- # Convert the audio to 16kHz mono WAV
71
- temp_wav = tempfile.NamedTemporaryFile(dir=configuration.get_temporary_directory(), suffix='.wav').name
72
- ffmpeg.convert_audio_to_wav(input_audio, temp_wav)
73
-
74
- if not vosk_model_path or not vosk_model:
75
- vosk_model_path = download_and_cache_vosk_model()
76
- vosk_model = vosk.Model(vosk_model_path)
77
-
78
- # Open the audio file
79
- with sf.SoundFile(temp_wav) as audio_file:
80
- recognizer = vosk.KaldiRecognizer(vosk_model, audio_file.samplerate)
81
- voice_activity = []
82
- total_duration = len(audio_file) / audio_file.samplerate # Get total duration in seconds
83
-
84
- recognizer.SetWords(True)
85
- # recognizer.SetPartialWords(True)
86
-
87
- # Process audio in chunks
88
- while True:
89
- data = audio_file.buffer_read(4000, dtype='int16')
90
- if len(data) == 0:
91
- break
92
-
93
- # Convert buffer to bytes using NumPy
94
- data_bytes = np.frombuffer(data, dtype='int16').tobytes()
95
-
96
- if recognizer.AcceptWaveform(data_bytes):
97
- pass
98
-
99
- final_result = json.loads(recognizer.FinalResult())
100
- if 'result' in final_result:
101
- should_use = False
102
- unique_words = set()
103
- for word in final_result['result']:
104
- if word['conf'] >= .90:
105
- logger.debug(word)
106
- should_use = True
107
- unique_words.add(word['word'])
108
- if len(unique_words) == 1 or all(item in ['えー', 'ん'] for item in unique_words):
109
- should_use = False
110
-
111
- if not should_use:
112
- return None, 0
113
-
114
- for word in final_result['result']:
115
- voice_activity.append({
116
- 'text': word['word'],
117
- 'start': word['start'],
118
- 'end': word['end']
119
- })
120
-
121
- # Return the detected voice activity and the total duration
122
- return voice_activity, total_duration
123
-
124
-
125
- # Example usage of Vosk with trimming
126
- def process_audio_with_vosk(input_audio, output_audio, game_line):
127
- voice_activity, total_duration = detect_voice_with_vosk(input_audio)
128
-
129
- if not voice_activity:
130
- logger.info("No voice activity detected in the audio.")
131
- return VADResult(False, 0, 0, VOSK)
132
-
133
- # Trim based on the first and last speech detected
134
- start_time = voice_activity[0]['start'] if voice_activity else 0
135
- # if (game_line.next and len(voice_activity) > 1
136
- # and voice_activity[-1]['start'] - get_config().audio.beginning_offset > len(input_audio) / 16000
137
- # and (voice_activity[-1]['start'] - voice_activity[-2]['end']) > 5.0):
138
- # end_time = voice_activity[-2]['end']
139
- # logger.info("Using the second last timestamp for trimming")
140
- # else:
141
- end_time = voice_activity[-1]['end'] if voice_activity else 0
142
-
143
- if get_config().vad.trim_beginning:
144
- logger.info(f"VAD Trimmed Beginning of Audio to {start_time}")
145
-
146
- # Print detected speech details with timestamps
147
- logger.info(f"VAD Trimmed End of Audio to {end_time} seconds:")
148
-
149
- # Trim the audio using FFmpeg
150
- ffmpeg.trim_audio(input_audio, start_time + get_config().vad.beginning_offset, end_time + get_config().audio.end_offset, output_audio)
151
- return VADResult(True, start_time + get_config().vad.beginning_offset, end_time + get_config().audio.end_offset, VOSK)
152
-
153
-
154
- def get_vosk_model():
155
- global vosk_model_path, vosk_model
156
- vosk_model_path = download_and_cache_vosk_model()
157
- vosk_model = vosk.Model(vosk_model_path)
158
- logger.info(f"Using Vosk model from {vosk_model_path}")
@@ -1,105 +0,0 @@
1
- import tempfile
2
- import warnings
3
-
4
- import stable_whisper as whisper
5
- import torch
6
- from stable_whisper import WhisperResult
7
-
8
- from GameSentenceMiner import configuration, ffmpeg
9
- from GameSentenceMiner.configuration import *
10
- from GameSentenceMiner.vad.result import VADResult
11
-
12
- ffmpeg_base_command_list = ["ffmpeg", "-hide_banner", "-loglevel", "error"]
13
- whisper_model = None
14
-
15
-
16
- # Function to download and load the Whisper model
17
- def load_whisper_model():
18
- global whisper_model
19
- if whisper_model is None:
20
- with warnings.catch_warnings(action="ignore"):
21
- whisper_model = whisper.load_model(get_config().vad.whisper_model)
22
- logger.info(f"Whisper model '{get_config().vad.whisper_model}' loaded.")
23
-
24
-
25
- # Use Whisper to detect voice activity with timestamps in the audio
26
- def detect_voice_with_whisper(input_audio):
27
- # Convert the audio to 16kHz mono WAV
28
- temp_wav = tempfile.NamedTemporaryFile(dir=configuration.get_temporary_directory(), suffix='.wav').name
29
- ffmpeg.convert_audio_to_wav(input_audio, temp_wav)
30
-
31
- # Make sure Whisper is loaded
32
- load_whisper_model()
33
-
34
- logger.info('transcribing audio...')
35
-
36
- # Transcribe the audio using Whisper
37
- with warnings.catch_warnings(action="ignore"):
38
- result: WhisperResult = whisper_model.transcribe(temp_wav, vad=True, language=get_config().vad.language, temperature=0.0)
39
- voice_activity = []
40
-
41
- logger.debug(result.to_dict())
42
-
43
- # Process the segments to extract tokens, timestamps, and confidence
44
- for segment in result.segments:
45
- logger.debug(segment.to_dict())
46
- for word in segment.words:
47
- logger.debug(word.to_dict())
48
- confidence = word.probability
49
- if confidence > .1:
50
- logger.debug(word)
51
- voice_activity.append({
52
- 'text': word.word,
53
- 'start': word.start,
54
- 'end': word.end,
55
- 'confidence': word.probability
56
- })
57
-
58
- # Analyze the detected words to decide whether to use the audio
59
- should_use = False
60
- unique_words = set(word['text'] for word in voice_activity)
61
- if len(unique_words) > 1 or not all(item in ['えー', 'ん'] for item in unique_words):
62
- should_use = True
63
-
64
- if not should_use:
65
- return None
66
-
67
- # Return the detected voice activity and the total duration
68
- return voice_activity
69
-
70
-
71
- # Example usage of Whisper with trimming
72
- def process_audio_with_whisper(input_audio, output_audio, game_line):
73
- voice_activity = detect_voice_with_whisper(input_audio)
74
-
75
- if not voice_activity:
76
- logger.info("No voice activity detected in the audio.")
77
- return VADResult(False, 0, 0, WHISPER)
78
-
79
- # Trim based on the first and last speech detected
80
- start_time = voice_activity[0]['start'] if voice_activity else 0
81
- # if (game_line.next and len(voice_activity) > 1
82
- # and voice_activity[-1]['start'] - get_config().audio.beginning_offset > len(input_audio) / 16000
83
- # and (voice_activity[-1]['start'] - voice_activity[-2]['end']) > 5.0):
84
- # end_time = voice_activity[-2]['end']
85
- # logger.info("Using the second last timestamp for trimming")
86
- # else:
87
- end_time = voice_activity[-1]['end'] if voice_activity else 0
88
-
89
- if get_config().vad.trim_beginning:
90
- logger.info(f"VAD Trimmed Beginning of Audio to {start_time}")
91
-
92
- # Print detected speech details with timestamps
93
- logger.info(f"VAD Trimmed End of Audio to {end_time} seconds:")
94
-
95
- # Trim the audio using FFmpeg
96
- ffmpeg.trim_audio(input_audio, start_time + get_config().vad.beginning_offset, end_time + get_config().audio.end_offset, output_audio)
97
- return VADResult(True, start_time + get_config().vad.beginning_offset, end_time + get_config().audio.end_offset, WHISPER)
98
-
99
-
100
- # Load Whisper model initially
101
- def initialize_whisper_model():
102
- load_whisper_model()
103
-
104
- # initialize_whisper_model()
105
- # process_audio_with_whisper("tmp6x81cy27.opus", "tmp6x81cy27_trimmed.opus", None)
@@ -1,72 +0,0 @@
1
- GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- GameSentenceMiner/anki.py,sha256=HAYblBjkth5p0tx9Z0LNQn6zuQC8_54RIB2BMzN8-LE,14705
3
- GameSentenceMiner/config_gui.py,sha256=h4zz85gfhxSphaJ-IZSu9D4jR70mDlKecZ9JRCO5Noc,80927
4
- GameSentenceMiner/configuration.py,sha256=KKW6fmpxya4dmXx9cERFVrzsKCTw0vmZrF2HAJDURBU,25667
5
- GameSentenceMiner/electron_config.py,sha256=dGcPYCISPehXubYSzsDuI2Gl092MYK0u3bTnkL9Jh1Y,9787
6
- GameSentenceMiner/ffmpeg.py,sha256=APa2vNdAgxYdG96_Z3Xdh1WqOiWaK6gTLJqzEvCMMeU,18323
7
- GameSentenceMiner/gametext.py,sha256=sll-6Pficd4ZXYy8yL8hBrEOSpfa53TOye7vtHHKFN4,6218
8
- GameSentenceMiner/gsm.py,sha256=olG3BIWjbVHWTsRKmeDVE5X8XrgppWke73Fy1J15dxA,29868
9
- GameSentenceMiner/model.py,sha256=1lRyJFf_LND_4O16h8CWVqDfosLgr0ZS6ufBZ3qJHpY,5699
10
- GameSentenceMiner/notification.py,sha256=e6TOzZJD7RTvMgxaY-V01r5OiocHhdqEIVdAnj4MGSw,3437
11
- GameSentenceMiner/obs.py,sha256=JiydRMpfSpNZ0nDAzH8OOECbYbsxMNSGz46lO8lZbvA,14871
12
- GameSentenceMiner/package.py,sha256=uu3Yb3pqu8vN5ISzP87YCHlFNR9wxMMf5hPRncTr7ws,1181
13
- GameSentenceMiner/ss_selector.py,sha256=csey9H3561-guRJcT6gQN6hXxvylP0CBI0dp2-kwo2Q,4446
14
- GameSentenceMiner/text_log.py,sha256=U2_g8THAYeexRiE2bLk_bCt_2ShiA8SQ9VdJsi4riHs,5181
15
- GameSentenceMiner/util.py,sha256=ZbK7i1UeOzKyc5WtCcttiGljR_stfu7qpnEpgqFBwro,8976
16
- GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- GameSentenceMiner/ai/ai_prompting.py,sha256=xw8et6XNwQiDXOXZnw8iIntVSg8lni4YYZbgWsK7qDE,10013
18
- GameSentenceMiner/assets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- GameSentenceMiner/assets/icon.png,sha256=9GRL8uXUAgkUSlvbm9Pv9o2poFVRGdW6s2ub_DeUD9M,937624
20
- GameSentenceMiner/assets/icon128.png,sha256=l90j7biwdz5ahwOd5wZ-406ryEV9Pan93dquJQ3e1CI,18395
21
- GameSentenceMiner/assets/icon256.png,sha256=JEW46wOrG1KR-907rvFaEdNbPtj5gu0HJmG7qUnIHxQ,51874
22
- GameSentenceMiner/assets/icon32.png,sha256=Kww0hU_qke9_22wBuO_Nq0Dv2SfnOLwMhCyGgbgXdg8,6089
23
- GameSentenceMiner/assets/icon512.png,sha256=HxUj2GHjyQsk8NV433256UxU9phPhtjCY-YB_7W4sqs,192487
24
- GameSentenceMiner/assets/icon64.png,sha256=N8xgdZXvhqVQP9QUK3wX5iqxX9LxHljD7c-Bmgim6tM,9301
25
- GameSentenceMiner/assets/pickaxe.png,sha256=VfIGyXyIZdzEnVcc4PmG3wszPMO1W4KCT7Q_nFK6eSE,1403829
26
- GameSentenceMiner/communication/__init__.py,sha256=_jGn9PJxtOAOPtJ2rI-Qu9hEHVZVpIvWlxKvqk91_zI,638
27
- GameSentenceMiner/communication/send.py,sha256=X0MytGv5hY-uUvkfvdCqQA_ljZFmV6UkJ6in1TA1bUE,217
28
- GameSentenceMiner/communication/websocket.py,sha256=8eFZaTtoFggEPdqw2Jl4zqHC2I7J3-Gk27CxVX7SyBo,3277
29
- GameSentenceMiner/downloader/Untitled_json.py,sha256=RUUl2bbbCpUDUUS0fP0tdvf5FngZ7ILdA_J5TFYAXUQ,15272
30
- GameSentenceMiner/downloader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- GameSentenceMiner/downloader/download_tools.py,sha256=aRfpCqEmKUFRVsGipwY-7PhY6AeWiFJanW4ZCB9e2iE,8124
32
- GameSentenceMiner/downloader/oneocr_dl.py,sha256=o3ANp5IodEQoQ8GPcJdg9Y8JzA_lictwnebFPwwUZVk,10144
33
- GameSentenceMiner/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
- GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=fEQ2o2NXksGRHpueO8c4TfAp75GEdAtAr1ngTFOsdpg,2257
35
- GameSentenceMiner/ocr/ocrconfig.py,sha256=_tY8mjnzHMJrLS8E5pHqYXZjMuLoGKYgJwdhYgN-ny4,6466
36
- GameSentenceMiner/ocr/owocr_area_selector.py,sha256=Q8ETMHL7BKMA1mbtjrntDLyqCQB0lZ5T4RCZsodjH7Y,47186
37
- GameSentenceMiner/ocr/owocr_helper.py,sha256=M4Is-Ki5O3r4ixYhILibfjrVGD6xDlOcR3YvVGmETQ4,17363
38
- GameSentenceMiner/owocr/owocr/__init__.py,sha256=opjBOyGGyEqZCE6YdZPnyt7nVfiwyELHsXA0jAsjm14,25
39
- GameSentenceMiner/owocr/owocr/__main__.py,sha256=XQaqZY99EKoCpU-gWQjNbTs7Kg17HvBVE7JY8LqIE0o,157
40
- GameSentenceMiner/owocr/owocr/config.py,sha256=qM7kISHdUhuygGXOxmgU6Ef2nwBShrZtdqu4InDCViE,8103
41
- GameSentenceMiner/owocr/owocr/lens_betterproto.py,sha256=oNoISsPilVVRBBPVDtb4-roJtAhp8ZAuFTci3TGXtMc,39141
42
- GameSentenceMiner/owocr/owocr/ocr.py,sha256=V0HqVRQlaE1-12IH480IupfSv1BlDdEcwNPejhQZfS0,42292
43
- GameSentenceMiner/owocr/owocr/run.py,sha256=0UyjOKEP0MqSdCaagCUMGdqO-BMexPxCl7ZabGlic4E,54749
44
- GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=Na6XStbQBtpQUSdbN3QhEswtKuU1JjReFk_K8t5ezQE,3395
45
- GameSentenceMiner/vad/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
- GameSentenceMiner/vad/groq_trim.py,sha256=MDYiApduwF7oDx3r0TXL3xQrTkbUC1RinMwNKSbF5gw,3764
47
- GameSentenceMiner/vad/result.py,sha256=aFlr2px90fn3qXj49dwF9BDXA5m4yXD_HYH01CVvP1U,799
48
- GameSentenceMiner/vad/silero_trim.py,sha256=u4BC93LieJW0CZ7HToz51FneojqW_SNjSKmJmHMKwUA,2240
49
- GameSentenceMiner/vad/vad_utils.py,sha256=_YC6rW2eXSBeLnYbVl_F3na1KCRL90VrnOzKYJ9RhUE,391
50
- GameSentenceMiner/vad/vosk_helper.py,sha256=h7yNHrzrzT-J74UniA0T2ZX8cHqhflCzwyDjoIdKLO4,6479
51
- GameSentenceMiner/vad/whisper_helper.py,sha256=B64-Eq_ZMCIyQX_A8uvYz-c48hSXJAyz6tSXNRaLjtA,4020
52
- GameSentenceMiner/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
- GameSentenceMiner/web/texthooking_page.py,sha256=7Z7TGDcnj-94Y9ws7bQph4oIXrqf8Q9qVKowKimHWxM,14749
54
- GameSentenceMiner/web/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
- GameSentenceMiner/web/static/apple-touch-icon.png,sha256=OcMI8af_68DA_tweOsQ5LytTyMwm7-hPW07IfrOVgEs,46132
56
- GameSentenceMiner/web/static/favicon-96x96.png,sha256=lOePzjiKl1JY2J1kT_PMdyEnrlJmi5GWbmXJunM12B4,16502
57
- GameSentenceMiner/web/static/favicon.ico,sha256=7d25r_FBqRSNsAoEHpSzNoT7zyVt2DJRLNDNq_HYoX8,15086
58
- GameSentenceMiner/web/static/favicon.svg,sha256=x305AP6WlXGtrXIZlaQspdLmwteoFYUoe5FyJ9MYlJ8,11517
59
- GameSentenceMiner/web/static/site.webmanifest,sha256=kaeNT-FjFt-T7JGzOhXH7YSqsrDeiplZ2kDxCN_CFU4,436
60
- GameSentenceMiner/web/static/style.css,sha256=bPZK0NVMuyRl5NNDuT7ZTzVLKlvSsdmeVHmAW4y5FM0,7001
61
- GameSentenceMiner/web/static/web-app-manifest-192x192.png,sha256=EfSNnBmsSaLfESbkGfYwbKzcjKOdzuWo18ABADfN974,51117
62
- GameSentenceMiner/web/static/web-app-manifest-512x512.png,sha256=wyqgCWCrLEUxSRXmaA3iJEESd-vM-ZmlTtZFBY4V8Pk,230819
63
- GameSentenceMiner/web/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
- GameSentenceMiner/web/templates/index.html,sha256=HZKiIjiGJV8PGQ9T2aLDUNSfJn71qOwbYCjbRuSIjpY,213583
65
- GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
66
- GameSentenceMiner/web/templates/utility.html,sha256=3flZinKNqUJ7pvrZk6xu__v67z44rXnaK7UTZ303R-8,16946
67
- gamesentenceminer-2.9.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
68
- gamesentenceminer-2.9.4.dist-info/METADATA,sha256=jNhOj4IaiTEJqGlm4-VCtviW5hmN8J5XnvPBGWgcY0Y,7280
69
- gamesentenceminer-2.9.4.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
70
- gamesentenceminer-2.9.4.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
71
- gamesentenceminer-2.9.4.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
72
- gamesentenceminer-2.9.4.dist-info/RECORD,,
File without changes
File without changes