GameSentenceMiner 2.8.26__py3-none-any.whl → 2.8.27__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/anki.py +7 -3
- GameSentenceMiner/ffmpeg.py +3 -3
- GameSentenceMiner/gsm.py +20 -17
- GameSentenceMiner/ocr/owocr_helper.py +1 -1
- GameSentenceMiner/owocr/owocr/config.py +25 -13
- GameSentenceMiner/owocr/owocr/ocr.py +103 -95
- GameSentenceMiner/owocr/owocr/run.py +602 -598
- GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py +3 -2
- GameSentenceMiner/vad/result.py +8 -0
- GameSentenceMiner/vad/silero_trim.py +3 -2
- GameSentenceMiner/vad/vosk_helper.py +3 -2
- GameSentenceMiner/vad/whisper_helper.py +3 -2
- {gamesentenceminer-2.8.26.dist-info → gamesentenceminer-2.8.27.dist-info}/METADATA +1 -1
- {gamesentenceminer-2.8.26.dist-info → gamesentenceminer-2.8.27.dist-info}/RECORD +18 -17
- {gamesentenceminer-2.8.26.dist-info → gamesentenceminer-2.8.27.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.8.26.dist-info → gamesentenceminer-2.8.27.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.8.26.dist-info → gamesentenceminer-2.8.27.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.8.26.dist-info → gamesentenceminer-2.8.27.dist-info}/top_level.txt +0 -0
@@ -1,10 +1,3 @@
|
|
1
|
-
try:
|
2
|
-
from .secret import *
|
3
|
-
except ImportError:
|
4
|
-
pass
|
5
|
-
from .config import Config
|
6
|
-
from .screen_coordinate_picker import get_screen_selection
|
7
|
-
|
8
1
|
try:
|
9
2
|
import win32gui
|
10
3
|
import win32ui
|
@@ -22,48 +15,117 @@ try:
|
|
22
15
|
import platform
|
23
16
|
from AppKit import NSData, NSImage, NSBitmapImageRep, NSDeviceRGBColorSpace, NSGraphicsContext, NSZeroPoint, NSZeroRect, NSCompositingOperationCopy
|
24
17
|
from Quartz import CGWindowListCreateImageFromArray, kCGWindowImageBoundsIgnoreFraming, CGRectMake, CGRectNull, CGMainDisplayID, CGWindowListCopyWindowInfo, \
|
25
|
-
|
26
|
-
|
18
|
+
CGWindowListCreateDescriptionFromArray, kCGWindowListOptionOnScreenOnly, kCGWindowListExcludeDesktopElements, kCGWindowName, kCGNullWindowID, \
|
19
|
+
CGImageGetWidth, CGImageGetHeight, CGDataProviderCopyData, CGImageGetDataProvider, CGImageGetBytesPerRow
|
27
20
|
from ScreenCaptureKit import SCContentFilter, SCScreenshotManager, SCShareableContent, SCStreamConfiguration, SCCaptureResolutionBest
|
28
21
|
except ImportError:
|
29
22
|
pass
|
30
23
|
|
31
|
-
|
24
|
+
import signal
|
32
25
|
import threading
|
26
|
+
from pathlib import Path
|
27
|
+
import queue
|
28
|
+
import io
|
29
|
+
import re
|
30
|
+
import logging
|
31
|
+
import inspect
|
32
|
+
|
33
33
|
import pyperclipfix
|
34
34
|
import mss
|
35
35
|
import asyncio
|
36
36
|
import websockets
|
37
37
|
import socketserver
|
38
38
|
import queue
|
39
|
-
import time
|
40
39
|
|
41
40
|
from datetime import datetime
|
42
|
-
from PIL import Image, ImageDraw
|
43
|
-
from
|
41
|
+
from PIL import Image, ImageDraw, UnidentifiedImageError
|
42
|
+
from loguru import logger
|
44
43
|
from pynput import keyboard
|
45
44
|
from desktop_notifier import DesktopNotifierSync
|
46
45
|
import psutil
|
47
46
|
|
48
47
|
import inspect
|
49
48
|
from .ocr import *
|
49
|
+
try:
|
50
|
+
from .secret import *
|
51
|
+
except ImportError:
|
52
|
+
pass
|
53
|
+
from .config import Config
|
54
|
+
from .screen_coordinate_picker import get_screen_selection
|
55
|
+
from ...configuration import get_temporary_directory
|
50
56
|
|
51
57
|
|
52
58
|
config = None
|
53
59
|
|
54
60
|
|
55
|
-
class
|
61
|
+
class ClipboardThread(threading.Thread):
|
56
62
|
def __init__(self):
|
57
63
|
super().__init__(daemon=True)
|
64
|
+
self.ignore_flag = config.get_general('ignore_flag')
|
65
|
+
self.delay_secs = config.get_general('delay_secs')
|
58
66
|
self.last_update = time.time()
|
59
67
|
|
68
|
+
def are_images_identical(self, img1, img2):
|
69
|
+
if None in (img1, img2):
|
70
|
+
return img1 == img2
|
71
|
+
|
72
|
+
img1 = np.array(img1)
|
73
|
+
img2 = np.array(img2)
|
74
|
+
|
75
|
+
return (img1.shape == img2.shape) and (img1 == img2).all()
|
76
|
+
|
77
|
+
def normalize_macos_clipboard(self, img):
|
78
|
+
ns_data = NSData.dataWithBytes_length_(img, len(img))
|
79
|
+
ns_image = NSImage.alloc().initWithData_(ns_data)
|
80
|
+
|
81
|
+
new_image = NSBitmapImageRep.alloc().initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel_(
|
82
|
+
None, # Set to None to create a new bitmap
|
83
|
+
int(ns_image.size().width),
|
84
|
+
int(ns_image.size().height),
|
85
|
+
8, # Bits per sample
|
86
|
+
4, # Samples per pixel (R, G, B, A)
|
87
|
+
True, # Has alpha
|
88
|
+
False, # Is not planar
|
89
|
+
NSDeviceRGBColorSpace,
|
90
|
+
0, # Automatically compute bytes per row
|
91
|
+
32 # Bits per pixel (8 bits per sample * 4 samples per pixel)
|
92
|
+
)
|
93
|
+
|
94
|
+
context = NSGraphicsContext.graphicsContextWithBitmapImageRep_(new_image)
|
95
|
+
NSGraphicsContext.setCurrentContext_(context)
|
96
|
+
|
97
|
+
ns_image.drawAtPoint_fromRect_operation_fraction_(
|
98
|
+
NSZeroPoint,
|
99
|
+
NSZeroRect,
|
100
|
+
NSCompositingOperationCopy,
|
101
|
+
1.0
|
102
|
+
)
|
103
|
+
|
104
|
+
return bytes(new_image.TIFFRepresentation())
|
105
|
+
|
60
106
|
def process_message(self, hwnd: int, msg: int, wparam: int, lparam: int):
|
61
107
|
WM_CLIPBOARDUPDATE = 0x031D
|
62
108
|
timestamp = time.time()
|
63
109
|
if msg == WM_CLIPBOARDUPDATE and timestamp - self.last_update > 1 and not paused:
|
64
|
-
|
65
|
-
|
66
|
-
|
110
|
+
self.last_update = timestamp
|
111
|
+
while True:
|
112
|
+
try:
|
113
|
+
win32clipboard.OpenClipboard()
|
114
|
+
break
|
115
|
+
except pywintypes.error:
|
116
|
+
pass
|
117
|
+
time.sleep(0.1)
|
118
|
+
try:
|
119
|
+
if win32clipboard.IsClipboardFormatAvailable(win32con.CF_BITMAP) and win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_DIB):
|
120
|
+
clipboard_text = ''
|
121
|
+
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_UNICODETEXT):
|
122
|
+
clipboard_text = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
|
123
|
+
if self.ignore_flag or clipboard_text != '*ocr_ignore*':
|
124
|
+
img = win32clipboard.GetClipboardData(win32clipboard.CF_DIB)
|
125
|
+
image_queue.put((img, False))
|
126
|
+
win32clipboard.CloseClipboard()
|
127
|
+
except pywintypes.error:
|
128
|
+
pass
|
67
129
|
return 0
|
68
130
|
|
69
131
|
def create_window(self):
|
@@ -76,10 +138,93 @@ class WindowsClipboardThread(threading.Thread):
|
|
76
138
|
return win32gui.CreateWindow(class_atom, className, 0, 0, 0, 0, 0, 0, 0, wc.hInstance, None)
|
77
139
|
|
78
140
|
def run(self):
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
141
|
+
if sys.platform == 'win32':
|
142
|
+
hwnd = self.create_window()
|
143
|
+
self.thread_id = win32api.GetCurrentThreadId()
|
144
|
+
ctypes.windll.user32.AddClipboardFormatListener(hwnd)
|
145
|
+
win32gui.PumpMessages()
|
146
|
+
else:
|
147
|
+
is_macos = sys.platform == 'darwin'
|
148
|
+
if is_macos:
|
149
|
+
from AppKit import NSPasteboard, NSPasteboardTypeTIFF, NSPasteboardTypeString
|
150
|
+
pasteboard = NSPasteboard.generalPasteboard()
|
151
|
+
count = pasteboard.changeCount()
|
152
|
+
else:
|
153
|
+
from PIL import ImageGrab
|
154
|
+
process_clipboard = False
|
155
|
+
img = None
|
156
|
+
|
157
|
+
while not terminated:
|
158
|
+
if paused:
|
159
|
+
sleep_time = 0.5
|
160
|
+
process_clipboard = False
|
161
|
+
else:
|
162
|
+
sleep_time = self.delay_secs
|
163
|
+
if is_macos:
|
164
|
+
with objc.autorelease_pool():
|
165
|
+
old_count = count
|
166
|
+
count = pasteboard.changeCount()
|
167
|
+
if process_clipboard and count != old_count:
|
168
|
+
while len(pasteboard.types()) == 0:
|
169
|
+
time.sleep(0.1)
|
170
|
+
if NSPasteboardTypeTIFF in pasteboard.types():
|
171
|
+
clipboard_text = ''
|
172
|
+
if NSPasteboardTypeString in pasteboard.types():
|
173
|
+
clipboard_text = pasteboard.stringForType_(NSPasteboardTypeString)
|
174
|
+
if self.ignore_flag or clipboard_text != '*ocr_ignore*':
|
175
|
+
img = self.normalize_macos_clipboard(pasteboard.dataForType_(NSPasteboardTypeTIFF))
|
176
|
+
image_queue.put((img, False))
|
177
|
+
else:
|
178
|
+
old_img = img
|
179
|
+
try:
|
180
|
+
img = ImageGrab.grabclipboard()
|
181
|
+
except Exception:
|
182
|
+
pass
|
183
|
+
else:
|
184
|
+
if (process_clipboard and isinstance(img, Image.Image) and \
|
185
|
+
(self.ignore_flag or pyperclipfix.paste() != '*ocr_ignore*') and \
|
186
|
+
(not self.are_images_identical(img, old_img))):
|
187
|
+
image_queue.put((img, False))
|
188
|
+
|
189
|
+
process_clipboard = True
|
190
|
+
|
191
|
+
if not terminated:
|
192
|
+
time.sleep(sleep_time)
|
193
|
+
|
194
|
+
|
195
|
+
class DirectoryWatcher(threading.Thread):
|
196
|
+
def __init__(self, path):
|
197
|
+
super().__init__(daemon=True)
|
198
|
+
self.path = path
|
199
|
+
self.delay_secs = config.get_general('delay_secs')
|
200
|
+
self.last_update = time.time()
|
201
|
+
self.allowed_extensions = ('.png', '.jpg', '.jpeg', '.bmp', '.gif', '.webp')
|
202
|
+
|
203
|
+
def get_path_key(self, path):
|
204
|
+
return path, path.lstat().st_mtime
|
205
|
+
|
206
|
+
def run(self):
|
207
|
+
old_paths = set()
|
208
|
+
for path in self.path.iterdir():
|
209
|
+
if path.suffix.lower() in self.allowed_extensions:
|
210
|
+
old_paths.add(get_path_key(path))
|
211
|
+
|
212
|
+
while not terminated:
|
213
|
+
if paused:
|
214
|
+
sleep_time = 0.5
|
215
|
+
else:
|
216
|
+
sleep_time = self.delay_secs
|
217
|
+
for path in self.path.iterdir():
|
218
|
+
if path.suffix.lower() in self.allowed_extensions:
|
219
|
+
path_key = self.get_path_key(path)
|
220
|
+
if path_key not in old_paths:
|
221
|
+
old_paths.add(path_key)
|
222
|
+
|
223
|
+
if not paused:
|
224
|
+
image_queue.put((path, False))
|
225
|
+
|
226
|
+
if not terminated:
|
227
|
+
time.sleep(sleep_time)
|
83
228
|
|
84
229
|
|
85
230
|
class WebsocketServerThread(threading.Thread):
|
@@ -104,7 +249,7 @@ class WebsocketServerThread(threading.Thread):
|
|
104
249
|
try:
|
105
250
|
async for message in websocket:
|
106
251
|
if self.read and not paused:
|
107
|
-
|
252
|
+
image_queue.put((message, False))
|
108
253
|
try:
|
109
254
|
await websocket.send('True')
|
110
255
|
except websockets.exceptions.ConnectionClosedOK:
|
@@ -153,145 +298,12 @@ class RequestHandler(socketserver.BaseRequestHandler):
|
|
153
298
|
pass
|
154
299
|
|
155
300
|
if not paused:
|
156
|
-
|
301
|
+
image_queue.put((img, False))
|
157
302
|
conn.sendall(b'True')
|
158
303
|
else:
|
159
304
|
conn.sendall(b'False')
|
160
305
|
|
161
306
|
|
162
|
-
class MacOSWindowTracker(threading.Thread):
|
163
|
-
def __init__(self, window_id):
|
164
|
-
super().__init__(daemon=True)
|
165
|
-
self.stop = False
|
166
|
-
self.window_id = window_id
|
167
|
-
self.window_active = False
|
168
|
-
|
169
|
-
def run(self):
|
170
|
-
found = True
|
171
|
-
while found and not self.stop:
|
172
|
-
found = False
|
173
|
-
is_active = False
|
174
|
-
with objc.autorelease_pool():
|
175
|
-
window_list = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID)
|
176
|
-
for i, window in enumerate(window_list):
|
177
|
-
if found and window.get(kCGWindowName, '') == 'Fullscreen Backdrop':
|
178
|
-
is_active = True
|
179
|
-
break
|
180
|
-
if self.window_id == window['kCGWindowNumber']:
|
181
|
-
found = True
|
182
|
-
if i == 0 or window_list[i-1].get(kCGWindowName, '') in ('Dock', 'Color Enforcer Window'):
|
183
|
-
is_active = True
|
184
|
-
break
|
185
|
-
if not found:
|
186
|
-
window_list = CGWindowListCreateDescriptionFromArray([self.window_id])
|
187
|
-
if len(window_list) > 0:
|
188
|
-
found = True
|
189
|
-
if found and self.window_active != is_active:
|
190
|
-
on_window_activated(is_active)
|
191
|
-
self.window_active = is_active
|
192
|
-
time.sleep(0.2)
|
193
|
-
if not found:
|
194
|
-
on_window_closed(False)
|
195
|
-
|
196
|
-
|
197
|
-
class WindowsWindowTracker(threading.Thread):
|
198
|
-
def __init__(self, window_handle, only_active):
|
199
|
-
super().__init__(daemon=True)
|
200
|
-
self.stop = False
|
201
|
-
self.window_handle = window_handle
|
202
|
-
self.only_active = only_active
|
203
|
-
self.window_active = False
|
204
|
-
self.window_minimized = False
|
205
|
-
|
206
|
-
def run(self):
|
207
|
-
found = True
|
208
|
-
while not self.stop:
|
209
|
-
found = win32gui.IsWindow(self.window_handle)
|
210
|
-
if not found:
|
211
|
-
break
|
212
|
-
if self.only_active:
|
213
|
-
is_active = self.window_handle == win32gui.GetForegroundWindow()
|
214
|
-
if self.window_active != is_active:
|
215
|
-
on_window_activated(is_active)
|
216
|
-
self.window_active = is_active
|
217
|
-
else:
|
218
|
-
is_minimized = win32gui.IsIconic(self.window_handle)
|
219
|
-
if self.window_minimized != is_minimized:
|
220
|
-
on_window_minimized(is_minimized)
|
221
|
-
self.window_minimized = is_minimized
|
222
|
-
time.sleep(0.2)
|
223
|
-
if not found:
|
224
|
-
on_window_closed(False)
|
225
|
-
|
226
|
-
|
227
|
-
def capture_macos_window_screenshot(window_id):
|
228
|
-
def shareable_content_completion_handler(shareable_content, error):
|
229
|
-
if error:
|
230
|
-
screencapturekit_queue.put(None)
|
231
|
-
return
|
232
|
-
|
233
|
-
target_window = None
|
234
|
-
for window in shareable_content.windows():
|
235
|
-
if window.windowID() == window_id:
|
236
|
-
target_window = window
|
237
|
-
break
|
238
|
-
|
239
|
-
if not target_window:
|
240
|
-
screencapturekit_queue.put(None)
|
241
|
-
return
|
242
|
-
|
243
|
-
with objc.autorelease_pool():
|
244
|
-
content_filter = SCContentFilter.alloc().initWithDesktopIndependentWindow_(target_window)
|
245
|
-
|
246
|
-
frame = content_filter.contentRect()
|
247
|
-
scale = content_filter.pointPixelScale()
|
248
|
-
width = frame.size.width * scale
|
249
|
-
height = frame.size.height * scale
|
250
|
-
configuration = SCStreamConfiguration.alloc().init()
|
251
|
-
configuration.setSourceRect_(CGRectMake(0, 0, frame.size.width, frame.size.height))
|
252
|
-
configuration.setWidth_(width)
|
253
|
-
configuration.setHeight_(height)
|
254
|
-
configuration.setShowsCursor_(False)
|
255
|
-
configuration.setCaptureResolution_(SCCaptureResolutionBest)
|
256
|
-
configuration.setIgnoreGlobalClipSingleWindow_(True)
|
257
|
-
|
258
|
-
SCScreenshotManager.captureImageWithFilter_configuration_completionHandler_(
|
259
|
-
content_filter, configuration, capture_image_completion_handler
|
260
|
-
)
|
261
|
-
|
262
|
-
def capture_image_completion_handler(image, error):
|
263
|
-
if error:
|
264
|
-
screencapturekit_queue.put(None)
|
265
|
-
return
|
266
|
-
|
267
|
-
screencapturekit_queue.put(image)
|
268
|
-
|
269
|
-
SCShareableContent.getShareableContentWithCompletionHandler_(
|
270
|
-
shareable_content_completion_handler
|
271
|
-
)
|
272
|
-
|
273
|
-
|
274
|
-
def get_windows_window_handle(window_title):
|
275
|
-
def callback(hwnd, window_title_part):
|
276
|
-
window_title = win32gui.GetWindowText(hwnd)
|
277
|
-
if window_title_part in window_title:
|
278
|
-
handles.append((hwnd, window_title))
|
279
|
-
return True
|
280
|
-
|
281
|
-
handle = win32gui.FindWindow(None, window_title)
|
282
|
-
if handle:
|
283
|
-
return (handle, window_title)
|
284
|
-
|
285
|
-
handles = []
|
286
|
-
win32gui.EnumWindows(callback, window_title)
|
287
|
-
for handle in handles:
|
288
|
-
_, pid = win32process.GetWindowThreadProcessId(handle[0])
|
289
|
-
if psutil.Process(pid).name().lower() not in ('cmd.exe', 'powershell.exe', 'windowsterminal.exe'):
|
290
|
-
return handle
|
291
|
-
|
292
|
-
return (None, None)
|
293
|
-
|
294
|
-
|
295
307
|
class TextFiltering:
|
296
308
|
accurate_filtering = False
|
297
309
|
|
@@ -315,6 +327,7 @@ class TextFiltering:
|
|
315
327
|
try:
|
316
328
|
from transformers import pipeline, AutoTokenizer
|
317
329
|
import torch
|
330
|
+
logging.getLogger('transformers').setLevel(logging.ERROR)
|
318
331
|
|
319
332
|
model_ckpt = 'papluca/xlm-roberta-base-language-detection'
|
320
333
|
tokenizer = AutoTokenizer.from_pretrained(
|
@@ -386,19 +399,330 @@ class TextFiltering:
|
|
386
399
|
break
|
387
400
|
else:
|
388
401
|
for block in new_blocks:
|
389
|
-
if self.classify(block)[0] == lang:
|
402
|
+
if lang not in ["ja", "zh"] or self.classify(block)[0] == lang:
|
390
403
|
final_blocks.append(block)
|
391
404
|
|
392
405
|
text = '\n'.join(final_blocks)
|
393
406
|
return text, orig_text_filtered
|
394
407
|
|
395
408
|
|
409
|
+
class ScreenshotClass:
|
410
|
+
def __init__(self, screen_capture_area, screen_capture_window, screen_capture_exclusions, screen_capture_only_active_windows):
|
411
|
+
self.macos_window_tracker_instance = None
|
412
|
+
self.windows_window_tracker_instance = None
|
413
|
+
self.screencapture_window_active = True
|
414
|
+
self.screencapture_window_visible = True
|
415
|
+
self.custom_left = None
|
416
|
+
self.screen_capture_exclusions = screen_capture_exclusions
|
417
|
+
self.screen_capture_window = screen_capture_window
|
418
|
+
if screen_capture_area == '':
|
419
|
+
self.screencapture_mode = 0
|
420
|
+
elif screen_capture_area.startswith('screen_'):
|
421
|
+
parts = screen_capture_area.split('_')
|
422
|
+
if len(parts) != 2 or not parts[1].isdigit():
|
423
|
+
raise ValueError('Invalid screen_capture_area')
|
424
|
+
screen_capture_monitor = int(parts[1])
|
425
|
+
self.screencapture_mode = 1
|
426
|
+
elif len(screen_capture_area.split(',')) == 4:
|
427
|
+
self.screencapture_mode = 3
|
428
|
+
else:
|
429
|
+
self.screencapture_mode = 2
|
430
|
+
self.screen_capture_window = screen_capture_area
|
431
|
+
if self.screen_capture_window:
|
432
|
+
self.screencapture_mode = 2
|
433
|
+
|
434
|
+
if self.screencapture_mode != 2:
|
435
|
+
self.sct = mss.mss()
|
436
|
+
|
437
|
+
if self.screencapture_mode == 1:
|
438
|
+
mon = self.sct.monitors
|
439
|
+
if len(mon) <= screen_capture_monitor:
|
440
|
+
raise ValueError('Invalid monitor number in screen_capture_area')
|
441
|
+
coord_left = mon[screen_capture_monitor]['left']
|
442
|
+
coord_top = mon[screen_capture_monitor]['top']
|
443
|
+
coord_width = mon[screen_capture_monitor]['width']
|
444
|
+
coord_height = mon[screen_capture_monitor]['height']
|
445
|
+
elif self.screencapture_mode == 3:
|
446
|
+
coord_left, coord_top, coord_width, coord_height = [int(c.strip()) for c in screen_capture_area.split(',')]
|
447
|
+
else:
|
448
|
+
logger.opt(ansi=True).info('Launching screen coordinate picker')
|
449
|
+
screen_selection = get_screen_selection()
|
450
|
+
if not screen_selection:
|
451
|
+
raise ValueError('Picker window was closed or an error occurred')
|
452
|
+
screen_capture_monitor = screen_selection['monitor']
|
453
|
+
x, y, coord_width, coord_height = screen_selection['coordinates']
|
454
|
+
if coord_width > 0 and coord_height > 0:
|
455
|
+
coord_top = screen_capture_monitor['top'] + y
|
456
|
+
coord_left = screen_capture_monitor['left'] + x
|
457
|
+
else:
|
458
|
+
logger.opt(ansi=True).info('Selection is empty, selecting whole screen')
|
459
|
+
coord_left = screen_capture_monitor['left']
|
460
|
+
coord_top = screen_capture_monitor['top']
|
461
|
+
coord_width = screen_capture_monitor['width']
|
462
|
+
coord_height = screen_capture_monitor['height']
|
463
|
+
|
464
|
+
self.sct_params = {'top': coord_top, 'left': coord_left, 'width': coord_width, 'height': coord_height}
|
465
|
+
logger.opt(ansi=True).info(f'Selected coordinates: {coord_left},{coord_top},{coord_width},{coord_height}')
|
466
|
+
if len(screen_capture_area.split(',')) == 4:
|
467
|
+
self.custom_left, self.custom_top, self.custom_width, self.custom_height = [int(c.strip()) for c in
|
468
|
+
screen_capture_area.split(',')]
|
469
|
+
|
470
|
+
|
471
|
+
if self.screencapture_mode == 2 or self.screen_capture_window:
|
472
|
+
self.screen_capture_only_active_windows = screen_capture_only_active_windows
|
473
|
+
area_invalid_error = '"screen_capture_area" must be empty, "screen_N" where N is a screen number starting from 1, a valid set of coordinates, or a valid window name'
|
474
|
+
if sys.platform == 'darwin':
|
475
|
+
if config.get_general('screen_capture_old_macos_api') or int(platform.mac_ver()[0].split('.')[0]) < 14:
|
476
|
+
self.old_macos_screenshot_api = True
|
477
|
+
else:
|
478
|
+
self.old_macos_screenshot_api = False
|
479
|
+
self.screencapturekit_queue = queue.Queue()
|
480
|
+
CGMainDisplayID()
|
481
|
+
window_list = CGWindowListCopyWindowInfo(kCGWindowListExcludeDesktopElements, kCGNullWindowID)
|
482
|
+
window_titles = []
|
483
|
+
window_ids = []
|
484
|
+
window_index = None
|
485
|
+
for i, window in enumerate(window_list):
|
486
|
+
window_title = window.get(kCGWindowName, '')
|
487
|
+
if psutil.Process(window['kCGWindowOwnerPID']).name() not in ('Terminal', 'iTerm2'):
|
488
|
+
window_titles.append(window_title)
|
489
|
+
window_ids.append(window['kCGWindowNumber'])
|
490
|
+
|
491
|
+
if screen_capture_window in window_titles:
|
492
|
+
window_index = window_titles.index(screen_capture_window)
|
493
|
+
else:
|
494
|
+
for t in window_titles:
|
495
|
+
if screen_capture_window in t:
|
496
|
+
window_index = window_titles.index(t)
|
497
|
+
break
|
498
|
+
|
499
|
+
if not window_index:
|
500
|
+
raise ValueError(area_invalid_error)
|
501
|
+
|
502
|
+
self.window_id = window_ids[window_index]
|
503
|
+
window_title = window_titles[window_index]
|
504
|
+
|
505
|
+
if self.screen_capture_only_active_windows:
|
506
|
+
self.macos_window_tracker_instance = threading.Thread(target=self.macos_window_tracker)
|
507
|
+
self.macos_window_tracker_instance.start()
|
508
|
+
logger.opt(ansi=True).info(f'Selected window: {window_title}')
|
509
|
+
elif sys.platform == 'win32':
|
510
|
+
self.window_handle, window_title = self.get_windows_window_handle(screen_capture_window)
|
511
|
+
|
512
|
+
if not self.window_handle:
|
513
|
+
raise ValueError(area_invalid_error)
|
514
|
+
|
515
|
+
ctypes.windll.shcore.SetProcessDpiAwareness(1)
|
516
|
+
|
517
|
+
self.windows_window_tracker_instance = threading.Thread(target=self.windows_window_tracker)
|
518
|
+
self.windows_window_tracker_instance.start()
|
519
|
+
logger.opt(ansi=True).info(f'Selected window: {window_title}')
|
520
|
+
else:
|
521
|
+
raise ValueError('Window capture is only currently supported on Windows and macOS')
|
522
|
+
|
523
|
+
def __del__(self):
|
524
|
+
if self.macos_window_tracker_instance:
|
525
|
+
self.macos_window_tracker_instance.join()
|
526
|
+
elif self.windows_window_tracker_instance:
|
527
|
+
self.windows_window_tracker_instance.join()
|
528
|
+
|
529
|
+
def get_windows_window_handle(self, window_title):
|
530
|
+
def callback(hwnd, window_title_part):
|
531
|
+
window_title = win32gui.GetWindowText(hwnd)
|
532
|
+
if window_title_part in window_title:
|
533
|
+
handles.append((hwnd, window_title))
|
534
|
+
return True
|
535
|
+
|
536
|
+
handle = win32gui.FindWindow(None, window_title)
|
537
|
+
if handle:
|
538
|
+
return (handle, window_title)
|
539
|
+
|
540
|
+
handles = []
|
541
|
+
win32gui.EnumWindows(callback, window_title)
|
542
|
+
for handle in handles:
|
543
|
+
_, pid = win32process.GetWindowThreadProcessId(handle[0])
|
544
|
+
if psutil.Process(pid).name().lower() not in ('cmd.exe', 'powershell.exe', 'windowsterminal.exe'):
|
545
|
+
return handle
|
546
|
+
|
547
|
+
return (None, None)
|
548
|
+
|
549
|
+
def windows_window_tracker(self):
|
550
|
+
found = True
|
551
|
+
while not terminated:
|
552
|
+
found = win32gui.IsWindow(self.window_handle)
|
553
|
+
if not found:
|
554
|
+
break
|
555
|
+
if self.screen_capture_only_active_windows:
|
556
|
+
self.screencapture_window_active = self.window_handle == win32gui.GetForegroundWindow()
|
557
|
+
else:
|
558
|
+
self.screencapture_window_visible = not win32gui.IsIconic(self.window_handle)
|
559
|
+
time.sleep(0.2)
|
560
|
+
if not found:
|
561
|
+
on_window_closed(False)
|
562
|
+
|
563
|
+
def capture_macos_window_screenshot(self, window_id):
|
564
|
+
def shareable_content_completion_handler(shareable_content, error):
|
565
|
+
if error:
|
566
|
+
self.screencapturekit_queue.put(None)
|
567
|
+
return
|
568
|
+
|
569
|
+
target_window = None
|
570
|
+
for window in shareable_content.windows():
|
571
|
+
if window.windowID() == window_id:
|
572
|
+
target_window = window
|
573
|
+
break
|
574
|
+
|
575
|
+
if not target_window:
|
576
|
+
self.screencapturekit_queue.put(None)
|
577
|
+
return
|
578
|
+
|
579
|
+
with objc.autorelease_pool():
|
580
|
+
content_filter = SCContentFilter.alloc().initWithDesktopIndependentWindow_(target_window)
|
581
|
+
|
582
|
+
frame = content_filter.contentRect()
|
583
|
+
scale = content_filter.pointPixelScale()
|
584
|
+
width = frame.size.width * scale
|
585
|
+
height = frame.size.height * scale
|
586
|
+
configuration = SCStreamConfiguration.alloc().init()
|
587
|
+
configuration.setSourceRect_(CGRectMake(0, 0, frame.size.width, frame.size.height))
|
588
|
+
configuration.setWidth_(width)
|
589
|
+
configuration.setHeight_(height)
|
590
|
+
configuration.setShowsCursor_(False)
|
591
|
+
configuration.setCaptureResolution_(SCCaptureResolutionBest)
|
592
|
+
configuration.setIgnoreGlobalClipSingleWindow_(True)
|
593
|
+
|
594
|
+
SCScreenshotManager.captureImageWithFilter_configuration_completionHandler_(
|
595
|
+
content_filter, configuration, capture_image_completion_handler
|
596
|
+
)
|
597
|
+
|
598
|
+
def capture_image_completion_handler(image, error):
|
599
|
+
if error:
|
600
|
+
self.screencapturekit_queue.put(None)
|
601
|
+
return
|
602
|
+
|
603
|
+
self.screencapturekit_queue.put(image)
|
604
|
+
|
605
|
+
SCShareableContent.getShareableContentWithCompletionHandler_(
|
606
|
+
shareable_content_completion_handler
|
607
|
+
)
|
608
|
+
|
609
|
+
def macos_window_tracker(self):
|
610
|
+
found = True
|
611
|
+
while found and not terminated:
|
612
|
+
found = False
|
613
|
+
is_active = False
|
614
|
+
with objc.autorelease_pool():
|
615
|
+
window_list = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID)
|
616
|
+
for i, window in enumerate(window_list):
|
617
|
+
if found and window.get(kCGWindowName, '') == 'Fullscreen Backdrop':
|
618
|
+
is_active = True
|
619
|
+
break
|
620
|
+
if self.window_id == window['kCGWindowNumber']:
|
621
|
+
found = True
|
622
|
+
if i == 0 or window_list[i-1].get(kCGWindowName, '') in ('Dock', 'Color Enforcer Window'):
|
623
|
+
is_active = True
|
624
|
+
break
|
625
|
+
if not found:
|
626
|
+
window_list = CGWindowListCreateDescriptionFromArray([self.window_id])
|
627
|
+
if len(window_list) > 0:
|
628
|
+
found = True
|
629
|
+
if found:
|
630
|
+
self.screencapture_window_active = is_active
|
631
|
+
time.sleep(0.2)
|
632
|
+
if not found:
|
633
|
+
on_window_closed(False)
|
634
|
+
|
635
|
+
def __call__(self):
|
636
|
+
if self.screencapture_mode == 2 or self.screen_capture_window:
|
637
|
+
if sys.platform == 'darwin':
|
638
|
+
with objc.autorelease_pool():
|
639
|
+
if self.old_macos_screenshot_api:
|
640
|
+
cg_image = CGWindowListCreateImageFromArray(CGRectNull, [self.window_id], kCGWindowImageBoundsIgnoreFraming)
|
641
|
+
else:
|
642
|
+
self.capture_macos_window_screenshot(self.window_id)
|
643
|
+
try:
|
644
|
+
cg_image = self.screencapturekit_queue.get(timeout=0.5)
|
645
|
+
except queue.Empty:
|
646
|
+
cg_image = None
|
647
|
+
if not cg_image:
|
648
|
+
return 0
|
649
|
+
width = CGImageGetWidth(cg_image)
|
650
|
+
height = CGImageGetHeight(cg_image)
|
651
|
+
raw_data = CGDataProviderCopyData(CGImageGetDataProvider(cg_image))
|
652
|
+
bpr = CGImageGetBytesPerRow(cg_image)
|
653
|
+
img = Image.frombuffer('RGBA', (width, height), raw_data, 'raw', 'BGRA', bpr, 1)
|
654
|
+
else:
|
655
|
+
try:
|
656
|
+
coord_left, coord_top, right, bottom = win32gui.GetWindowRect(self.window_handle)
|
657
|
+
coord_width = right - coord_left
|
658
|
+
coord_height = bottom - coord_top
|
659
|
+
|
660
|
+
hwnd_dc = win32gui.GetWindowDC(self.window_handle)
|
661
|
+
mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
|
662
|
+
save_dc = mfc_dc.CreateCompatibleDC()
|
663
|
+
|
664
|
+
save_bitmap = win32ui.CreateBitmap()
|
665
|
+
save_bitmap.CreateCompatibleBitmap(mfc_dc, coord_width, coord_height)
|
666
|
+
save_dc.SelectObject(save_bitmap)
|
667
|
+
|
668
|
+
result = ctypes.windll.user32.PrintWindow(self.window_handle, save_dc.GetSafeHdc(), 2)
|
669
|
+
|
670
|
+
bmpinfo = save_bitmap.GetInfo()
|
671
|
+
bmpstr = save_bitmap.GetBitmapBits(True)
|
672
|
+
except pywintypes.error:
|
673
|
+
return 0
|
674
|
+
img = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
|
675
|
+
try:
|
676
|
+
win32gui.DeleteObject(save_bitmap.GetHandle())
|
677
|
+
except:
|
678
|
+
pass
|
679
|
+
try:
|
680
|
+
save_dc.DeleteDC()
|
681
|
+
except:
|
682
|
+
pass
|
683
|
+
try:
|
684
|
+
mfc_dc.DeleteDC()
|
685
|
+
except:
|
686
|
+
pass
|
687
|
+
try:
|
688
|
+
win32gui.ReleaseDC(self.window_handle, hwnd_dc)
|
689
|
+
except:
|
690
|
+
pass
|
691
|
+
else:
|
692
|
+
sct_img = self.sct.grab(self.sct_params)
|
693
|
+
img = Image.frombytes('RGB', sct_img.size, sct_img.bgra, 'raw', 'BGRX')
|
694
|
+
|
695
|
+
# import random # Ensure this is imported at the top of the file if not already
|
696
|
+
# rand_int = random.randint(1, 10) # Executes only once out of 10 times
|
697
|
+
|
698
|
+
# if rand_int == 1: # Executes only once out of 10 times
|
699
|
+
# img.show()
|
700
|
+
|
701
|
+
if self.screen_capture_exclusions:
|
702
|
+
img = img.convert("RGBA")
|
703
|
+
draw = ImageDraw.Draw(img)
|
704
|
+
for exclusion in self.screen_capture_exclusions:
|
705
|
+
left, top, width, height = exclusion
|
706
|
+
draw.rectangle((left, top, left + width, top + height), fill=(0, 0, 0, 0))
|
707
|
+
|
708
|
+
if self.custom_left:
|
709
|
+
img = img.crop((self.custom_left, self.custom_top, self.custom_left + self.custom_width, self.custom_top + self.custom_height))
|
710
|
+
|
711
|
+
# if rand_int == 1:
|
712
|
+
# img.show()
|
713
|
+
|
714
|
+
return img
|
715
|
+
|
716
|
+
|
396
717
|
class AutopauseTimer:
|
397
718
|
def __init__(self, timeout):
|
398
719
|
self.stop_event = threading.Event()
|
399
720
|
self.timeout = timeout
|
400
721
|
self.timer_thread = None
|
401
722
|
|
723
|
+
def __del__(self):
|
724
|
+
self.stop()
|
725
|
+
|
402
726
|
def start(self):
|
403
727
|
self.stop()
|
404
728
|
self.stop_event.clear()
|
@@ -411,28 +735,22 @@ class AutopauseTimer:
|
|
411
735
|
self.timer_thread.join()
|
412
736
|
|
413
737
|
def _countdown(self):
|
414
|
-
seconds = self.timeout
|
415
|
-
while seconds > 0 and not self.stop_event.is_set():
|
738
|
+
seconds = self.timeout
|
739
|
+
while seconds > 0 and not self.stop_event.is_set() and not terminated:
|
416
740
|
time.sleep(1)
|
417
741
|
seconds -= 1
|
418
742
|
if not self.stop_event.is_set():
|
419
743
|
self.stop_event.set()
|
420
|
-
if not paused:
|
744
|
+
if not (paused or terminated):
|
421
745
|
pause_handler(True)
|
422
746
|
|
423
747
|
|
424
748
|
def pause_handler(is_combo=True):
|
425
749
|
global paused
|
426
|
-
|
427
|
-
if paused:
|
428
|
-
message = 'Unpaused!'
|
429
|
-
just_unpaused = True
|
430
|
-
else:
|
431
|
-
message = 'Paused!'
|
750
|
+
message = 'Unpaused!' if paused else 'Paused!'
|
432
751
|
|
433
752
|
if auto_pause_handler:
|
434
753
|
auto_pause_handler.stop()
|
435
|
-
|
436
754
|
if is_combo:
|
437
755
|
notifier.send(title='owocr', message=message)
|
438
756
|
logger.info(message)
|
@@ -450,7 +768,6 @@ def engine_change_handler(user_input='s', is_combo=True):
|
|
450
768
|
engine_index += 1
|
451
769
|
elif user_input.lower() != '' and user_input.lower() in engine_keys:
|
452
770
|
engine_index = engine_keys.index(user_input.lower())
|
453
|
-
|
454
771
|
if engine_index != old_engine_index:
|
455
772
|
new_engine_name = engine_instances[engine_index].readable_name
|
456
773
|
if is_combo:
|
@@ -497,15 +814,6 @@ def user_input_thread_run():
|
|
497
814
|
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
498
815
|
|
499
816
|
|
500
|
-
def on_screenshot_combo():
|
501
|
-
if not paused:
|
502
|
-
screenshot_event.set()
|
503
|
-
|
504
|
-
def on_screenshot_bus():
|
505
|
-
if not paused:
|
506
|
-
screenshot_event.set()
|
507
|
-
|
508
|
-
|
509
817
|
def signal_handler(sig, frame):
|
510
818
|
global terminated
|
511
819
|
logger.info('Terminated!')
|
@@ -519,9 +827,10 @@ def on_window_closed(alive):
|
|
519
827
|
terminated = True
|
520
828
|
|
521
829
|
|
522
|
-
def
|
523
|
-
|
524
|
-
|
830
|
+
def on_screenshot_combo():
|
831
|
+
if not paused:
|
832
|
+
img = take_screenshot()
|
833
|
+
image_queue.put((img, True))
|
525
834
|
|
526
835
|
|
527
836
|
def on_window_minimized(minimized):
|
@@ -529,47 +838,7 @@ def on_window_minimized(minimized):
|
|
529
838
|
screencapture_window_visible = not minimized
|
530
839
|
|
531
840
|
|
532
|
-
def
|
533
|
-
ns_data = NSData.dataWithBytes_length_(img, len(img))
|
534
|
-
ns_image = NSImage.alloc().initWithData_(ns_data)
|
535
|
-
|
536
|
-
new_image = NSBitmapImageRep.alloc().initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel_(
|
537
|
-
None, # Set to None to create a new bitmap
|
538
|
-
int(ns_image.size().width),
|
539
|
-
int(ns_image.size().height),
|
540
|
-
8, # Bits per sample
|
541
|
-
4, # Samples per pixel (R, G, B, A)
|
542
|
-
True, # Has alpha
|
543
|
-
False, # Is not planar
|
544
|
-
NSDeviceRGBColorSpace,
|
545
|
-
0, # Automatically compute bytes per row
|
546
|
-
32 # Bits per pixel (8 bits per sample * 4 samples per pixel)
|
547
|
-
)
|
548
|
-
|
549
|
-
context = NSGraphicsContext.graphicsContextWithBitmapImageRep_(new_image)
|
550
|
-
NSGraphicsContext.setCurrentContext_(context)
|
551
|
-
|
552
|
-
ns_image.drawAtPoint_fromRect_operation_fraction_(
|
553
|
-
NSZeroPoint,
|
554
|
-
NSZeroRect,
|
555
|
-
NSCompositingOperationCopy,
|
556
|
-
1.0
|
557
|
-
)
|
558
|
-
|
559
|
-
return new_image.TIFFRepresentation()
|
560
|
-
|
561
|
-
|
562
|
-
def are_images_identical(img1, img2):
|
563
|
-
if None in (img1, img2):
|
564
|
-
return img1 == img2
|
565
|
-
|
566
|
-
img1 = np.array(img1)
|
567
|
-
img2 = np.array(img2)
|
568
|
-
|
569
|
-
return (img1.shape == img2.shape) and (img1 == img2).all()
|
570
|
-
|
571
|
-
|
572
|
-
def process_and_write_results(img_or_path, write_to, notifications, last_result, filtering, engine=None, rectangle=None, start_time=None):
|
841
|
+
def process_and_write_results(img_or_path, write_to=None, last_result=None, filtering=None, notify=None, engine=None, rectangle=None, ocr_start_time=None):
|
573
842
|
global engine_index
|
574
843
|
if auto_pause_handler:
|
575
844
|
auto_pause_handler.stop()
|
@@ -583,9 +852,9 @@ def process_and_write_results(img_or_path, write_to, notifications, last_result,
|
|
583
852
|
else:
|
584
853
|
engine_instance = engine_instances[engine_index]
|
585
854
|
|
586
|
-
|
855
|
+
start_time = time.time()
|
587
856
|
res, text = engine_instance(img_or_path)
|
588
|
-
|
857
|
+
end_time = time.time()
|
589
858
|
|
590
859
|
orig_text = []
|
591
860
|
engine_color = config.get_general('engine_color')
|
@@ -594,13 +863,16 @@ def process_and_write_results(img_or_path, write_to, notifications, last_result,
|
|
594
863
|
#
|
595
864
|
# print(lang)
|
596
865
|
|
866
|
+
# print(last_result)
|
867
|
+
# print(engine_index)
|
868
|
+
|
597
869
|
if res:
|
598
870
|
if filtering:
|
599
871
|
text, orig_text = filtering(text, last_result)
|
600
872
|
if lang == "ja" or lang == "zh":
|
601
873
|
text = post_process(text)
|
602
|
-
logger.opt(ansi=True).info(f'Text recognized in {
|
603
|
-
if notifications:
|
874
|
+
logger.opt(ansi=True).info(f'Text recognized in {end_time - start_time:0.03f}s using <{engine_color}>{engine_instance.readable_name}</{engine_color}>: {text}')
|
875
|
+
if notify and config.get_general('notifications'):
|
604
876
|
notifier.send(title='owocr', message='Text recognized: ' + text)
|
605
877
|
|
606
878
|
if write_to == 'websocket':
|
@@ -608,7 +880,7 @@ def process_and_write_results(img_or_path, write_to, notifications, last_result,
|
|
608
880
|
elif write_to == 'clipboard':
|
609
881
|
pyperclipfix.copy(text)
|
610
882
|
elif write_to == "callback":
|
611
|
-
txt_callback(text, orig_text, rectangle,
|
883
|
+
txt_callback(text, orig_text, rectangle, ocr_start_time, img_or_path)
|
612
884
|
elif write_to:
|
613
885
|
with Path(write_to).open('a', encoding='utf-8') as f:
|
614
886
|
f.write(text + '\n')
|
@@ -616,7 +888,7 @@ def process_and_write_results(img_or_path, write_to, notifications, last_result,
|
|
616
888
|
if auto_pause_handler and not paused:
|
617
889
|
auto_pause_handler.start()
|
618
890
|
else:
|
619
|
-
logger.opt(ansi=True).info(f'<{engine_color}>{engine_instance.readable_name}</{engine_color}> reported an error after {
|
891
|
+
logger.opt(ansi=True).info(f'<{engine_color}>{engine_instance.readable_name}</{engine_color}> reported an error after {end_time - start_time:0.03f}s: {text}')
|
620
892
|
|
621
893
|
# print(orig_text)
|
622
894
|
# print(text)
|
@@ -743,7 +1015,7 @@ def run(read_from=None,
|
|
743
1015
|
for _, engine_class in sorted(inspect.getmembers(sys.modules[__name__],
|
744
1016
|
lambda x: hasattr(x, '__module__') and x.__module__ and (
|
745
1017
|
__package__ + '.ocr' in x.__module__ or __package__ + '.secret' in x.__module__) and inspect.isclass(
|
746
|
-
|
1018
|
+
x))):
|
747
1019
|
if len(config_engines) == 0 or engine_class.name in config_engines:
|
748
1020
|
if config.get_engine(engine_class.name) == None:
|
749
1021
|
engine_instance = engine_class()
|
@@ -767,6 +1039,8 @@ def run(read_from=None,
|
|
767
1039
|
global first_pressed
|
768
1040
|
global notifier
|
769
1041
|
global auto_pause_handler
|
1042
|
+
global websocket_server_thread
|
1043
|
+
global image_queue
|
770
1044
|
custom_left = None
|
771
1045
|
terminated = False
|
772
1046
|
paused = pause_at_startup
|
@@ -777,14 +1051,31 @@ def run(read_from=None,
|
|
777
1051
|
engine_color = config.get_general('engine_color')
|
778
1052
|
prefix_to_use = ""
|
779
1053
|
delay_secs = config.get_general('delay_secs')
|
1054
|
+
|
1055
|
+
non_path_inputs = ('screencapture', 'clipboard', 'websocket', 'unixsocket')
|
1056
|
+
read_from_secondary = config.get_general('read_from_secondary')
|
1057
|
+
read_from_path = None
|
1058
|
+
read_from_readable = []
|
1059
|
+
terminated = False
|
1060
|
+
paused = config.get_general('pause_at_startup')
|
1061
|
+
auto_pause = config.get_general('auto_pause')
|
1062
|
+
clipboard_thread = None
|
1063
|
+
websocket_server_thread = None
|
1064
|
+
directory_watcher_thread = None
|
1065
|
+
unix_socket_server = None
|
1066
|
+
key_combo_listener = None
|
1067
|
+
filtering = None
|
1068
|
+
auto_pause_handler = None
|
1069
|
+
engine_index = engine_keys.index(default_engine) if default_engine != '' else 0
|
1070
|
+
engine_color = config.get_general('engine_color')
|
1071
|
+
combo_pause = config.get_general('combo_pause')
|
1072
|
+
combo_engine_switch = config.get_general('combo_engine_switch')
|
780
1073
|
screen_capture_on_combo = False
|
781
1074
|
notifier = DesktopNotifierSync()
|
1075
|
+
image_queue = queue.Queue()
|
782
1076
|
key_combos = {}
|
783
1077
|
|
784
|
-
if
|
785
|
-
auto_pause_handler = AutopauseTimer(auto_pause)
|
786
|
-
|
787
|
-
if combo_pause:
|
1078
|
+
if combo_pause != '':
|
788
1079
|
key_combos[combo_pause] = pause_handler
|
789
1080
|
if combo_engine_switch:
|
790
1081
|
if combo_pause:
|
@@ -792,190 +1083,51 @@ def run(read_from=None,
|
|
792
1083
|
else:
|
793
1084
|
raise ValueError('combo_pause must also be specified')
|
794
1085
|
|
795
|
-
if
|
796
|
-
|
797
|
-
websocket_server_thread = WebsocketServerThread(read_from == 'websocket')
|
1086
|
+
if 'websocket' in (read_from, read_from_secondary) or write_to == 'websocket':
|
1087
|
+
websocket_server_thread = WebsocketServerThread('websocket' in (read_from, read_from_secondary))
|
798
1088
|
websocket_server_thread.start()
|
799
1089
|
|
800
1090
|
if write_to == "callback" and text_callback:
|
801
1091
|
global txt_callback
|
802
1092
|
txt_callback = text_callback
|
803
1093
|
|
804
|
-
if read_from
|
805
|
-
global
|
806
|
-
|
807
|
-
|
808
|
-
|
1094
|
+
if 'screencapture' in (read_from, read_from_secondary):
|
1095
|
+
global take_screenshot
|
1096
|
+
screen_capture_combo = config.get_general('screen_capture_combo')
|
1097
|
+
last_screenshot_time = 0
|
1098
|
+
last_result = ([], engine_index)
|
1099
|
+
if screen_capture_combo != '':
|
1100
|
+
screen_capture_on_combo = True
|
1101
|
+
key_combos[screen_capture_combo] = on_screenshot_combo
|
1102
|
+
take_screenshot = ScreenshotClass(screen_capture_area, screen_capture_window, screen_capture_exclusions, screen_capture_only_active_windows)
|
1103
|
+
filtering = TextFiltering()
|
1104
|
+
read_from_readable.append('screen capture')
|
1105
|
+
if 'websocket' in (read_from, read_from_secondary):
|
1106
|
+
read_from_readable.append('websocket')
|
1107
|
+
if 'unixsocket' in (read_from, read_from_secondary):
|
809
1108
|
if sys.platform == 'win32':
|
810
1109
|
raise ValueError('"unixsocket" is not currently supported on Windows')
|
811
|
-
|
812
|
-
global unixsocket_queue
|
813
|
-
unixsocket_queue = queue.Queue()
|
814
1110
|
socket_path = Path('/tmp/owocr.sock')
|
815
1111
|
if socket_path.exists():
|
816
1112
|
socket_path.unlink()
|
817
1113
|
unix_socket_server = socketserver.ThreadingUnixStreamServer(str(socket_path), RequestHandler)
|
818
1114
|
unix_socket_server_thread = threading.Thread(target=unix_socket_server.serve_forever, daemon=True)
|
819
1115
|
unix_socket_server_thread.start()
|
820
|
-
read_from_readable
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
if
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
windows_clipboard_thread.start()
|
836
|
-
windows_clipboard_polling = True
|
837
|
-
else:
|
838
|
-
from PIL import ImageGrab
|
839
|
-
|
840
|
-
read_from_readable = 'clipboard'
|
841
|
-
elif read_from == 'screencapture':
|
842
|
-
if screen_capture_combo:
|
843
|
-
screen_capture_on_combo = True
|
844
|
-
global screenshot_event
|
845
|
-
screenshot_event = threading.Event()
|
846
|
-
key_combos[screen_capture_combo] = on_screenshot_combo
|
847
|
-
if screen_capture_event_bus:
|
848
|
-
screen_capture_on_combo = True
|
849
|
-
screenshot_event = threading.Event()
|
850
|
-
screen_capture_event_bus = on_screenshot_bus
|
851
|
-
if type(screen_capture_area) == tuple:
|
852
|
-
screen_capture_area = ','.join(map(str, screen_capture_area))
|
853
|
-
global screencapture_window_active
|
854
|
-
global screencapture_window_visible
|
855
|
-
screencapture_mode = None
|
856
|
-
screencapture_window_active = True
|
857
|
-
screencapture_window_visible = True
|
858
|
-
last_result = ([], engine_index)
|
859
|
-
if screen_capture_area == '':
|
860
|
-
screencapture_mode = 0
|
861
|
-
elif screen_capture_area.startswith('screen_'):
|
862
|
-
parts = screen_capture_area.split('_')
|
863
|
-
if len(parts) != 2 or not parts[1].isdigit():
|
864
|
-
raise ValueError('Invalid screen_capture_area')
|
865
|
-
screen_capture_monitor = int(parts[1])
|
866
|
-
screencapture_mode = 1
|
867
|
-
elif len(screen_capture_area.split(',')) == 4:
|
868
|
-
screencapture_mode = 3
|
869
|
-
else:
|
870
|
-
screencapture_mode = 2
|
871
|
-
screen_capture_window = screen_capture_area
|
872
|
-
if screen_capture_window:
|
873
|
-
screencapture_mode = 2
|
874
|
-
|
875
|
-
if screencapture_mode != 2:
|
876
|
-
sct = mss.mss()
|
877
|
-
|
878
|
-
if screencapture_mode == 1:
|
879
|
-
mon = sct.monitors
|
880
|
-
if len(mon) <= screen_capture_monitor:
|
881
|
-
raise ValueError('Invalid monitor number in screen_capture_area')
|
882
|
-
coord_left = mon[screen_capture_monitor]['left']
|
883
|
-
coord_top = mon[screen_capture_monitor]['top']
|
884
|
-
coord_width = mon[screen_capture_monitor]['width']
|
885
|
-
coord_height = mon[screen_capture_monitor]['height']
|
886
|
-
elif screencapture_mode == 3:
|
887
|
-
coord_left, coord_top, coord_width, coord_height = [int(c.strip()) for c in screen_capture_area.split(',')]
|
888
|
-
else:
|
889
|
-
logger.opt(ansi=True).info('Launching screen coordinate picker')
|
890
|
-
screen_selection = get_screen_selection()
|
891
|
-
if not screen_selection:
|
892
|
-
raise ValueError('Picker window was closed or an error occurred')
|
893
|
-
screen_capture_monitor = screen_selection['monitor']
|
894
|
-
x, y, coord_width, coord_height = screen_selection['coordinates']
|
895
|
-
if coord_width > 0 and coord_height > 0:
|
896
|
-
coord_top = screen_capture_monitor['top'] + y
|
897
|
-
coord_left = screen_capture_monitor['left'] + x
|
898
|
-
else:
|
899
|
-
logger.opt(ansi=True).info('Selection is empty, selecting whole screen')
|
900
|
-
coord_left = screen_capture_monitor['left']
|
901
|
-
coord_top = screen_capture_monitor['top']
|
902
|
-
coord_width = screen_capture_monitor['width']
|
903
|
-
coord_height = screen_capture_monitor['height']
|
904
|
-
|
905
|
-
sct_params = {'top': coord_top, 'left': coord_left, 'width': coord_width, 'height': coord_height}
|
906
|
-
logger.opt(ansi=True).info(f'Selected coordinates: {coord_left},{coord_top},{coord_width},{coord_height}')
|
907
|
-
if len(screen_capture_area.split(',')) == 4:
|
908
|
-
custom_left, custom_top, custom_width, custom_height = [int(c.strip()) for c in screen_capture_area.split(',')]
|
909
|
-
if screencapture_mode == 2 or screen_capture_window:
|
910
|
-
area_invalid_error = '"screen_capture_area" must be empty, "screen_N" where N is a screen number starting from 1, a valid set of coordinates, or a valid window name'
|
911
|
-
if sys.platform == 'darwin':
|
912
|
-
if int(platform.mac_ver()[0].split('.')[0]) < 14:
|
913
|
-
old_macos_screenshot_api = True
|
914
|
-
else:
|
915
|
-
global screencapturekit_queue
|
916
|
-
screencapturekit_queue = queue.Queue()
|
917
|
-
CGMainDisplayID()
|
918
|
-
old_macos_screenshot_api = False
|
919
|
-
|
920
|
-
window_list = CGWindowListCopyWindowInfo(kCGWindowListExcludeDesktopElements, kCGNullWindowID)
|
921
|
-
window_titles = []
|
922
|
-
window_ids = []
|
923
|
-
window_index = None
|
924
|
-
for i, window in enumerate(window_list):
|
925
|
-
window_title = window.get(kCGWindowName, '')
|
926
|
-
if psutil.Process(window['kCGWindowOwnerPID']).name() not in ('Terminal', 'iTerm2'):
|
927
|
-
window_titles.append(window_title)
|
928
|
-
window_ids.append(window['kCGWindowNumber'])
|
929
|
-
|
930
|
-
if screen_capture_area in window_titles:
|
931
|
-
window_index = window_titles.index(screen_capture_window)
|
932
|
-
else:
|
933
|
-
for t in window_titles:
|
934
|
-
if screen_capture_area in t:
|
935
|
-
window_index = window_titles.index(t)
|
936
|
-
break
|
937
|
-
|
938
|
-
if not window_index:
|
939
|
-
raise ValueError(area_invalid_error)
|
940
|
-
|
941
|
-
window_id = window_ids[window_index]
|
942
|
-
window_title = window_titles[window_index]
|
943
|
-
|
944
|
-
if screen_capture_only_active_windows:
|
945
|
-
screencapture_window_active = False
|
946
|
-
macos_window_tracker = MacOSWindowTracker(window_id)
|
947
|
-
macos_window_tracker.start()
|
948
|
-
logger.opt(ansi=True).info(f'Selected window: {window_title}')
|
949
|
-
elif sys.platform == 'win32':
|
950
|
-
window_handle, window_title = get_windows_window_handle(screen_capture_window)
|
951
|
-
|
952
|
-
if not window_handle:
|
953
|
-
raise ValueError(area_invalid_error)
|
954
|
-
|
955
|
-
ctypes.windll.shcore.SetProcessDpiAwareness(1)
|
956
|
-
|
957
|
-
if screen_capture_only_active_windows:
|
958
|
-
screencapture_window_active = False
|
959
|
-
windows_window_tracker = WindowsWindowTracker(window_handle, screen_capture_only_active_windows)
|
960
|
-
windows_window_tracker.start()
|
961
|
-
logger.opt(ansi=True).info(f'Selected window: {window_title}')
|
962
|
-
else:
|
963
|
-
raise ValueError('Window capture is only currently supported on Windows and macOS')
|
964
|
-
|
965
|
-
filtering = TextFiltering()
|
966
|
-
read_from_readable = 'screen capture'
|
967
|
-
else:
|
968
|
-
read_from = Path(read_from)
|
969
|
-
if not read_from.is_dir():
|
970
|
-
raise ValueError('read_from must be either "websocket", "unixsocket", "clipboard", "screencapture", or a path to a directory')
|
971
|
-
|
972
|
-
allowed_extensions = ('.png', '.jpg', '.jpeg', '.bmp', '.gif', '.webp')
|
973
|
-
old_paths = set()
|
974
|
-
for path in read_from.iterdir():
|
975
|
-
if path.suffix.lower() in allowed_extensions:
|
976
|
-
old_paths.add(get_path_key(path))
|
977
|
-
|
978
|
-
read_from_readable = f'directory {read_from}'
|
1116
|
+
read_from_readable.append('unix socket')
|
1117
|
+
if 'clipboard' in (read_from, read_from_secondary):
|
1118
|
+
clipboard_thread = ClipboardThread()
|
1119
|
+
clipboard_thread.start()
|
1120
|
+
read_from_readable.append('clipboard')
|
1121
|
+
if any(i and i not in non_path_inputs for i in (read_from, read_from_secondary)):
|
1122
|
+
if all(i and i not in non_path_inputs for i in (read_from, read_from_secondary)):
|
1123
|
+
raise ValueError("read_from and read_from_secondary can't both be directory paths")
|
1124
|
+
delete_images = config.get_general('delete_images')
|
1125
|
+
read_from_path = Path(read_from) if read_from not in non_path_inputs else Path(read_from_secondary)
|
1126
|
+
if not read_from_path.is_dir():
|
1127
|
+
raise ValueError('read_from and read_from_secondary must be either "websocket", "unixsocket", "clipboard", "screencapture", or a path to a directory')
|
1128
|
+
directory_watcher_thread = DirectoryWatcher(read_from_path)
|
1129
|
+
directory_watcher_thread.start()
|
1130
|
+
read_from_readable.append(f'directory {read_from_path}')
|
979
1131
|
|
980
1132
|
if len(key_combos) > 0:
|
981
1133
|
key_combo_listener = keyboard.GlobalHotKeys(key_combos)
|
@@ -988,214 +1140,66 @@ def run(read_from=None,
|
|
988
1140
|
raise ValueError('write_to must be either "websocket", "clipboard" or a path to a text file')
|
989
1141
|
write_to_readable = f'file {write_to}'
|
990
1142
|
|
991
|
-
|
1143
|
+
process_queue = (any(i in ('clipboard', 'websocket', 'unixsocket') for i in (read_from, read_from_secondary)) or read_from_path or screen_capture_on_combo)
|
1144
|
+
process_screenshots = 'screencapture' in (read_from, read_from_secondary) and not screen_capture_on_combo
|
1145
|
+
if threading.current_thread() == threading.main_thread():
|
1146
|
+
signal.signal(signal.SIGINT, signal_handler)
|
1147
|
+
if (not process_screenshots) and auto_pause != 0:
|
1148
|
+
auto_pause_handler = AutopauseTimer(auto_pause)
|
992
1149
|
user_input_thread = threading.Thread(target=user_input_thread_run, daemon=True)
|
993
1150
|
user_input_thread.start()
|
994
|
-
logger.opt(ansi=True).info(f"Reading from {read_from_readable}, writing to {write_to_readable} using <{engine_color}>{engine_instances[engine_index].readable_name}</{engine_color}>{' (paused)' if paused else ''}")
|
1151
|
+
logger.opt(ansi=True).info(f"Reading from {' and '.join(read_from_readable)}, writing to {write_to_readable} using <{engine_color}>{engine_instances[engine_index].readable_name}</{engine_color}>{' (paused)' if paused else ''}")
|
995
1152
|
|
996
|
-
while not terminated
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
item = websocket_queue.get(timeout=delay_secs)
|
1002
|
-
except queue.Empty:
|
1003
|
-
break
|
1004
|
-
else:
|
1005
|
-
if not paused:
|
1006
|
-
img = Image.open(io.BytesIO(item))
|
1007
|
-
process_and_write_results(img, write_to, notifications, None, None, start_time=start_time)
|
1008
|
-
elif read_from == 'unixsocket':
|
1009
|
-
while True:
|
1010
|
-
try:
|
1011
|
-
item = unixsocket_queue.get(timeout=delay_secs)
|
1012
|
-
except queue.Empty:
|
1013
|
-
break
|
1014
|
-
else:
|
1015
|
-
if not paused:
|
1016
|
-
img = Image.open(io.BytesIO(item))
|
1017
|
-
process_and_write_results(img, write_to, notifications, None, None, start_time=start_time)
|
1018
|
-
elif read_from == 'clipboard':
|
1019
|
-
process_clipboard = False
|
1020
|
-
if windows_clipboard_polling:
|
1021
|
-
if clipboard_event.wait(delay_secs):
|
1022
|
-
clipboard_event.clear()
|
1023
|
-
while True:
|
1024
|
-
try:
|
1025
|
-
win32clipboard.OpenClipboard()
|
1026
|
-
break
|
1027
|
-
except pywintypes.error:
|
1028
|
-
pass
|
1029
|
-
time.sleep(0.1)
|
1030
|
-
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_DIB):
|
1031
|
-
clipboard_text = ''
|
1032
|
-
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_UNICODETEXT):
|
1033
|
-
clipboard_text = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
|
1034
|
-
if ignore_flag or clipboard_text != '*ocr_ignore*':
|
1035
|
-
img = Image.open(io.BytesIO(win32clipboard.GetClipboardData(win32clipboard.CF_DIB)))
|
1036
|
-
process_clipboard = True
|
1037
|
-
win32clipboard.CloseClipboard()
|
1038
|
-
elif macos_clipboard_polling:
|
1039
|
-
if not paused:
|
1040
|
-
with objc.autorelease_pool():
|
1041
|
-
old_count = count
|
1042
|
-
count = pasteboard.changeCount()
|
1043
|
-
if not just_unpaused and count != old_count and NSPasteboardTypeTIFF in pasteboard.types():
|
1044
|
-
clipboard_text = ''
|
1045
|
-
if NSPasteboardTypeString in pasteboard.types():
|
1046
|
-
clipboard_text = pasteboard.stringForType_(NSPasteboardTypeString)
|
1047
|
-
if ignore_flag or clipboard_text != '*ocr_ignore*':
|
1048
|
-
img = normalize_macos_clipboard(pasteboard.dataForType_(NSPasteboardTypeTIFF))
|
1049
|
-
img = Image.open(io.BytesIO(img))
|
1050
|
-
process_clipboard = True
|
1051
|
-
else:
|
1052
|
-
if not paused:
|
1053
|
-
old_img = img
|
1054
|
-
try:
|
1055
|
-
img = ImageGrab.grabclipboard()
|
1056
|
-
except Exception:
|
1057
|
-
pass
|
1058
|
-
else:
|
1059
|
-
if ((not just_unpaused) and isinstance(img, Image.Image) and \
|
1060
|
-
(ignore_flag or pyperclipfix.paste() != '*ocr_ignore*') and \
|
1061
|
-
(not are_images_identical(img, old_img))):
|
1062
|
-
process_clipboard = True
|
1063
|
-
|
1064
|
-
if process_clipboard:
|
1065
|
-
process_and_write_results(img, write_to, notifications, None, None, start_time=start_time)
|
1066
|
-
|
1067
|
-
just_unpaused = False
|
1068
|
-
|
1069
|
-
if not windows_clipboard_polling:
|
1070
|
-
time.sleep(delay_secs)
|
1071
|
-
elif read_from == 'screencapture':
|
1072
|
-
if screen_capture_on_combo:
|
1073
|
-
take_screenshot = screenshot_event.wait(delay_secs)
|
1074
|
-
if take_screenshot:
|
1075
|
-
screenshot_event.clear()
|
1076
|
-
else:
|
1077
|
-
take_screenshot = screencapture_window_active and not paused
|
1078
|
-
|
1079
|
-
if take_screenshot and screencapture_window_visible:
|
1080
|
-
if screencapture_mode == 2:
|
1081
|
-
if sys.platform == 'darwin':
|
1082
|
-
with objc.autorelease_pool():
|
1083
|
-
if old_macos_screenshot_api:
|
1084
|
-
cg_image = CGWindowListCreateImageFromArray(CGRectNull, [window_id], kCGWindowImageBoundsIgnoreFraming)
|
1085
|
-
else:
|
1086
|
-
capture_macos_window_screenshot(window_id)
|
1087
|
-
try:
|
1088
|
-
cg_image = screencapturekit_queue.get(timeout=0.5)
|
1089
|
-
except queue.Empty:
|
1090
|
-
cg_image = None
|
1091
|
-
if not cg_image:
|
1092
|
-
on_window_closed(False)
|
1093
|
-
break
|
1094
|
-
width = CGImageGetWidth(cg_image)
|
1095
|
-
height = CGImageGetHeight(cg_image)
|
1096
|
-
raw_data = CGDataProviderCopyData(CGImageGetDataProvider(cg_image))
|
1097
|
-
bpr = CGImageGetBytesPerRow(cg_image)
|
1098
|
-
img = Image.frombuffer('RGBA', (width, height), raw_data, 'raw', 'BGRA', bpr, 1)
|
1099
|
-
else:
|
1100
|
-
try:
|
1101
|
-
coord_left, coord_top, right, bottom = win32gui.GetWindowRect(window_handle)
|
1102
|
-
|
1103
|
-
coord_width = right - coord_left
|
1104
|
-
coord_height = bottom - coord_top
|
1105
|
-
|
1106
|
-
hwnd_dc = win32gui.GetWindowDC(window_handle)
|
1107
|
-
mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
|
1108
|
-
save_dc = mfc_dc.CreateCompatibleDC()
|
1109
|
-
|
1110
|
-
save_bitmap = win32ui.CreateBitmap()
|
1111
|
-
save_bitmap.CreateCompatibleBitmap(mfc_dc, coord_width, coord_height)
|
1112
|
-
save_dc.SelectObject(save_bitmap)
|
1113
|
-
|
1114
|
-
result = ctypes.windll.user32.PrintWindow(window_handle, save_dc.GetSafeHdc(), 2)
|
1115
|
-
|
1116
|
-
bmpinfo = save_bitmap.GetInfo()
|
1117
|
-
bmpstr = save_bitmap.GetBitmapBits(True)
|
1118
|
-
except pywintypes.error:
|
1119
|
-
on_window_closed(False)
|
1120
|
-
break
|
1121
|
-
|
1122
|
-
# rand = random.randint(1, 10)
|
1123
|
-
|
1124
|
-
img = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
|
1125
|
-
|
1126
|
-
# if rand == 1:
|
1127
|
-
# img.show()
|
1153
|
+
while not terminated:
|
1154
|
+
ocr_start_time = datetime.now()
|
1155
|
+
start_time = time.time()
|
1156
|
+
img = None
|
1157
|
+
filter_img = False
|
1128
1158
|
|
1129
|
-
|
1130
|
-
|
1159
|
+
if process_queue:
|
1160
|
+
try:
|
1161
|
+
img, filter_img = image_queue.get(timeout=0.1)
|
1162
|
+
notify = True
|
1163
|
+
except queue.Empty:
|
1164
|
+
pass
|
1131
1165
|
|
1132
|
-
|
1133
|
-
|
1166
|
+
if (not img) and process_screenshots:
|
1167
|
+
if (not paused) and take_screenshot.screencapture_window_active and take_screenshot.screencapture_window_visible and (time.time() - last_screenshot_time) > screen_capture_delay_secs:
|
1168
|
+
img = take_screenshot()
|
1169
|
+
filter_img = True
|
1170
|
+
notify = False
|
1171
|
+
last_screenshot_time = time.time()
|
1134
1172
|
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
# img.save(os.path.join(get_temporary_directory(), 'screencapture_before.png'), 'png')
|
1143
|
-
if screen_capture_exclusions:
|
1144
|
-
img = img.convert("RGBA")
|
1145
|
-
draw = ImageDraw.Draw(img)
|
1146
|
-
for exclusion in screen_capture_exclusions:
|
1147
|
-
left, top, width, height = exclusion
|
1148
|
-
draw.rectangle((left, top, left + width, top + height), fill=(0, 0, 0, 0))
|
1149
|
-
# draw.rectangle((left, top, right, bottom), fill=(0, 0, 0))
|
1150
|
-
# img.save(os.path.join(get_temporary_directory(), 'screencapture.png'), 'png')
|
1151
|
-
res, _ = process_and_write_results(img, write_to, notifications, last_result, filtering, rectangle=rectangle, start_time=start_time)
|
1173
|
+
if img == 0:
|
1174
|
+
on_window_closed(False)
|
1175
|
+
terminated = True
|
1176
|
+
break
|
1177
|
+
elif img:
|
1178
|
+
if filter_img:
|
1179
|
+
res, _ = process_and_write_results(img, write_to, last_result, filtering, notify, rectangle=rectangle, ocr_start_time=ocr_start_time)
|
1152
1180
|
if res:
|
1153
1181
|
last_result = (res, engine_index)
|
1154
|
-
delay = screen_capture_delay_secs
|
1155
1182
|
else:
|
1156
|
-
|
1183
|
+
process_and_write_results(img, None, None, notify)
|
1184
|
+
if isinstance(img, Path):
|
1185
|
+
if delete_images:
|
1186
|
+
Path.unlink(img)
|
1157
1187
|
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
path_key = get_path_key(path)
|
1164
|
-
if path_key not in old_paths:
|
1165
|
-
old_paths.add(path_key)
|
1166
|
-
|
1167
|
-
if not paused:
|
1168
|
-
try:
|
1169
|
-
img = Image.open(path)
|
1170
|
-
img.load()
|
1171
|
-
except (UnidentifiedImageError, OSError) as e:
|
1172
|
-
logger.warning(f'Error while reading file {path}: {e}')
|
1173
|
-
else:
|
1174
|
-
process_and_write_results(img, write_to, notifications, None, None, start_time=start_time)
|
1175
|
-
img.close()
|
1176
|
-
if delete_images:
|
1177
|
-
Path.unlink(path)
|
1178
|
-
|
1179
|
-
time.sleep(delay_secs)
|
1180
|
-
|
1181
|
-
if read_from == 'websocket' or write_to == 'websocket':
|
1188
|
+
elapsed_time = time.time() - start_time
|
1189
|
+
if (not terminated) and elapsed_time < 0.1:
|
1190
|
+
time.sleep(0.1 - elapsed_time)
|
1191
|
+
|
1192
|
+
if websocket_server_thread:
|
1182
1193
|
websocket_server_thread.stop_server()
|
1183
1194
|
websocket_server_thread.join()
|
1184
|
-
if
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
macos_window_tracker.join()
|
1192
|
-
else:
|
1193
|
-
windows_window_tracker.stop = True
|
1194
|
-
windows_window_tracker.join()
|
1195
|
-
elif read_from == 'unixsocket':
|
1195
|
+
if clipboard_thread:
|
1196
|
+
if sys.platform == 'win32':
|
1197
|
+
win32api.PostThreadMessage(clipboard_thread.thread_id, win32con.WM_QUIT, 0, 0)
|
1198
|
+
clipboard_thread.join()
|
1199
|
+
if directory_watcher_thread:
|
1200
|
+
directory_watcher_thread.join()
|
1201
|
+
if unix_socket_server:
|
1196
1202
|
unix_socket_server.shutdown()
|
1197
1203
|
unix_socket_server_thread.join()
|
1198
|
-
if
|
1204
|
+
if key_combo_listener:
|
1199
1205
|
key_combo_listener.stop()
|
1200
|
-
if auto_pause_handler:
|
1201
|
-
auto_pause_handler.stop()
|