GameSentenceMiner 2.7.16__py3-none-any.whl → 2.8.0__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 (35) hide show
  1. GameSentenceMiner/anki.py +7 -8
  2. GameSentenceMiner/config_gui.py +19 -3
  3. GameSentenceMiner/configuration.py +8 -1
  4. GameSentenceMiner/ffmpeg.py +1 -3
  5. GameSentenceMiner/gametext.py +16 -155
  6. GameSentenceMiner/gsm.py +28 -29
  7. GameSentenceMiner/obs.py +0 -3
  8. GameSentenceMiner/ocr/ocrconfig.py +0 -1
  9. GameSentenceMiner/ocr/oneocr_dl.py +243 -0
  10. GameSentenceMiner/ocr/owocr_area_selector.py +0 -1
  11. GameSentenceMiner/ocr/owocr_helper.py +25 -26
  12. GameSentenceMiner/owocr/owocr/run.py +1 -1
  13. GameSentenceMiner/text_log.py +186 -0
  14. GameSentenceMiner/util.py +52 -3
  15. GameSentenceMiner/web/__init__.py +0 -0
  16. GameSentenceMiner/web/static/__init__.py +0 -0
  17. GameSentenceMiner/web/static/apple-touch-icon.png +0 -0
  18. GameSentenceMiner/web/static/favicon-96x96.png +0 -0
  19. GameSentenceMiner/web/static/favicon.ico +0 -0
  20. GameSentenceMiner/web/static/favicon.svg +3 -0
  21. GameSentenceMiner/web/static/site.webmanifest +21 -0
  22. GameSentenceMiner/web/static/style.css +292 -0
  23. GameSentenceMiner/web/static/text_replacements.html +238 -0
  24. GameSentenceMiner/web/static/utility.html +313 -0
  25. GameSentenceMiner/web/static/web-app-manifest-192x192.png +0 -0
  26. GameSentenceMiner/web/static/web-app-manifest-512x512.png +0 -0
  27. GameSentenceMiner/web/texthooking_page.py +234 -0
  28. {gamesentenceminer-2.7.16.dist-info → gamesentenceminer-2.8.0.dist-info}/METADATA +2 -1
  29. gamesentenceminer-2.8.0.dist-info/RECORD +58 -0
  30. {gamesentenceminer-2.7.16.dist-info → gamesentenceminer-2.8.0.dist-info}/WHEEL +1 -1
  31. GameSentenceMiner/utility_gui.py +0 -204
  32. gamesentenceminer-2.7.16.dist-info/RECORD +0 -44
  33. {gamesentenceminer-2.7.16.dist-info → gamesentenceminer-2.8.0.dist-info}/entry_points.txt +0 -0
  34. {gamesentenceminer-2.7.16.dist-info → gamesentenceminer-2.8.0.dist-info}/licenses/LICENSE +0 -0
  35. {gamesentenceminer-2.7.16.dist-info → gamesentenceminer-2.8.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,313 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>GSM TextHooker</title>
6
+ <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
7
+ <style>
8
+ body {
9
+ background-color: #121212;
10
+ color: #e0e0e0;
11
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
12
+ margin: 20px;
13
+ }
14
+
15
+ h2 {
16
+ color: #ffffff;
17
+ text-align: center;
18
+ font-weight: 300;
19
+ margin-bottom: 20px;
20
+ }
21
+
22
+ .textline {
23
+ margin: 15px 0;
24
+ padding: 15px;
25
+ display: flex;
26
+ align-items: center;
27
+ gap: 15px;
28
+ }
29
+ .textline:last-child {
30
+ border-bottom: none;
31
+ }
32
+
33
+ .textline > p {
34
+ font-size: 24px;
35
+ flex: 1;
36
+ min-width: 200px;
37
+ }
38
+
39
+ .textline > em {
40
+ color: #aaa;
41
+ font-size: 0.9em;
42
+ margin-right: 10px;
43
+ }
44
+
45
+ .textline > button {
46
+ background-color: #1a73e8;
47
+ color: #ffffff;
48
+ border: none;
49
+ padding: 8px 15px;
50
+ font-size: 14px;
51
+ cursor: pointer;
52
+ transition: background-color 0.3s;
53
+ border-radius: 5px;
54
+ }
55
+
56
+ .textline > button:hover {
57
+ background-color: #1669c1;
58
+ cursor: pointer;
59
+ }
60
+
61
+ .textline-buttons {
62
+ display: flex;
63
+ gap: 10px;
64
+ }
65
+
66
+ @media (max-width: 600px) {
67
+ .textline {
68
+ flex-direction: column;
69
+ align-items: flex-start;
70
+ }
71
+ .textline-buttons{
72
+ margin-top: 10px;
73
+ }
74
+ .textline > strong{
75
+ min-width: auto;
76
+ }
77
+ }
78
+
79
+ .initial-event {
80
+ margin: 15px 0;
81
+ padding: 15px;
82
+ }
83
+
84
+ hr.initial-events-separator {
85
+ border: 0;
86
+ border-top: 2px solid #aaa;
87
+ margin: 20px 0;
88
+ }
89
+
90
+ .multi-line-checkbox {
91
+ transform: scale(1.5);
92
+ margin-right: 10px;
93
+ background-color: #00FFFF !important; /* Cyan/Electric Blue */
94
+ border: 4px solid #00FFFF; /* Keep the border the same color */
95
+ }
96
+
97
+ .multi-line-checkbox:checked {
98
+ /* You'll likely need to target the checkmark specifically */
99
+ /* Example assuming it's a pseudo-element with a font-based check: */
100
+ /* &::before { */
101
+ /* color: #FFFF00; /* Bright Yellow */
102
+ /* } */
103
+ /* If it's a background image, you might need to adjust the background or use a filter. */
104
+ }
105
+
106
+ </style>
107
+ </head>
108
+ <body>
109
+ <div id="initial-events">
110
+
111
+ </div>
112
+ <hr class="initial-events-separator" style="display: none;">
113
+ <div id="session-events">
114
+
115
+ </div>
116
+ <div>
117
+ <button onclick="window.location.href='/textreplacements'" style="margin-top: 20px; background-color: #1a73e8; color: #ffffff; border: none; padding: 10px 20px; font-size: 16px; cursor: pointer; transition: background-color 0.3s; border-radius: 5px;">
118
+ Text Replacements
119
+ </button>
120
+ </div>
121
+ <script>
122
+ let displayedEventIds = new Set();
123
+ let isTabActive = true;
124
+ let isFetching = false; // Flag to track if a fetch is in progress
125
+ let intervalId = 0;
126
+ const fetchInterval = 100; // Define the interval as a constant
127
+
128
+ // Drag selection variables
129
+ let isDragging = false;
130
+ let dragStartCheckbox = null;
131
+ let newCheckboxState = false;
132
+ let hoveredCheckboxes = new Set();
133
+ let checkboxes = []; // Will hold all checkbox elements
134
+
135
+ // Shift click selection variable
136
+ let lastChecked = null;
137
+
138
+ async function fetchEvents() {
139
+ if (document.hidden || isFetching) {
140
+ return;
141
+ }
142
+ isFetching = true
143
+ try {
144
+ const res = await fetch('/data');
145
+ if (!res.ok) {
146
+ throw new Error(`HTTP error! Status: ${res.status}`);
147
+ }
148
+ const events = await res.json();
149
+
150
+ events.forEach(ev => {
151
+ if (!displayedEventIds.has(ev.id)) {
152
+ addNewEvent(ev)
153
+ }
154
+ const checkbox = document.querySelector(`[data-event-id="${ev.id}"]`);
155
+ if (checkbox) {
156
+ checkbox.checked = ev.checked; // Update the checkbox state
157
+ }
158
+ });
159
+ checkboxes = Array.from(document.querySelectorAll('#session-events input[type="checkbox"]')); // Update checkboxes array after new events
160
+ } catch (error) {
161
+ console.error("Error fetching events:", error);
162
+ } finally {
163
+ isFetching = false;
164
+ }
165
+ }
166
+
167
+ function addNewEvent(event) {
168
+ const container = document.getElementById('session-events');
169
+ const div = document.createElement('div');
170
+ div.className = 'textline';
171
+ div.innerHTML = `
172
+ <input type="checkbox"
173
+ class="multi-line-checkbox"
174
+ id="multi-line-checkbox-${event.id}"
175
+ ${event.checked ? 'checked' : ''}
176
+ aria-label="Mark item"
177
+ data-event-id="${event.id}"
178
+ onchange="toggleCheckbox('${event.id}', this.checked)">
179
+ <p>${event.text}</p>
180
+ <em>${event.time.replace(" GMT", "")}</em>
181
+ <div class="textline-buttons">
182
+ <button onclick="buttonClick('${event.id}', 'Screenshot')">Screenshot</button>
183
+ <button onclick="buttonClick('${event.id}', 'Audio')">Audio</button>
184
+ </div>
185
+ `;
186
+ container.appendChild(div);
187
+ displayedEventIds.add(event.id);
188
+ window.scrollTo({
189
+ top: document.documentElement.scrollHeight,
190
+ behavior: 'smooth'
191
+ });
192
+ }
193
+
194
+ function buttonClick(id, action) {
195
+ console.log(id);
196
+ const endpoint = action === 'Screenshot' ? '/get-screenshot' : '/play-audio';
197
+ fetch(endpoint, {
198
+ method: 'POST',
199
+ headers: { 'Content-Type': 'application/json' },
200
+ body: JSON.stringify({ id })
201
+ })
202
+ .then(response => {
203
+ if (!response.ok) {
204
+ throw new Error(`HTTP error! Status: ${response.status}`);
205
+ }
206
+ return response.json();
207
+ })
208
+ .then(data => {
209
+ console.log(`${action} action completed for event ID: ${id}`, data);
210
+ })
211
+ .catch(error => {
212
+ console.error(`Error performing ${action} action for event ID: ${id}`, error);
213
+ });
214
+ }
215
+
216
+ async function toggleCheckbox(id, checked) {
217
+ try {
218
+ const res = await fetch('/update', {
219
+ method: 'POST',
220
+ headers: { 'Content-Type': 'application/json' },
221
+ body: JSON.stringify({ id, checked })
222
+ });
223
+ if (!res.ok) {
224
+ throw new Error(`HTTP error! Status: ${res.status}`);
225
+ }
226
+ } catch (error) {
227
+ console.error("Error updating checkbox:", error);
228
+ }
229
+ }
230
+
231
+ function handleMouseDown(e) {
232
+ if (e.target.type === 'checkbox') {
233
+ newCheckboxState = !e.target.checked;
234
+ isDragging = true;
235
+ dragStartCheckbox = e.target;
236
+ hoveredCheckboxes.add(e.target)
237
+ }
238
+ }
239
+
240
+ function handleMouseUp(e) {
241
+ if (e.target === dragStartCheckbox) {
242
+ isDragging = false;
243
+ dragStartCheckbox = null;
244
+ return;
245
+ }
246
+ if (isDragging) {
247
+ isDragging = false;
248
+
249
+ hoveredCheckboxes.forEach(checkbox => {
250
+ checkbox.checked = newCheckboxState; // Set all hovered checkboxes to the new state
251
+ const eventId = checkbox.dataset.eventId;
252
+ toggleCheckbox(eventId, newCheckboxState);
253
+ });
254
+ isDragging = false;
255
+ dragStartCheckbox = null;
256
+ }
257
+
258
+ }
259
+
260
+ function handleMouseOver(e) {
261
+ if (!isDragging || e.target.type !== 'checkbox' || e.target === dragStartCheckbox) {
262
+ return;
263
+ }
264
+ e.preventDefault(); // Prevent text selection during drag
265
+ if (dragStartCheckbox) {
266
+ hoveredCheckboxes.add(e.target);
267
+ }
268
+ }
269
+
270
+ function handleCheckboxClick(e) {
271
+ if (!e.shiftKey) {
272
+ lastChecked = e.target;
273
+ return;
274
+ }
275
+
276
+ if (!lastChecked) return;
277
+
278
+ let inBetween = false;
279
+ checkboxes.forEach(checkbox => {
280
+ if (checkbox === e.target || checkbox === lastChecked) {
281
+ inBetween = !inBetween;
282
+ }
283
+
284
+ if (inBetween) {
285
+ checkbox.checked = lastChecked.checked;
286
+ const eventId = checkbox.dataset.eventId;
287
+ toggleCheckbox(eventId, lastChecked.checked);
288
+ }
289
+ });
290
+
291
+ lastChecked = e.target;
292
+ }
293
+
294
+ document.addEventListener('mousedown', handleMouseDown);
295
+ document.addEventListener('mouseup', handleMouseUp);
296
+ document.addEventListener('mouseover', handleMouseOver);
297
+ document.addEventListener('click', handleCheckboxClick);
298
+
299
+ console.log("Initial load, fetching events and starting interval...");
300
+ fetchEvents();
301
+ intervalId = setInterval(async () => {
302
+ if (isTabActive) {
303
+ await fetchEvents();
304
+ }
305
+ }, fetchInterval);
306
+
307
+ window.scrollTo({
308
+ top: document.documentElement.scrollHeight,
309
+ behavior: 'smooth'
310
+ });
311
+ </script>
312
+ </body>
313
+ </html>
@@ -0,0 +1,234 @@
1
+ import datetime
2
+ import json
3
+ import os
4
+ from dataclasses import dataclass
5
+
6
+ import flask
7
+
8
+ from GameSentenceMiner.text_log import GameLine, get_line_by_id
9
+ from flask import request, jsonify, send_from_directory
10
+ import webbrowser
11
+ from GameSentenceMiner import obs
12
+ from GameSentenceMiner.configuration import logger, get_config
13
+ from GameSentenceMiner.util import TEXT_REPLACEMENTS_FILE
14
+
15
+ port = get_config().general.texthooker_port
16
+ url = f"http://localhost:{port}"
17
+
18
+
19
+ @dataclass
20
+ class EventItem:
21
+ line: 'GameLine'
22
+ id: str
23
+ text: str
24
+ time: datetime.datetime
25
+ timestamp: float
26
+ checked: bool = False
27
+
28
+ def to_dict(self):
29
+ return {
30
+ 'id': self.id,
31
+ 'text': self.text,
32
+ 'time': self.time,
33
+ 'timestamp': self.timestamp,
34
+ 'checked': self.checked
35
+ }
36
+
37
+ class EventManager:
38
+ events: list[EventItem]
39
+ events_dict: dict[str, EventItem] = {}
40
+ line_for_audio: GameLine = None
41
+ line_for_screenshot: GameLine = None
42
+
43
+ def __init__(self):
44
+ self.events = []
45
+ self.events_dict = {}
46
+
47
+ def __iter__(self):
48
+ return iter(self.events)
49
+
50
+ def replace_events(self, new_events: list[EventItem]):
51
+ self.events = new_events
52
+
53
+ def add_gameline(self, line: GameLine):
54
+ new_event = EventItem(line, line.id, line.text, line.time, line.time.timestamp(), False)
55
+ self.events_dict[line.id] = new_event
56
+ self.events.append(new_event)
57
+
58
+ def get_events(self):
59
+ return self.events
60
+
61
+ def add_event(self, event):
62
+ self.events.append(event)
63
+
64
+ def get(self, event_id):
65
+ return self.events_dict.get(event_id)
66
+
67
+ event_manager = EventManager()
68
+
69
+ server_start_time = datetime.datetime.now().timestamp()
70
+
71
+ app = flask.Flask(__name__)
72
+
73
+ # Load data from the JSON file
74
+ def load_data_from_file():
75
+ if os.path.exists(TEXT_REPLACEMENTS_FILE):
76
+ with open(TEXT_REPLACEMENTS_FILE, 'r', encoding='utf-8') as file:
77
+ return json.load(file)
78
+ return {"enabled": True, "args": {"replacements": {}}}
79
+
80
+ # Save data to the JSON file
81
+ def save_data_to_file(data):
82
+ with open(TEXT_REPLACEMENTS_FILE, 'w', encoding='utf-8') as file:
83
+ json.dump(data, file, indent=4, ensure_ascii=False)
84
+
85
+ @app.route('/load-data', methods=['GET'])
86
+ def load_data():
87
+ try:
88
+ data = load_data_from_file()
89
+ return jsonify(data), 200
90
+ except Exception as e:
91
+ return jsonify({"error": f"Failed to load data: {str(e)}"}), 500
92
+
93
+ @app.route('/save-data', methods=['POST'])
94
+ def save_data():
95
+ try:
96
+ data = request.get_json()
97
+ if not isinstance(data, dict):
98
+ return jsonify({"error": "Invalid data format"}), 400
99
+
100
+ # Save updated data
101
+ save_data_to_file(data)
102
+ return jsonify({"message": "Data saved successfully"}), 200
103
+ except Exception as e:
104
+ return jsonify({"error": f"Failed to save data: {str(e)}"}), 500
105
+
106
+ def inject_server_start_time(html_content, timestamp):
107
+ placeholder = '<script>'
108
+ replacement = f'<script>const serverStartTime = {timestamp};'
109
+ return html_content.replace(placeholder, replacement)
110
+
111
+ @app.route('/favicon.ico')
112
+ def favicon():
113
+ return send_from_directory(os.path.join(app.root_path, 'static'),
114
+ 'favicon.ico', mimetype='image/vnd.microsoft.icon')
115
+
116
+ @app.route('/<path:filename>')
117
+ def serve_static(filename):
118
+ return send_from_directory('pages', filename)
119
+
120
+ @app.route('/')
121
+ def index():
122
+ with open(os.path.join(app.root_path, 'static', 'utility.html'), encoding='utf-8') as file:
123
+ return file.read()
124
+
125
+ @app.route('/texthooker')
126
+ def texthooker():
127
+ with open(os.path.join(app.root_path, 'static', 'utility.html'), encoding='utf-8') as file:
128
+ return file.read()
129
+
130
+ @app.route('/textreplacements')
131
+ def textreplacements():
132
+ with open(os.path.join(app.root_path, 'static', 'text_replacements.html'), encoding='utf-8') as file:
133
+ return file.read()
134
+
135
+ @app.route('/data', methods=['GET'])
136
+ def get_data():
137
+ return jsonify([event.to_dict() for event in event_manager])
138
+
139
+
140
+ def add_event_to_texthooker(line: GameLine):
141
+ logger.info("Adding event to web server: %s", line.text)
142
+ event_manager.add_gameline(line)
143
+
144
+
145
+ @app.route('/update', methods=['POST'])
146
+ def update_event():
147
+ data = request.get_json()
148
+ event_id = data.get('id')
149
+ checked = data.get('checked')
150
+
151
+ logger.info(event_id)
152
+ logger.info(checked)
153
+
154
+ if event_id is None or checked is None:
155
+ return jsonify({'error': 'Missing id or checked status'}), 400
156
+
157
+ logger.info(event_manager.get(event_id))
158
+
159
+ event_manager.get(event_id).checked = checked
160
+
161
+ return jsonify({'error': 'Event not found'}), 404
162
+
163
+ @app.route('/get-screenshot', methods=['Post'])
164
+ def get_screenshot():
165
+ """Endpoint to get a screenshot of the current game screen."""
166
+ data = request.get_json()
167
+ event_id = data.get('id')
168
+ if event_id is None:
169
+ return jsonify({'error': 'Missing id'}), 400
170
+ event_manager.line_for_screenshot = get_line_by_id(event_id)
171
+ obs.save_replay_buffer()
172
+ return jsonify({}), 200
173
+
174
+ @app.route('/play-audio', methods=['POST'])
175
+ def play_audio():
176
+ """Endpoint to play audio for a specific event."""
177
+ data = request.get_json()
178
+ event_id = data.get('id')
179
+ if event_id is None:
180
+ return jsonify({'error': 'Missing id'}), 400
181
+ event_manager.line_for_audio = get_line_by_id(event_id)
182
+ obs.save_replay_buffer()
183
+ return jsonify({}), 200
184
+
185
+
186
+ # @app.route('/store-events', methods=['POST'])
187
+ # def store_events():
188
+ # data = request.get_json()
189
+ # events_data = data.get('events', [])
190
+ #
191
+ # if not isinstance(events_data, list):
192
+ # return jsonify({'error': 'Invalid data format. Expected an array of events.'}), 400
193
+ #
194
+ # for event_data in events_data:
195
+ # if not all(k in event_data for k in ('id', 'text', 'time', 'checked')):
196
+ # return jsonify({'error': 'Invalid event structure. Missing keys.'}), 400
197
+ # if not (isinstance(event_data['id'], (int, float)) and
198
+ # isinstance(event_data['text'], str) and
199
+ # isinstance(event_data['time'], str) and
200
+ # isinstance(event_data['checked'], bool)):
201
+ # return jsonify({'error': 'Invalid event structure. Incorrect data types.'}), 400
202
+ #
203
+ # event_manager.replace_events([EventItem(item['id'], item['text'], item['time'], item.get(['timestamp'], 0), item['checked']) for item in data])
204
+ #
205
+ # return jsonify({'message': 'Events successfully stored on server.', 'receivedEvents': data}), 200
206
+
207
+
208
+ def get_selected_lines():
209
+ return [item.line for item in event_manager if item.checked]
210
+
211
+ def are_lines_selected():
212
+ return any(item.checked for item in event_manager)
213
+
214
+ def reset_checked_lines():
215
+ for item in event_manager:
216
+ item.checked = False
217
+
218
+ def open_texthooker():
219
+ webbrowser.open(url + '/texthooker')
220
+
221
+ def start_web_server():
222
+ logger.debug("Starting web server...")
223
+ import logging
224
+ log = logging.getLogger('werkzeug')
225
+ log.setLevel(logging.ERROR) # Set to ERROR to suppress most logs
226
+
227
+ # Open the default browser
228
+ if get_config().general.open_multimine_on_startup:
229
+ open_texthooker()
230
+
231
+ app.run(port=port, debug=False) # debug=True provides helpful error messages during development
232
+
233
+ if __name__ == '__main__':
234
+ start_web_server()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.7.16
3
+ Version: 2.8.0
4
4
  Summary: A tool for mining sentences from games. Update: Multi-Line Mining! Fixed!
5
5
  Author-email: Beangate <bpwhelan95@gmail.com>
6
6
  License: MIT License
@@ -35,6 +35,7 @@ Requires-Dist: pystray
35
35
  Requires-Dist: pywin32; sys_platform == "win32"
36
36
  Requires-Dist: google-generativeai
37
37
  Requires-Dist: pygetwindow; sys_platform == "win32"
38
+ Requires-Dist: flask
38
39
  Dynamic: license-file
39
40
 
40
41
  # Game Sentence Miner
@@ -0,0 +1,58 @@
1
+ GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ GameSentenceMiner/anki.py,sha256=2Oy3a8Od8DC3vs1-ktS6KF1iUj74Ptd0SCe-U0cZcfc,14206
3
+ GameSentenceMiner/config_gui.py,sha256=8Wc5YdAyaYT09T4pvHiL5Q4NiKRaL19CS3A0JhnSu6k,68822
4
+ GameSentenceMiner/configuration.py,sha256=NQMsM23p4_keTFXEaKMTMacIx0TCdk4-TIl4usZkJy4,20801
5
+ GameSentenceMiner/electron_config.py,sha256=dGcPYCISPehXubYSzsDuI2Gl092MYK0u3bTnkL9Jh1Y,9787
6
+ GameSentenceMiner/ffmpeg.py,sha256=mcEcJnYl06oJGbLaymFUfqClFiHf6Hhf2SXo3UV9tvM,13378
7
+ GameSentenceMiner/gametext.py,sha256=pPJO-NYW15SqAG-IQ0ZHpPt8py4g0yL1Lx0UlR-en-4,5431
8
+ GameSentenceMiner/gsm.py,sha256=6BcCYNV9tGQ8FgDh6Y8bPbZ66KWP57oBUqIoVF_nnXg,24667
9
+ GameSentenceMiner/model.py,sha256=JdnkT4VoPOXmOpRgFdvERZ09c9wLN6tUJxdrKlGZcqo,5305
10
+ GameSentenceMiner/notification.py,sha256=FY39ChSRK0Y8TQ6lBGsLnpZUFPtFpSy2tweeXVoV7kc,2809
11
+ GameSentenceMiner/obs.py,sha256=DLTcK48Dqcg-5BYC2Fp-tPCO4dVI9FuVYbwIawECkwQ,8977
12
+ GameSentenceMiner/package.py,sha256=YlS6QRMuVlm6mdXx0rlXv9_3erTGS21jaP3PNNWfAH0,1250
13
+ GameSentenceMiner/text_log.py,sha256=tBuZ8ElUgPtiQV8U6U90kmRxposwIkL3fjOYejdzikc,5153
14
+ GameSentenceMiner/util.py,sha256=H4JxfwysfnfiEe0-jZGeYKEQWX1HN-LvIKAo-OZYaoE,8498
15
+ GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ GameSentenceMiner/ai/gemini.py,sha256=6kTQPuRH16D-1srhrWa5uPGIy-jBqNm9eRdKBSbpiXA,5423
17
+ GameSentenceMiner/communication/__init__.py,sha256=_jGn9PJxtOAOPtJ2rI-Qu9hEHVZVpIvWlxKvqk91_zI,638
18
+ GameSentenceMiner/communication/send.py,sha256=oOJdCS6-LNX90amkRn5FL2xqx6THGm56zHR2ntVIFTE,229
19
+ GameSentenceMiner/communication/websocket.py,sha256=pTcUe_ZZRp9REdSU4qalhPmbT_1DKa7w18j6RfFLELA,3074
20
+ GameSentenceMiner/downloader/Untitled_json.py,sha256=RUUl2bbbCpUDUUS0fP0tdvf5FngZ7ILdA_J5TFYAXUQ,15272
21
+ GameSentenceMiner/downloader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ GameSentenceMiner/downloader/download_tools.py,sha256=mI1u_FGBmBqDIpCH3jOv8DOoZ3obgP5pIf9o9SVfX2Q,8131
23
+ GameSentenceMiner/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=zagsB4UD9mmZX_r6dFBCXZqdDa0XGk-RvIqbKoPB9lQ,1932
25
+ GameSentenceMiner/ocr/ocrconfig.py,sha256=_tY8mjnzHMJrLS8E5pHqYXZjMuLoGKYgJwdhYgN-ny4,6466
26
+ GameSentenceMiner/ocr/oneocr_dl.py,sha256=o3ANp5IodEQoQ8GPcJdg9Y8JzA_lictwnebFPwwUZVk,10144
27
+ GameSentenceMiner/ocr/owocr_area_selector.py,sha256=a2i4kW9g9_DJKt6OJ5dlSiqJGiXkKyqAQaAiXBdrq8U,46680
28
+ GameSentenceMiner/ocr/owocr_helper.py,sha256=wZsWV0e8OfI752lezrJccci05MmXkxMN10QYLMY34uk,16010
29
+ GameSentenceMiner/owocr/owocr/__init__.py,sha256=opjBOyGGyEqZCE6YdZPnyt7nVfiwyELHsXA0jAsjm14,25
30
+ GameSentenceMiner/owocr/owocr/__main__.py,sha256=r8MI6RAmbkTWqOJ59uvXoDS7CSw5jX5war9ULGWELrA,128
31
+ GameSentenceMiner/owocr/owocr/config.py,sha256=738QCJHEWpFhMh966plOcXYWwcshSiRsxjjIwldeTtI,7461
32
+ GameSentenceMiner/owocr/owocr/lens_betterproto.py,sha256=oNoISsPilVVRBBPVDtb4-roJtAhp8ZAuFTci3TGXtMc,39141
33
+ GameSentenceMiner/owocr/owocr/ocr.py,sha256=n24Xg8Z8dbcgLpq1u4d22z3tLV1evmf0dK3-Xocv3vs,39878
34
+ GameSentenceMiner/owocr/owocr/run.py,sha256=_1zAbJooOR051tsFh0iCyjtlVP5BVE0KFEl7i95mUd0,47805
35
+ GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=fjJ3CSXLti3WboGPpmsa7MWOwIXsfpHC8N4zKahGGY0,3346
36
+ GameSentenceMiner/vad/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
+ GameSentenceMiner/vad/silero_trim.py,sha256=ULf3zwS-JMsY82cKF7gZxREHw8L6lgpWF2U1YqgE9Oc,1681
38
+ GameSentenceMiner/vad/vosk_helper.py,sha256=125X8C9NxFPlWWpoNsbOnEqKx8RCjXN109zNx_QXhyg,6070
39
+ GameSentenceMiner/vad/whisper_helper.py,sha256=JJ-iltCh813XdjyEw0Wn5DaErf6PDqfH0Efu1Md8cIY,3543
40
+ GameSentenceMiner/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
+ GameSentenceMiner/web/texthooking_page.py,sha256=rcxCw81jjgyWPslVTFhQ5gaGs3FVN1X2CF-nmcCjo78,7325
42
+ GameSentenceMiner/web/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
+ GameSentenceMiner/web/static/apple-touch-icon.png,sha256=OcMI8af_68DA_tweOsQ5LytTyMwm7-hPW07IfrOVgEs,46132
44
+ GameSentenceMiner/web/static/favicon-96x96.png,sha256=lOePzjiKl1JY2J1kT_PMdyEnrlJmi5GWbmXJunM12B4,16502
45
+ GameSentenceMiner/web/static/favicon.ico,sha256=7d25r_FBqRSNsAoEHpSzNoT7zyVt2DJRLNDNq_HYoX8,15086
46
+ GameSentenceMiner/web/static/favicon.svg,sha256=x305AP6WlXGtrXIZlaQspdLmwteoFYUoe5FyJ9MYlJ8,11517
47
+ GameSentenceMiner/web/static/site.webmanifest,sha256=kaeNT-FjFt-T7JGzOhXH7YSqsrDeiplZ2kDxCN_CFU4,436
48
+ GameSentenceMiner/web/static/style.css,sha256=bPZK0NVMuyRl5NNDuT7ZTzVLKlvSsdmeVHmAW4y5FM0,7001
49
+ GameSentenceMiner/web/static/text_replacements.html,sha256=LJ2qBEZvyMaBJLLJzDIw0baiTeusplbn4jkpyHM6ZBI,8613
50
+ GameSentenceMiner/web/static/utility.html,sha256=DlXaglC2ZkBfEKK1sxuS8Dc5Xj09Yqi27qQuEF0c59w,9812
51
+ GameSentenceMiner/web/static/web-app-manifest-192x192.png,sha256=EfSNnBmsSaLfESbkGfYwbKzcjKOdzuWo18ABADfN974,51117
52
+ GameSentenceMiner/web/static/web-app-manifest-512x512.png,sha256=wyqgCWCrLEUxSRXmaA3iJEESd-vM-ZmlTtZFBY4V8Pk,230819
53
+ gamesentenceminer-2.8.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
54
+ gamesentenceminer-2.8.0.dist-info/METADATA,sha256=bzYt80WzMvh_OIfxjdl8Kma1tD-uL9VjNJv2K7BOnS0,5912
55
+ gamesentenceminer-2.8.0.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
56
+ gamesentenceminer-2.8.0.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
57
+ gamesentenceminer-2.8.0.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
58
+ gamesentenceminer-2.8.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.0)
2
+ Generator: setuptools (79.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5