ryry-cli 4.2__tar.gz → 4.4__tar.gz

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 (32) hide show
  1. {ryry_cli-4.2/ryry_cli.egg-info → ryry_cli-4.4}/PKG-INFO +2 -1
  2. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/constant.py +2 -2
  3. ryry_cli-4.4/ryry/daemon_base.py +224 -0
  4. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/server_func.py +8 -8
  5. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/upload.py +62 -16
  6. {ryry_cli-4.2 → ryry_cli-4.4/ryry_cli.egg-info}/PKG-INFO +2 -1
  7. {ryry_cli-4.2 → ryry_cli-4.4}/ryry_cli.egg-info/requires.txt +1 -0
  8. {ryry_cli-4.2 → ryry_cli-4.4}/setup.py +2 -1
  9. ryry_cli-4.2/ryry/daemon_base.py +0 -131
  10. {ryry_cli-4.2 → ryry_cli-4.4}/LICENSE +0 -0
  11. {ryry_cli-4.2 → ryry_cli-4.4}/README.md +0 -0
  12. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/__init__.py +0 -0
  13. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/daemon_manager.py +0 -0
  14. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/main.py +0 -0
  15. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/ryry_server_socket.py +0 -0
  16. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/ryry_service.py +0 -0
  17. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/ryry_webapi.py +0 -0
  18. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/ryry_widget.py +0 -0
  19. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/script_template/__init__.py +0 -0
  20. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/script_template/daemon.py +0 -0
  21. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/script_template/main.py +0 -0
  22. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/script_template/run.py +0 -0
  23. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/shared_memory.py +0 -0
  24. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/store.py +0 -0
  25. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/task.py +0 -0
  26. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/taskUtils.py +0 -0
  27. {ryry_cli-4.2 → ryry_cli-4.4}/ryry/utils.py +0 -0
  28. {ryry_cli-4.2 → ryry_cli-4.4}/ryry_cli.egg-info/SOURCES.txt +0 -0
  29. {ryry_cli-4.2 → ryry_cli-4.4}/ryry_cli.egg-info/dependency_links.txt +0 -0
  30. {ryry_cli-4.2 → ryry_cli-4.4}/ryry_cli.egg-info/entry_points.txt +0 -0
  31. {ryry_cli-4.2 → ryry_cli-4.4}/ryry_cli.egg-info/top_level.txt +0 -0
  32. {ryry_cli-4.2 → ryry_cli-4.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ryry-cli
3
- Version: 4.2
3
+ Version: 4.4
4
4
  Summary: ryry tools
5
5
  Home-page: https://github.com/dalipenMedia
6
6
  Author: dalipen
@@ -23,6 +23,7 @@ Requires-Dist: gputil
23
23
  Requires-Dist: urlparser
24
24
  Requires-Dist: urllib3
25
25
  Requires-Dist: portalocker
26
+ Requires-Dist: mecord-cli>=0.7.405
26
27
 
27
28
  ryry Python Tool
28
29
  ===============================================
@@ -1,6 +1,6 @@
1
1
  #!!!!! do not change this file !!!!!
2
- app_version="4.2"
3
- app_bulld_anchor="Noh_2025-07-15 16:56:04.603552"
2
+ app_version="4.4"
3
+ app_bulld_anchor="Noh_2025-07-18 15:18:12.819574"
4
4
  app_name="ryry-cli"
5
5
  import sys, os
6
6
  if getattr(sys, 'frozen', False):
@@ -0,0 +1,224 @@
1
+ import sys
2
+ import os
3
+ import json
4
+ import time
5
+ import signal
6
+ from typing import Optional, Dict, Any
7
+
8
+ class SimpleDaemonBase:
9
+ """
10
+ 简化的Daemon基类
11
+
12
+ 用户需要实现的方法:
13
+ - initialize(): 初始化资源
14
+ - loop_function(): 循环执行的任务(可选)
15
+ - on_stop(): 清理资源(可选)
16
+ """
17
+
18
+ def __init__(self, widget_id: str):
19
+ # 基础配置
20
+ self.widget_id = widget_id
21
+ self.widget_name = "???"
22
+ self.running = False
23
+
24
+ # 配置加载
25
+ self._load_config()
26
+
27
+ # 信号处理
28
+ self._setup_signal_handlers()
29
+
30
+ print(f"【{self.widget_name}】Daemon实例创建完成", file=sys.stderr)
31
+
32
+ def _load_config(self):
33
+ """加载配置文件"""
34
+ try:
35
+ self._script_path = os.path.abspath(sys.modules[self.__class__.__module__].__file__)
36
+ self.base_path = sys.argv[2] if len(sys.argv) > 2 else os.path.expanduser("~/.ryry")
37
+ config_path = os.path.join(os.path.dirname(self._script_path), "config.json")
38
+
39
+ if os.path.exists(config_path):
40
+ with open(config_path, 'r', encoding='utf-8') as f:
41
+ config = json.load(f)
42
+ self.timeout = config.get("timeout", 600)
43
+ self.widget_name = config.get("name", self.widget_name)
44
+ else:
45
+ self.timeout = 600
46
+ except Exception as e:
47
+ print(f"配置加载失败: {e}", file=sys.stderr)
48
+ self.timeout = 600
49
+
50
+ def _setup_signal_handlers(self):
51
+ """设置信号处理器"""
52
+ def signal_handler(signum, frame):
53
+ print(f"【{self.widget_name}】收到信号 {signum},开始停止", file=sys.stderr)
54
+ self.running = False
55
+
56
+ # 注册信号处理器
57
+ signal.signal(signal.SIGINT, signal_handler) # Ctrl+C
58
+ signal.signal(signal.SIGTERM, signal_handler) # 终止信号
59
+
60
+ if hasattr(signal, 'SIGBREAK'):
61
+ signal.signal(signal.SIGBREAK, signal_handler)
62
+
63
+ # ==================== 生命周期方法 ====================
64
+
65
+ def initialize(self):
66
+ """
67
+ 初始化方法 - 子类可选实现
68
+ 在这里进行资源初始化,如数据库连接、模型加载等
69
+ """
70
+ pass
71
+
72
+ def loop_function(self):
73
+ """
74
+ 循环执行的方法 - 子类可选实现
75
+ 每秒调用一次,用于定时任务、心跳等
76
+ """
77
+ pass
78
+
79
+ def on_stop(self):
80
+ """
81
+ 停止时的清理方法 - 子类可选实现
82
+ 在这里进行资源清理,如关闭连接、保存状态等
83
+ """
84
+ pass
85
+
86
+ # ==================== 通信方法 ====================
87
+
88
+ def send_ready_signal(self):
89
+ """发送就绪信号"""
90
+ try:
91
+ ready_file = os.path.join(self.base_path, f"daemon_ready_{self.widget_id}.json")
92
+ with open(ready_file, 'w', encoding='utf-8') as f:
93
+ json.dump({"accept_tasks": False}, f)
94
+ except Exception as e:
95
+ print(f"发送就绪信号失败: {e}", file=sys.stderr)
96
+
97
+ def process_command(self):
98
+ """处理命令文件"""
99
+ cmd_file = os.path.join(self.base_path, f"daemon_cmd_{self.widget_id}.json")
100
+ if not os.path.exists(cmd_file):
101
+ return
102
+
103
+ try:
104
+ with open(cmd_file, 'r', encoding='utf-8') as f:
105
+ command = json.load(f)
106
+ os.remove(cmd_file)
107
+
108
+ cmd_type = command.get("type")
109
+ if cmd_type == "task":
110
+ self._handle_task_command(command)
111
+ elif cmd_type == "health":
112
+ self._handle_health_command(command)
113
+ elif cmd_type == "stop":
114
+ self._handle_stop_command(command)
115
+
116
+ except Exception as e:
117
+ print(f"处理命令失败: {e}", file=sys.stderr)
118
+
119
+ def _handle_task_command(self, command: Dict[str, Any]):
120
+ """处理任务命令"""
121
+ task_data = command.get("data", {})
122
+ timeout = command.get("timeout", self.timeout)
123
+
124
+ try:
125
+ result = self.process_task(task_data, timeout)
126
+ self._send_response({
127
+ "type": "task_result",
128
+ "task_id": command.get("task_id"),
129
+ "success": True,
130
+ "data": result
131
+ })
132
+ except Exception as e:
133
+ self._send_response({
134
+ "type": "task_result",
135
+ "task_id": command.get("task_id"),
136
+ "success": False,
137
+ "error": str(e),
138
+ "data": {"result": [], "status": 1, "message": str(e)}
139
+ })
140
+
141
+ def _handle_health_command(self, command: Dict[str, Any]):
142
+ """处理健康检查命令"""
143
+ health = self.health_check()
144
+ self._send_response({
145
+ "type": "health_result",
146
+ "data": health
147
+ })
148
+
149
+ def _handle_stop_command(self, command: Dict[str, Any]):
150
+ """处理停止命令"""
151
+ self.running = False
152
+ self._send_response({
153
+ "type": "stop_result",
154
+ "success": True
155
+ })
156
+
157
+ def _send_response(self, response: Dict[str, Any]):
158
+ """发送响应"""
159
+ try:
160
+ result_file = os.path.join(self.base_path, f"daemon_result_{self.widget_id}.json")
161
+ with open(result_file, 'w', encoding='utf-8') as f:
162
+ json.dump(response, f)
163
+ except Exception as e:
164
+ print(f"发送响应失败: {e}", file=sys.stderr)
165
+
166
+ # ==================== 子类可选重写的方法 ====================
167
+
168
+ def process_task(self, task_data: Dict[str, Any], timeout: Optional[int] = None) -> Dict[str, Any]:
169
+ """
170
+ 处理短链任务 - 子类可选实现
171
+ 默认返回空结果
172
+ """
173
+ return {
174
+ "result": [{"type": "text", "content": [f"Processed: {task_data}"]}],
175
+ "status": 0,
176
+ "message": "Success"
177
+ }
178
+
179
+ def health_check(self) -> Dict[str, Any]:
180
+ """
181
+ 健康检查 - 子类可选重写
182
+ """
183
+ return {
184
+ "healthy": True,
185
+ "state": "running",
186
+ "accept_tasks": False,
187
+ "timestamp": time.time()
188
+ }
189
+
190
+ # ==================== 控制方法 ====================
191
+
192
+ def run(self):
193
+ """
194
+ 运行daemon的主入口方法
195
+ """
196
+ try:
197
+ # 初始化
198
+ self.initialize()
199
+
200
+ # 发送就绪信号
201
+ self.send_ready_signal()
202
+
203
+ # 主循环
204
+ self.running = True
205
+ while self.running:
206
+ try:
207
+ # 处理命令
208
+ self.process_command()
209
+ # 执行循环函数
210
+ self.loop_function()
211
+ time.sleep(1)
212
+ except KeyboardInterrupt:
213
+ print(f"【{self.widget_name}】收到KeyboardInterrupt", file=sys.stderr)
214
+ break
215
+ except Exception as e:
216
+ print(f"主循环异常: {e}", file=sys.stderr)
217
+ time.sleep(5) # 异常后等待5秒再继续
218
+ # 清理
219
+ self.on_stop()
220
+
221
+ except Exception as e:
222
+ print(f"运行异常: {e}", file=sys.stderr)
223
+ finally:
224
+ print(f"【{self.widget_name}】进程终止", file=sys.stderr)
@@ -79,10 +79,11 @@ class Task:
79
79
  if realTaskUUID == None or len(realTaskUUID) <= 0:
80
80
  realTaskUUID = taskUtils.taskInfoWithFirstTask()
81
81
 
82
- support_subdomain = upload.getFirstSupportSubdomain()
83
82
  mayby_domain = ""
84
- if support_subdomain:
85
- mayby_domain = support_subdomain["readpath"]
83
+ if "domain" not in multi_params[0]:
84
+ support_subdomain = upload.getFirstSupportSubdomain()
85
+ if support_subdomain:
86
+ mayby_domain = support_subdomain["domain"]
86
87
 
87
88
  def _callback(idx, data):
88
89
  self.thread_data[str(idx)]["result"] = data
@@ -118,12 +119,11 @@ class AsyncTask:
118
119
  if realTaskUUID == None or len(realTaskUUID) <= 0:
119
120
  realTaskUUID = taskUtils.taskInfoWithFirstTask()
120
121
 
121
- support_subdomain = upload.getFirstSupportSubdomain()
122
- domain = ""
123
- if support_subdomain:
124
- domain = support_subdomain["readpath"]
125
122
  self.params["fromUUID"] = realTaskUUID
126
- self.params["domain"] = domain
123
+ if "domain" not in self.params:
124
+ support_subdomain = upload.getFirstSupportSubdomain()
125
+ if support_subdomain:
126
+ self.params["domain"] = support_subdomain["domain"]
127
127
 
128
128
  def call(self):
129
129
  cmd = task.cmdWithWidgetName(self.func)
@@ -46,12 +46,17 @@ def additionalUrl(srcFile, ossUrl):
46
46
  def upload(src, taskUUID=None, needTranscode=True, keepItAlways=False, additionalUrl=False):
47
47
  import os
48
48
  from pathlib import Path
49
- from ryry import taskUtils
49
+ from ryry import taskUtils as ryry_taskUtils
50
50
  if os.path.exists(src) == False:
51
51
  raise Exception(f"upload file not found")
52
52
  if taskUUID == None or len(taskUUID) <= 0:
53
- taskUUID = taskUtils.taskInfoWithFirstTask()
54
- targetDomain = taskUtils.taskDomainWithUUID(taskUUID)
53
+ taskUUID = ryry_taskUtils.taskInfoWithFirstTask()
54
+ targetDomain = ryry_taskUtils.taskDomainWithUUID(taskUUID)
55
+ else:
56
+ targetDomain = ryry_taskUtils.taskDomainWithUUID(taskUUID)
57
+ if targetDomain == None or len(targetDomain) <= 0:
58
+ from mecord import taskUtils as mecord_taskUtils
59
+ targetDomain = mecord_taskUtils.taskDomainWithUUID(taskUUID)
55
60
 
56
61
  if needTranscode:
57
62
  needDeleteSrc, newSrc = transcode(src)
@@ -60,7 +65,7 @@ def upload(src, taskUUID=None, needTranscode=True, keepItAlways=False, additiona
60
65
  newSrc = src
61
66
 
62
67
  # 根据targetDomain判断使用不同的上传逻辑
63
- ossurl = uploadByDomain(newSrc, targetDomain, keepItAlways)
68
+ ossurl = uploadByDomain(newSrc, targetDomain, taskUUID, keepItAlways)
64
69
 
65
70
  if additionalUrl:
66
71
  ossurl = additionalUrl(newSrc, ossurl)
@@ -68,7 +73,7 @@ def upload(src, taskUUID=None, needTranscode=True, keepItAlways=False, additiona
68
73
  os.remove(newSrc)
69
74
  return ossurl
70
75
 
71
- def uploadByDomain(src, targetDomain, keepItAlways=False):
76
+ def uploadByDomain(src, targetDomain, taskUUID, keepItAlways=False):
72
77
  import os
73
78
  from pathlib import Path
74
79
  import uuid
@@ -79,6 +84,10 @@ def uploadByDomain(src, targetDomain, keepItAlways=False):
79
84
  new_file_name = ''.join(str(uuid.uuid4()).split('-'))
80
85
 
81
86
  parsed_domain, upload_path = parseDomainAndPath(targetDomain)
87
+ if isMecordConfig(parsed_domain):
88
+ from mecord import upload as mecord_upload
89
+ return mecord_upload.upload(src, taskUUID)
90
+
82
91
  oss_config = getOssConfig(parsed_domain)
83
92
  if oss_config:
84
93
  return uploadToOss(src, f"{new_file_name}.{ext}", oss_config, upload_path)
@@ -239,14 +248,34 @@ oss_template_config = {
239
248
  "domain": "https://oss.zjtemplate.com"
240
249
  }
241
250
 
251
+ def match_wildcard_domain(target_domain, pattern):
252
+ if pattern.startswith("*."):
253
+ suffix = pattern[1:] # 去掉 "*." 前缀
254
+ return target_domain.endswith(suffix)
255
+ return target_domain == pattern
256
+
257
+ def get_domain_config(target_domain):
258
+ # 首先尝试精确匹配
259
+ if target_domain in DOMAIN_CONFIG:
260
+ return DOMAIN_CONFIG[target_domain]
261
+
262
+ # 然后尝试通配符匹配
263
+ for pattern, config in DOMAIN_CONFIG.items():
264
+ if match_wildcard_domain(target_domain, pattern):
265
+ return config
266
+
267
+ return None
268
+
242
269
  DOMAIN_CONFIG = {
270
+ "192.168.50.12": {"type": "ftp", "config": ftp_192_168_50_12},
271
+ "183.6.90.205": {"type": "ftp", "config": ftp_183_6_90_205},
272
+ "ftp://192.168.50.12/mnt/NAS/mcn/cache": {"type": "ftp", "config": ftp_192_168_50_12},
273
+ "ftp://183.6.90.205/mnt/NAS/mcn/cache": {"type": "ftp", "config": ftp_183_6_90_205},
243
274
  "res.zjtemplate.com": {"type": "oss", "config": oss_res_config},
244
275
  "upload.zjtemplate.com": {"type": "oss", "config": oss_upload_config},
245
276
  "oss.zjtemplate.com": {"type": "oss", "config": oss_template_config},
246
- "ftp://192.168.50.12/mnt/NAS/mcn/cache": {"type": "ftp", "config": ftp_192_168_50_12},
247
- "ftp://183.6.90.205/mnt/NAS/mcn/cache": {"type": "ftp", "config": ftp_183_6_90_205},
248
- "192.168.50.12": {"type": "ftp", "config": ftp_192_168_50_12},
249
- "183.6.90.205": {"type": "ftp", "config": ftp_183_6_90_205},
277
+ "*.mecoai.cn": {"type": "mecord-cli", "config": {}},
278
+ "*.mecordai.com": {"type": "mecord-cli", "config": {}},
250
279
  }
251
280
 
252
281
  SUBDOMAIN = {
@@ -261,13 +290,21 @@ SUBDOMAIN = {
261
290
  }
262
291
 
263
292
  def getOssConfig(targetDomain):
264
- if targetDomain in DOMAIN_CONFIG and DOMAIN_CONFIG[targetDomain]["type"] == "oss":
265
- return DOMAIN_CONFIG[targetDomain]["config"]
293
+ config = get_domain_config(targetDomain)
294
+ if config and config["type"] == "oss":
295
+ return config["config"]
266
296
  return None
267
297
 
298
+ def isMecordConfig(targetDomain):
299
+ config = get_domain_config(targetDomain)
300
+ if config and config["type"] == "mecord-cli":
301
+ return True
302
+ return False
303
+
268
304
  def getFtpConfig(targetDomain):
269
- if targetDomain in DOMAIN_CONFIG and DOMAIN_CONFIG[targetDomain]["type"] == "ftp":
270
- return DOMAIN_CONFIG[targetDomain]["config"]
305
+ config = get_domain_config(targetDomain)
306
+ if config and config["type"] == "ftp":
307
+ return config["config"]
271
308
  return None
272
309
 
273
310
  def getSubdomain(targetDomain):
@@ -299,11 +336,19 @@ def getFirstSupportSubdomain():
299
336
  def _getFirstSupportSubdomain():
300
337
  for domain, config in DOMAIN_CONFIG.items():
301
338
  if config["type"] == "oss":
302
- return {"type": "oss", "config": config["config"], "domain": domain}
339
+ return {"type": "oss", "config": config["config"], "domain": config["config"]["domain"]}
340
+ if config["type"] == "mecord-cli":
341
+ # 对于通配符域名,返回一个示例域名
342
+ if domain.startswith("*."):
343
+ # 将 *.mecoai.cn 转换为 example.mecoai.cn
344
+ example_domain = "example" + domain[1:]
345
+ return {"type": "mecord-cli", "config": {}, "domain": example_domain}
346
+ else:
347
+ return {"type": "mecord-cli", "config": {}, "domain": domain}
303
348
  elif config["type"] == "ftp":
304
349
  ftp_config = config["config"]
305
350
  if _can_connect_ftp(ftp_config["host"], ftp_config["port"], ftp_config["username"], ftp_config["password"], ftp_config["writepath"]):
306
- return {"type": "ftp", "config": ftp_config, "domain": domain}
351
+ return {"type": "ftp", "config": ftp_config, "domain": ftp_config["readpath"]}
307
352
 
308
353
  for ip in SUBDOMAIN.keys():
309
354
  for host_item in SUBDOMAIN[ip]:
@@ -322,7 +367,8 @@ def getFirstSupportSubdomain():
322
367
  sp.write(read_data)
323
368
  else:
324
369
  last_network_hash = firstSupportDomainConfig.get("hash", "")
325
- if last_network_hash != network_hash:
370
+ #网络变化,或者旧版本数据的时候,重新更新
371
+ if last_network_hash != network_hash or "domain" not in firstSupportDomainConfig:
326
372
  firstSupportDomainConfig = _getFirstSupportSubdomain()
327
373
  if firstSupportDomainConfig:
328
374
  firstSupportDomainConfig["hash"] = network_hash
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ryry-cli
3
- Version: 4.2
3
+ Version: 4.4
4
4
  Summary: ryry tools
5
5
  Home-page: https://github.com/dalipenMedia
6
6
  Author: dalipen
@@ -23,6 +23,7 @@ Requires-Dist: gputil
23
23
  Requires-Dist: urlparser
24
24
  Requires-Dist: urllib3
25
25
  Requires-Dist: portalocker
26
+ Requires-Dist: mecord-cli>=0.7.405
26
27
 
27
28
  ryry Python Tool
28
29
  ===============================================
@@ -10,3 +10,4 @@ gputil
10
10
  urlparser
11
11
  urllib3
12
12
  portalocker
13
+ mecord-cli>=0.7.405
@@ -3,7 +3,7 @@ import os
3
3
  import subprocess
4
4
  import datetime
5
5
 
6
- ryry_version = "4.2"
6
+ ryry_version = "4.4"
7
7
  cur_dir = os.path.dirname(os.path.abspath(__file__))
8
8
  constanspy = os.path.join(cur_dir, "ryry", "constant.py")
9
9
  try:
@@ -70,6 +70,7 @@ setuptools.setup(
70
70
  'urlparser',
71
71
  'urllib3',
72
72
  'portalocker',
73
+ 'mecord-cli>=0.7.405',
73
74
  ],
74
75
  dependency_links=[],
75
76
  entry_points={
@@ -1,131 +0,0 @@
1
- import sys
2
- import os
3
- import json
4
- import time
5
- from threading import Event
6
-
7
- DEFAULT_ACCEPT_TASKS = False # 默认是否接受短链任务
8
-
9
- class DaemonBase:
10
- def __init__(self, widget_id: str):
11
- self.widget_id = widget_id
12
- self.running = True
13
- self.stop_event = Event()
14
- self.accept_tasks = self.default_accept_tasks()
15
- self.widget_name = "???"
16
- # 获取子类文件(即实际widget的daemon.py)所在目录
17
- self._script_path = os.path.abspath(sys.modules[self.__class__.__module__].__file__)
18
- self.base_path = sys.argv[2] if len(sys.argv) > 2 else os.path.expanduser("~/.ryry")
19
- config_path = os.path.join(os.path.dirname(self._script_path), "config.json")
20
- if os.path.exists(config_path):
21
- try:
22
- with open(config_path, 'r', encoding='utf-8') as f:
23
- config = json.load(f)
24
- self.timeout = config.get("timeout", 600)
25
- self.widget_name = config.get("name", self.widget_name)
26
- except:
27
- self.timeout = 600
28
- else:
29
- self.timeout = 600
30
-
31
- def default_accept_tasks(self):
32
- return DEFAULT_ACCEPT_TASKS
33
-
34
- def initialize(self):
35
- pass
36
-
37
- def process_task(self, task_data, timeout=None):
38
- raise NotImplementedError("process_task必须由子类实现")
39
-
40
- def on_stop(self):
41
- pass
42
-
43
- def health_check(self):
44
- return {
45
- "healthy": True,
46
- "accept_tasks": self.accept_tasks,
47
- "timestamp": time.time()
48
- }
49
-
50
- def loop_function(self):
51
- pass
52
-
53
- def _send_ready_signal(self):
54
- try:
55
- ready_file = os.path.join(self.base_path, f"daemon_ready_{self.widget_id}.json")
56
- with open(ready_file, 'w', encoding='utf-8') as f:
57
- json.dump({"accept_tasks": self.accept_tasks}, f)
58
- except Exception as e:
59
- print(f"Failed to send ready signal: {e}", file=sys.stderr)
60
-
61
- def _send_response(self, response):
62
- try:
63
- result_file = os.path.join(self.base_path, f"daemon_result_{self.widget_id}.json")
64
- with open(result_file, 'w', encoding='utf-8') as f:
65
- json.dump(response, f)
66
- except Exception as e:
67
- print(f"Failed to send response: {e}", file=sys.stderr)
68
-
69
- def _process_command(self, command):
70
- cmd_type = command.get("type")
71
- if cmd_type == "task":
72
- task_data = command.get("data", {})
73
- timeout = command.get("timeout", self.timeout)
74
- try:
75
- result = self.process_task(task_data, timeout)
76
- self._send_response({
77
- "type": "task_result",
78
- "task_id": command.get("task_id"),
79
- "success": True,
80
- "data": result
81
- })
82
- except Exception as e:
83
- self._send_response({
84
- "type": "task_result",
85
- "task_id": command.get("task_id"),
86
- "success": False,
87
- "error": str(e),
88
- "data": {"result": [], "status": 1, "message": str(e)}
89
- })
90
- elif cmd_type == "health":
91
- health = self.health_check()
92
- self._send_response({
93
- "type": "health_result",
94
- "data": health
95
- })
96
- elif cmd_type == "stop":
97
- print(f"【{self.widget_name}后台进程】Received stop command", file=sys.stderr)
98
- self.running = False
99
- self.stop_event.set()
100
- self._send_response({
101
- "type": "stop_result",
102
- "success": True
103
- })
104
-
105
- def run(self):
106
- try:
107
- self.initialize()
108
- self._send_ready_signal()
109
- cmd_file = os.path.join(self.base_path, f"daemon_cmd_{self.widget_id}.json")
110
- while self.running:
111
- try:
112
- if os.path.exists(cmd_file):
113
- try:
114
- with open(cmd_file, 'r', encoding='utf-8') as f:
115
- command = json.load(f)
116
- os.remove(cmd_file)
117
- self._process_command(command)
118
- except Exception as e:
119
- print(f"Error processing command: {e}", file=sys.stderr)
120
- if self.stop_event.is_set():
121
- break
122
- self.loop_function()
123
- time.sleep(1)
124
- except KeyboardInterrupt:
125
- break
126
- except Exception as e:
127
- print(f"Error in main loop: {e}", file=sys.stderr)
128
- time.sleep(1)
129
- finally:
130
- self.on_stop()
131
- print(f"【{self.widget_name}】已终止", file=sys.stderr)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes