MaaFw 4.4.0b1__py3-none-manylinux2014_x86_64.whl → 5.4.0__py3-none-manylinux2014_x86_64.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.

Potentially problematic release.


This version of MaaFw might be problematic. Click here for more details.

maa/resource.py CHANGED
@@ -1,17 +1,20 @@
1
1
  import ctypes
2
2
  import pathlib
3
3
  import json
4
- from typing import Any, Optional, Union
4
+ from typing import Optional, Union, List, Dict
5
+ from dataclasses import dataclass, field
5
6
 
6
- from .notification_handler import NotificationHandler
7
+ import numpy
8
+
9
+ from .event_sink import EventSink, NotificationType
7
10
  from .define import *
8
11
  from .job import Job
9
12
  from .library import Library
10
- from .buffer import StringBuffer, StringListBuffer
13
+ from .buffer import StringBuffer, StringListBuffer, ImageBuffer
14
+ from .pipeline import JPipelineData, JPipelineParser
11
15
 
12
16
 
13
17
  class Resource:
14
- _notification_handler: Optional[NotificationHandler]
15
18
  _handle: MaaResourceHandle
16
19
  _own: bool
17
20
 
@@ -19,19 +22,24 @@ class Resource:
19
22
 
20
23
  def __init__(
21
24
  self,
22
- notification_handler: Optional[NotificationHandler] = None,
23
25
  handle: Optional[MaaResourceHandle] = None,
24
26
  ):
27
+ """创建资源 / Create resource
28
+
29
+ Args:
30
+ handle: 可选的外部句柄 / Optional external handle
31
+
32
+ Raises:
33
+ RuntimeError: 如果创建失败
34
+ """
35
+
25
36
  self._set_api_properties()
26
37
 
27
38
  if handle:
28
39
  self._handle = handle
29
40
  self._own = False
30
41
  else:
31
- self._notification_handler = notification_handler
32
- self._handle = Library.framework().MaaResourceCreate(
33
- *NotificationHandler._gen_c_param(self._notification_handler)
34
- )
42
+ self._handle = Library.framework().MaaResourceCreate()
35
43
  self._own = True
36
44
 
37
45
  if not self._handle:
@@ -45,20 +53,110 @@ class Resource:
45
53
  Library.framework().MaaResourceDestroy(self._handle)
46
54
 
47
55
  def post_bundle(self, path: Union[pathlib.Path, str]) -> Job:
48
- resid = Library.framework().MaaResourcePostBundle(
56
+ """异步加载资源 / Asynchronously load resources from path
57
+
58
+ 这是一个异步操作,会立即返回一个 Job 对象
59
+ This is an asynchronous operation that immediately returns a Job object
60
+
61
+ Args:
62
+ path: 资源路径 / Resource path
63
+
64
+ Returns:
65
+ Job: 作业对象,可通过 status/wait 查询状态 / Job object, can query status via status/wait
66
+ """
67
+ res_id = Library.framework().MaaResourcePostBundle(
49
68
  self._handle, str(path).encode()
50
69
  )
51
- return Job(resid, self._status, self._wait)
70
+ return Job(res_id, self._status, self._wait)
71
+
72
+ def post_ocr_model(self, path: Union[pathlib.Path, str]) -> Job:
73
+ """异步加载 OCR 模型 / Asynchronously load OCR model from path
74
+
75
+ 这是一个异步操作,会立即返回一个 Job 对象
76
+ This is an asynchronous operation that immediately returns a Job object
77
+
78
+ Args:
79
+ path: OCR 模型目录路径 / OCR model directory path
80
+
81
+ Returns:
82
+ Job: 作业对象,可通过 status/wait 查询状态 / Job object, can query status via status/wait
83
+ """
84
+ res_id = Library.framework().MaaResourcePostOcrModel(
85
+ self._handle, str(path).encode()
86
+ )
87
+ return Job(res_id, self._status, self._wait)
88
+
89
+ def post_pipeline(self, path: Union[pathlib.Path, str]) -> Job:
90
+ """异步加载 Pipeline / Asynchronously load pipeline from path
91
+
92
+ 这是一个异步操作,会立即返回一个 Job 对象
93
+ This is an asynchronous operation that immediately returns a Job object
94
+
95
+ 支持加载目录或单个 json/jsonc 文件
96
+ Supports loading a directory or a single json/jsonc file
97
+
98
+ Args:
99
+ path: Pipeline 目录或文件路径 / Pipeline directory or file path
100
+
101
+ Returns:
102
+ Job: 作业对象,可通过 status/wait 查询状态 / Job object, can query status via status/wait
103
+ """
104
+ res_id = Library.framework().MaaResourcePostPipeline(
105
+ self._handle, str(path).encode()
106
+ )
107
+ return Job(res_id, self._status, self._wait)
108
+
109
+ def post_image(self, path: Union[pathlib.Path, str]) -> Job:
110
+ """异步加载图片资源 / Asynchronously load image resources from path
111
+
112
+ 这是一个异步操作,会立即返回一个 Job 对象
113
+ This is an asynchronous operation that immediately returns a Job object
114
+
115
+ 支持加载目录或单个图片文件
116
+ Supports loading a directory or a single image file
117
+
118
+ Args:
119
+ path: 图片目录或文件路径 / Image directory or file path
120
+
121
+ Returns:
122
+ Job: 作业对象,可通过 status/wait 查询状态 / Job object, can query status via status/wait
123
+ """
124
+ res_id = Library.framework().MaaResourcePostImage(
125
+ self._handle, str(path).encode()
126
+ )
127
+ return Job(res_id, self._status, self._wait)
52
128
 
53
129
  def override_pipeline(self, pipeline_override: Dict) -> bool:
130
+ """覆盖 pipeline / Override pipeline_override
131
+
132
+ Args:
133
+ pipeline_override: 用于覆盖的 json / JSON for overriding
134
+
135
+ Returns:
136
+ bool: 是否成功 / Whether successful
137
+ """
138
+ pipeline_json = json.dumps(pipeline_override, ensure_ascii=False)
139
+
54
140
  return bool(
55
141
  Library.framework().MaaResourceOverridePipeline(
56
142
  self._handle,
57
- json.dumps(pipeline_override, ensure_ascii=False).encode(),
143
+ pipeline_json.encode(),
58
144
  )
59
145
  )
60
146
 
61
147
  def override_next(self, name: str, next_list: List[str]) -> bool:
148
+ """覆盖任务的 next 列表 / Override the next list of task
149
+
150
+ 注意:此方法会直接设置 next 列表,即使节点不存在也会创建
151
+ Note: This method directly sets the next list, creating the node if it doesn't exist
152
+
153
+ Args:
154
+ name: 任务名 / Task name
155
+ next_list: next 列表 / Next list
156
+
157
+ Returns:
158
+ bool: 总是返回 True / Always returns True
159
+ """
62
160
  list_buffer = StringListBuffer()
63
161
  list_buffer.set(next_list)
64
162
 
@@ -67,14 +165,40 @@ class Resource:
67
165
  self._handle, name.encode(), list_buffer._handle
68
166
  )
69
167
  )
70
-
71
- def get_node_data(self, name: str) -> Optional[dict]:
168
+
169
+ def override_image(self, image_name: str, image: numpy.ndarray) -> bool:
170
+ """覆盖图片 / Override the image corresponding to image_name
171
+
172
+ Args:
173
+ image_name: 图片名 / Image name
174
+ image: 图片数据 / Image data
175
+
176
+ Returns:
177
+ bool: 总是返回 True / Always returns True
178
+ """
179
+ image_buffer = ImageBuffer()
180
+ image_buffer.set(image)
181
+
182
+ return bool(
183
+ Library.framework().MaaResourceOverrideImage(
184
+ self._handle, image_name.encode(), image_buffer._handle
185
+ )
186
+ )
187
+
188
+ def get_node_data(self, name: str) -> Optional[Dict]:
189
+ """获取任务当前的定义 / Get the current definition of task
190
+
191
+ Args:
192
+ name: 任务名 / Task name
193
+
194
+ Returns:
195
+ Optional[Dict]: 任务定义字典,如果不存在则返回 None / Task definition dict, or None if not exists
196
+ """
72
197
  string_buffer = StringBuffer()
73
198
  if not Library.framework().MaaResourceGetNodeData(
74
199
  self._handle, name.encode(), string_buffer._handle
75
200
  ):
76
201
  return None
77
-
78
202
  data = string_buffer.get()
79
203
  if not data:
80
204
  return None
@@ -84,25 +208,80 @@ class Resource:
84
208
  except json.JSONDecodeError:
85
209
  return None
86
210
 
211
+ def get_node_object(self, name: str) -> Optional[JPipelineData]:
212
+ """获取任务当前的定义(解析为对象) / Get the current definition of task (parsed as object)
213
+
214
+ Args:
215
+ name: 任务名 / Task name
216
+
217
+ Returns:
218
+ Optional[JPipelineData]: 任务定义对象,如果不存在则返回 None / Task definition object, or None if not exists
219
+ """
220
+ node_data = self.get_node_data(name)
221
+
222
+ if not node_data:
223
+ return None
224
+
225
+ return JPipelineParser.parse_pipeline_data(node_data)
226
+
87
227
  @property
88
228
  def loaded(self) -> bool:
229
+ """判断是否加载正常 / Check if resources loaded normally
230
+
231
+ Returns:
232
+ bool: 是否已加载 / Whether loaded
233
+ """
89
234
  return bool(Library.framework().MaaResourceLoaded(self._handle))
90
235
 
91
236
  def clear(self) -> bool:
237
+ """清除已加载内容 / Clear loaded content
238
+
239
+ 如果资源正在加载中,此方法会失败
240
+ This method will fail if resources are currently loading
241
+
242
+ Returns:
243
+ bool: 成功返回 True,如果正在加载中则返回 False / Returns True on success, False if currently loading
244
+ """
92
245
  return bool(Library.framework().MaaResourceClear(self._handle))
93
246
 
94
247
  def use_cpu(self) -> bool:
248
+ """使用 CPU 进行推理 / Use CPU for inference
249
+
250
+ Returns:
251
+ bool: 是否成功 / Whether successful
252
+ """
95
253
  return self.set_inference(
96
254
  MaaInferenceExecutionProviderEnum.CPU, MaaInferenceDeviceEnum.CPU
97
255
  )
98
256
 
99
257
  def use_directml(self, device_id: int = MaaInferenceDeviceEnum.Auto) -> bool:
258
+ """使用 DirectML 进行推理 / Use DirectML for inference
259
+
260
+ Args:
261
+ device_id: 设备 id,默认为自动选择 / Device id, default is Auto
262
+
263
+ Returns:
264
+ bool: 是否成功 / Whether successful
265
+ """
100
266
  return self.set_inference(MaaInferenceExecutionProviderEnum.DirectML, device_id)
101
267
 
102
268
  def use_coreml(self, coreml_flag: int = MaaInferenceDeviceEnum.Auto) -> bool:
269
+ """使用 CoreML 进行推理 / Use CoreML for inference
270
+
271
+ Args:
272
+ coreml_flag: CoreML 标志,默认为自动选择 / CoreML flag, default is Auto
273
+
274
+ Returns:
275
+ bool: 是否成功 / Whether successful
276
+ """
103
277
  return self.set_inference(MaaInferenceExecutionProviderEnum.CoreML, coreml_flag)
104
278
 
105
279
  def use_auto_ep(self) -> bool:
280
+ """自动选择推理执行提供者 / Auto select inference execution provider
281
+
282
+ Returns:
283
+ bool: 是否成功 / Whether successful
284
+ """
106
285
  return self.set_inference(
107
286
  MaaInferenceExecutionProviderEnum.Auto, MaaInferenceDeviceEnum.Auto
108
287
  )
@@ -132,6 +311,14 @@ class Resource:
132
311
  return self.use_auto_ep()
133
312
 
134
313
  def custom_recognition(self, name: str):
314
+ """自定义识别器装饰器 / Custom recognition decorator
315
+
316
+ Args:
317
+ name: 识别器名称,需与 Pipeline 中的 custom_recognition 字段匹配 / Recognition name, should match the custom_recognition field in Pipeline
318
+
319
+ Returns:
320
+ 装饰器函数 / Decorator function
321
+ """
135
322
 
136
323
  def wrapper_recognition(recognition):
137
324
  self.register_custom_recognition(name=name, recognition=recognition())
@@ -142,7 +329,15 @@ class Resource:
142
329
  def register_custom_recognition(
143
330
  self, name: str, recognition: "CustomRecognition" # type: ignore
144
331
  ) -> bool:
332
+ """注册自定义识别器 / Register a custom recognizer
145
333
 
334
+ Args:
335
+ name: 名称 / Name
336
+ recognition: 自定义识别器 / Custom recognizer
337
+
338
+ Returns:
339
+ bool: 是否成功 / Whether successful
340
+ """
146
341
  # avoid gc
147
342
  self._custom_recognition_holder[name] = recognition
148
343
 
@@ -156,6 +351,14 @@ class Resource:
156
351
  )
157
352
 
158
353
  def unregister_custom_recognition(self, name: str) -> bool:
354
+ """移除自定义识别器 / Remove the custom recognizer
355
+
356
+ Args:
357
+ name: 名称 / Name
358
+
359
+ Returns:
360
+ bool: 是否成功 / Whether successful
361
+ """
159
362
  self._custom_recognition_holder.pop(name, None)
160
363
 
161
364
  return bool(
@@ -166,6 +369,11 @@ class Resource:
166
369
  )
167
370
 
168
371
  def clear_custom_recognition(self) -> bool:
372
+ """移除所有自定义识别器 / Remove all custom recognizers
373
+
374
+ Returns:
375
+ bool: 是否成功 / Whether successful
376
+ """
169
377
  self._custom_recognition_holder.clear()
170
378
 
171
379
  return bool(
@@ -175,6 +383,14 @@ class Resource:
175
383
  )
176
384
 
177
385
  def custom_action(self, name: str):
386
+ """自定义动作装饰器 / Custom action decorator
387
+
388
+ Args:
389
+ name: 动作名称,需与 Pipeline 中的 custom_action 字段匹配 / Action name, should match the custom_action field in Pipeline
390
+
391
+ Returns:
392
+ 装饰器函数 / Decorator function
393
+ """
178
394
 
179
395
  def wrapper_action(action):
180
396
  self.register_custom_action(name=name, action=action())
@@ -183,6 +399,15 @@ class Resource:
183
399
  return wrapper_action
184
400
 
185
401
  def register_custom_action(self, name: str, action: "CustomAction") -> bool: # type: ignore
402
+ """注册自定义操作 / Register a custom action
403
+
404
+ Args:
405
+ name: 名称 / Name
406
+ action: 自定义操作 / Custom action
407
+
408
+ Returns:
409
+ bool: 是否成功 / Whether successful
410
+ """
186
411
  # avoid gc
187
412
  self._custom_action_holder[name] = action
188
413
 
@@ -196,6 +421,14 @@ class Resource:
196
421
  )
197
422
 
198
423
  def unregister_custom_action(self, name: str) -> bool:
424
+ """移除自定义操作 / Remove the custom action
425
+
426
+ Args:
427
+ name: 名称 / Name
428
+
429
+ Returns:
430
+ bool: 是否成功 / Whether successful
431
+ """
199
432
  self._custom_action_holder.pop(name, None)
200
433
 
201
434
  return bool(
@@ -206,6 +439,11 @@ class Resource:
206
439
  )
207
440
 
208
441
  def clear_custom_action(self) -> bool:
442
+ """移除所有自定义操作 / Remove all custom actions
443
+
444
+ Returns:
445
+ bool: 是否成功 / Whether successful
446
+ """
209
447
  self._custom_action_holder.clear()
210
448
 
211
449
  return bool(
@@ -216,21 +454,103 @@ class Resource:
216
454
 
217
455
  @property
218
456
  def node_list(self) -> list[str]:
219
- """
220
- Returns a list of node names.
457
+ """获取任务列表 / Get task list
458
+
459
+ Returns:
460
+ list[str]: 任务名列表 / List of task names
461
+
462
+ Raises:
463
+ RuntimeError: 如果获取失败
221
464
  """
222
465
  buffer = StringListBuffer()
223
466
  if not Library.framework().MaaResourceGetNodeList(self._handle, buffer._handle):
224
467
  raise RuntimeError("Failed to get node list.")
225
468
  return buffer.get()
226
469
 
470
+ @property
471
+ def custom_recognition_list(self) -> list[str]:
472
+ """获取已注册的自定义识别器列表 / Get registered custom recognizer list
473
+
474
+ Returns:
475
+ list[str]: 自定义识别器名列表 / List of custom recognizer names
476
+
477
+ Raises:
478
+ RuntimeError: 如果获取失败
479
+ """
480
+ buffer = StringListBuffer()
481
+ if not Library.framework().MaaResourceGetCustomRecognitionList(
482
+ self._handle, buffer._handle
483
+ ):
484
+ raise RuntimeError("Failed to get custom recognition list.")
485
+ return buffer.get()
486
+
487
+ @property
488
+ def custom_action_list(self) -> list[str]:
489
+ """获取已注册的自定义操作列表 / Get registered custom action list
490
+
491
+ Returns:
492
+ list[str]: 自定义操作名列表 / List of custom action names
493
+
494
+ Raises:
495
+ RuntimeError: 如果获取失败
496
+ """
497
+ buffer = StringListBuffer()
498
+ if not Library.framework().MaaResourceGetCustomActionList(
499
+ self._handle, buffer._handle
500
+ ):
501
+ raise RuntimeError("Failed to get custom action list.")
502
+ return buffer.get()
503
+
227
504
  @property
228
505
  def hash(self) -> str:
506
+ """获取资源 hash / Get resource hash
507
+
508
+ Returns:
509
+ str: 资源 hash / Resource hash
510
+
511
+ Raises:
512
+ RuntimeError: 如果获取失败
513
+ """
229
514
  buffer = StringBuffer()
230
515
  if not Library.framework().MaaResourceGetHash(self._handle, buffer._handle):
231
516
  raise RuntimeError("Failed to get hash.")
232
517
  return buffer.get()
233
518
 
519
+ _sink_holder: Dict[int, "ResourceEventSink"] = {}
520
+
521
+ def add_sink(self, sink: "ResourceEventSink") -> Optional[int]:
522
+ """添加资源事件监听器 / Add resource event listener
523
+
524
+ Args:
525
+ sink: 事件监听器 / Event sink
526
+
527
+ Returns:
528
+ Optional[int]: 监听器 id,失败返回 None / Listener id, or None if failed
529
+ """
530
+ sink_id = int(
531
+ Library.framework().MaaResourceAddSink(
532
+ self._handle, *EventSink._gen_c_param(sink)
533
+ )
534
+ )
535
+ if sink_id == MaaInvalidId:
536
+ return None
537
+
538
+ self._sink_holder[sink_id] = sink
539
+ return sink_id
540
+
541
+ def remove_sink(self, sink_id: int) -> None:
542
+ """移除资源事件监听器 / Remove resource event listener
543
+
544
+ Args:
545
+ sink_id: 监听器 id / Listener id
546
+ """
547
+ Library.framework().MaaResourceRemoveSink(self._handle, sink_id)
548
+ self._sink_holder.pop(sink_id)
549
+
550
+ def clear_sinks(self) -> None:
551
+ """清除所有资源事件监听器 / Clear all resource event listeners"""
552
+ Library.framework().MaaResourceClearSinks(self._handle)
553
+
234
554
  ### private ###
235
555
 
236
556
  def set_inference(self, execution_provider: int, device_id: int) -> bool:
@@ -267,10 +587,7 @@ class Resource:
267
587
  Resource._api_properties_initialized = True
268
588
 
269
589
  Library.framework().MaaResourceCreate.restype = MaaResourceHandle
270
- Library.framework().MaaResourceCreate.argtypes = [
271
- MaaNotificationCallback,
272
- ctypes.c_void_p,
273
- ]
590
+ Library.framework().MaaResourceCreate.argtypes = []
274
591
 
275
592
  Library.framework().MaaResourceDestroy.restype = None
276
593
  Library.framework().MaaResourceDestroy.argtypes = [MaaResourceHandle]
@@ -281,6 +598,24 @@ class Resource:
281
598
  ctypes.c_char_p,
282
599
  ]
283
600
 
601
+ Library.framework().MaaResourcePostOcrModel.restype = MaaResId
602
+ Library.framework().MaaResourcePostOcrModel.argtypes = [
603
+ MaaResourceHandle,
604
+ ctypes.c_char_p,
605
+ ]
606
+
607
+ Library.framework().MaaResourcePostPipeline.restype = MaaResId
608
+ Library.framework().MaaResourcePostPipeline.argtypes = [
609
+ MaaResourceHandle,
610
+ ctypes.c_char_p,
611
+ ]
612
+
613
+ Library.framework().MaaResourcePostImage.restype = MaaResId
614
+ Library.framework().MaaResourcePostImage.argtypes = [
615
+ MaaResourceHandle,
616
+ ctypes.c_char_p,
617
+ ]
618
+
284
619
  Library.framework().MaaResourceStatus.restype = MaaStatus
285
620
  Library.framework().MaaResourceStatus.argtypes = [
286
621
  MaaResourceHandle,
@@ -312,6 +647,13 @@ class Resource:
312
647
  MaaStringListBufferHandle,
313
648
  ]
314
649
 
650
+ Library.framework().MaaResourceOverrideImage.restype = MaaBool
651
+ Library.framework().MaaResourceOverrideImage.argtypes = [
652
+ MaaResourceHandle,
653
+ ctypes.c_char_p,
654
+ MaaImageBufferHandle,
655
+ ]
656
+
315
657
  Library.framework().MaaResourceGetNodeData.restype = MaaBool
316
658
  Library.framework().MaaResourceGetNodeData.argtypes = [
317
659
  MaaContextHandle,
@@ -376,3 +718,70 @@ class Resource:
376
718
  MaaResourceHandle,
377
719
  MaaStringListBufferHandle,
378
720
  ]
721
+
722
+ Library.framework().MaaResourceGetCustomRecognitionList.restype = MaaBool
723
+ Library.framework().MaaResourceGetCustomRecognitionList.argtypes = [
724
+ MaaResourceHandle,
725
+ MaaStringListBufferHandle,
726
+ ]
727
+
728
+ Library.framework().MaaResourceGetCustomActionList.restype = MaaBool
729
+ Library.framework().MaaResourceGetCustomActionList.argtypes = [
730
+ MaaResourceHandle,
731
+ MaaStringListBufferHandle,
732
+ ]
733
+
734
+ Library.framework().MaaResourceAddSink.restype = MaaSinkId
735
+ Library.framework().MaaResourceAddSink.argtypes = [
736
+ MaaResourceHandle,
737
+ MaaEventCallback,
738
+ ctypes.c_void_p,
739
+ ]
740
+
741
+ Library.framework().MaaResourceRemoveSink.restype = None
742
+ Library.framework().MaaResourceRemoveSink.argtypes = [
743
+ MaaResourceHandle,
744
+ MaaSinkId,
745
+ ]
746
+
747
+ Library.framework().MaaResourceClearSinks.restype = None
748
+ Library.framework().MaaResourceClearSinks.argtypes = [MaaResourceHandle]
749
+
750
+
751
+ class ResourceEventSink(EventSink):
752
+
753
+ @dataclass
754
+ class ResourceLoadingDetail:
755
+ res_id: int
756
+ path: str
757
+ type: str
758
+ hash: str
759
+
760
+ def on_resource_loading(
761
+ self,
762
+ resource: Resource,
763
+ noti_type: NotificationType,
764
+ detail: ResourceLoadingDetail,
765
+ ):
766
+ pass
767
+
768
+ def on_raw_notification(self, resource: Resource, msg: str, details: dict):
769
+ pass
770
+
771
+ def _on_raw_notification(self, handle: ctypes.c_void_p, msg: str, details: dict):
772
+
773
+ resource = Resource(handle=handle)
774
+ self.on_raw_notification(resource, msg, details)
775
+
776
+ noti_type = EventSink._notification_type(msg)
777
+ if msg.startswith("Resource.Loading"):
778
+ detail = self.ResourceLoadingDetail(
779
+ res_id=details["res_id"],
780
+ path=details["path"],
781
+ type=details.get("type", "Bundle"),
782
+ hash=details["hash"],
783
+ )
784
+ self.on_resource_loading(resource, noti_type, detail)
785
+
786
+ else:
787
+ self.on_unknown_notification(resource, msg, details)