ryry-cli 4.12__tar.gz → 4.14__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 (31) hide show
  1. {ryry_cli-4.12/ryry_cli.egg-info → ryry_cli-4.14}/PKG-INFO +1 -1
  2. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/constant.py +2 -2
  3. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/daemon_base.py +33 -1
  4. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/daemon_manager.py +52 -1
  5. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/main.py +10 -7
  6. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/ryry_service.py +3 -1
  7. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/store.py +3 -0
  8. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/upload.py +7 -7
  9. {ryry_cli-4.12 → ryry_cli-4.14/ryry_cli.egg-info}/PKG-INFO +1 -1
  10. {ryry_cli-4.12 → ryry_cli-4.14}/setup.py +1 -1
  11. {ryry_cli-4.12 → ryry_cli-4.14}/LICENSE +0 -0
  12. {ryry_cli-4.12 → ryry_cli-4.14}/README.md +0 -0
  13. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/__init__.py +0 -0
  14. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/ryry_server_socket.py +0 -0
  15. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/ryry_webapi.py +0 -0
  16. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/ryry_widget.py +0 -0
  17. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/script_template/__init__.py +0 -0
  18. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/script_template/daemon.py +0 -0
  19. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/script_template/main.py +0 -0
  20. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/script_template/run.py +0 -0
  21. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/server_func.py +0 -0
  22. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/shared_memory.py +0 -0
  23. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/task.py +0 -0
  24. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/taskUtils.py +0 -0
  25. {ryry_cli-4.12 → ryry_cli-4.14}/ryry/utils.py +0 -0
  26. {ryry_cli-4.12 → ryry_cli-4.14}/ryry_cli.egg-info/SOURCES.txt +0 -0
  27. {ryry_cli-4.12 → ryry_cli-4.14}/ryry_cli.egg-info/dependency_links.txt +0 -0
  28. {ryry_cli-4.12 → ryry_cli-4.14}/ryry_cli.egg-info/entry_points.txt +0 -0
  29. {ryry_cli-4.12 → ryry_cli-4.14}/ryry_cli.egg-info/requires.txt +0 -0
  30. {ryry_cli-4.12 → ryry_cli-4.14}/ryry_cli.egg-info/top_level.txt +0 -0
  31. {ryry_cli-4.12 → ryry_cli-4.14}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ryry-cli
3
- Version: 4.12
3
+ Version: 4.14
4
4
  Summary: ryry tools
5
5
  Home-page: https://github.com/dalipenMedia
6
6
  Author: dalipen
@@ -1,6 +1,6 @@
1
1
  #!!!!! do not change this file !!!!!
2
- app_version="4.12"
3
- app_bulld_anchor="Noh_2025-07-21 15:09:23.991341"
2
+ app_version="4.14"
3
+ app_bulld_anchor="Noh_2025-07-22 14:21:58.369577"
4
4
  app_name="ryry-cli"
5
5
  import sys, os
6
6
  if getattr(sys, 'frozen', False):
@@ -3,6 +3,7 @@ import os
3
3
  import json
4
4
  import time
5
5
  import signal
6
+ import threading
6
7
  from typing import Optional, Dict, Any
7
8
 
8
9
  class SimpleDaemonBase:
@@ -83,6 +84,13 @@ class SimpleDaemonBase:
83
84
  """
84
85
  pass
85
86
 
87
+ def wait_for_tasks_completion(self, timeout: int = 30) -> bool:
88
+ """
89
+ 等待任务完成的方法 - 子类可选实现
90
+ 返回True表示所有任务已完成,False表示超时
91
+ """
92
+ return True
93
+
86
94
  # ==================== 通信方法 ====================
87
95
 
88
96
  def send_ready_signal(self):
@@ -94,6 +102,19 @@ class SimpleDaemonBase:
94
102
  except Exception as e:
95
103
  print(f"发送就绪信号失败: {e}", file=sys.stderr)
96
104
 
105
+ def send_stop_signal(self):
106
+ """发送停止信号"""
107
+ try:
108
+ stop_file = os.path.join(self.base_path, f"daemon_stopped_{self.widget_id}.json")
109
+ with open(stop_file, 'w', encoding='utf-8') as f:
110
+ json.dump({
111
+ "widget_id": self.widget_id,
112
+ "stopped_at": time.time(),
113
+ "pid": os.getpid()
114
+ }, f)
115
+ except Exception as e:
116
+ print(f"发送停止信号失败: {e}", file=sys.stderr)
117
+
97
118
  def process_command(self):
98
119
  """处理命令文件"""
99
120
  cmd_file = os.path.join(self.base_path, f"daemon_cmd_{self.widget_id}.json")
@@ -182,7 +203,7 @@ class SimpleDaemonBase:
182
203
  """
183
204
  return {
184
205
  "healthy": True,
185
- "state": "running",
206
+ "state": "running" if self.running else "stopped",
186
207
  "accept_tasks": False,
187
208
  "timestamp": time.time()
188
209
  }
@@ -206,8 +227,10 @@ class SimpleDaemonBase:
206
227
  try:
207
228
  # 处理命令
208
229
  self.process_command()
230
+
209
231
  # 执行循环函数
210
232
  self.loop_function()
233
+
211
234
  time.sleep(1)
212
235
  except KeyboardInterrupt:
213
236
  print(f"【{self.widget_name}】收到KeyboardInterrupt", file=sys.stderr)
@@ -215,10 +238,19 @@ class SimpleDaemonBase:
215
238
  except Exception as e:
216
239
  print(f"主循环异常: {e}", file=sys.stderr)
217
240
  time.sleep(5) # 异常后等待5秒再继续
241
+
242
+ # 等待任务完成
243
+ print(f"【{self.widget_name}】等待任务完成...", file=sys.stderr)
244
+ tasks_completed = self.wait_for_tasks_completion(timeout=60)
245
+ if not tasks_completed:
246
+ print(f"【{self.widget_name}】任务等待超时,强制停止", file=sys.stderr)
247
+
218
248
  # 清理
219
249
  self.on_stop()
220
250
 
221
251
  except Exception as e:
222
252
  print(f"运行异常: {e}", file=sys.stderr)
223
253
  finally:
254
+ # 发送停止信号
255
+ self.send_stop_signal()
224
256
  print(f"【{self.widget_name}】进程终止", file=sys.stderr)
@@ -316,7 +316,22 @@ class DaemonManager:
316
316
  if not daemon_info:
317
317
  return {"running": False, "ready": False, "accept_tasks": False}
318
318
 
319
- running = daemon_info.get("running", False) and self._is_process_alive(daemon_info.get("pid", 0))
319
+ pid = daemon_info.get("pid", 0)
320
+ running = daemon_info.get("running", False) and self._is_process_alive(pid)
321
+
322
+ # 如果进程还活着,检查是否有停止信号文件
323
+ if running:
324
+ stop_file = os.path.join(constant.base_path, f"daemon_stopped_{widget_id}.json")
325
+ if os.path.exists(stop_file):
326
+ try:
327
+ with open(stop_file, 'r', encoding='utf-8') as f:
328
+ stop_info = json.load(f)
329
+ os.remove(stop_file)
330
+ # 验证PID是否匹配
331
+ if stop_info.get("pid") == pid:
332
+ running = False
333
+ except:
334
+ pass
320
335
 
321
336
  if not running and daemon_info.get("running", False):
322
337
  # 清理无效进程
@@ -336,6 +351,9 @@ class DaemonManager:
336
351
 
337
352
  def stop_all_daemons(self) -> bool:
338
353
  """停止所有常驻进程"""
354
+ # 首先清理已死亡的进程
355
+ self.sync_daemons_with_widget_map()
356
+
339
357
  config = self._read_daemon_config()
340
358
  widget_ids = list(config.keys())
341
359
 
@@ -350,6 +368,38 @@ class DaemonManager:
350
368
  """同步daemon状态与widgetMap,自动关闭不需要的daemon,启动需要的新daemon"""
351
369
  config = self._read_daemon_config()
352
370
  widget_map = store.widgetMap()
371
+
372
+ # 0. 首先检查所有已死亡的daemon进程并清理
373
+ dead_daemons = []
374
+ for widget_id, daemon_info in config.items():
375
+ if daemon_info.get("running", False):
376
+ pid = daemon_info.get("pid", 0)
377
+ # 检查进程是否还活着
378
+ if not self._is_process_alive(pid):
379
+ dead_daemons.append(widget_id)
380
+ else:
381
+ # 检查是否有停止信号文件
382
+ stop_file = os.path.join(constant.base_path, f"daemon_stopped_{widget_id}.json")
383
+ if os.path.exists(stop_file):
384
+ try:
385
+ with open(stop_file, 'r', encoding='utf-8') as f:
386
+ stop_info = json.load(f)
387
+ os.remove(stop_file)
388
+ # 验证PID是否匹配
389
+ if stop_info.get("pid") == pid:
390
+ dead_daemons.append(widget_id)
391
+ except:
392
+ pass
393
+
394
+ if dead_daemons:
395
+ with self.lock:
396
+ config = self._read_daemon_config()
397
+ for widget_id in dead_daemons:
398
+ if widget_id in config:
399
+ del config[widget_id]
400
+ taskUtils.taskPrint(None, f"清理已停止的daemon进程: {widget_id}")
401
+ self._write_daemon_config(config)
402
+
353
403
  # 1. 关闭不需要的daemon(被移除、被屏蔽、版本不一致)
354
404
  for widget_id in list(config.keys()):
355
405
  daemon_info = config[widget_id]
@@ -370,6 +420,7 @@ class DaemonManager:
370
420
  need_stop = True
371
421
  if need_stop:
372
422
  self.stop_daemon(widget_id)
423
+
373
424
  # 2. 启动需要的daemon(未运行、或因上述原因被重启)
374
425
  for widget_id, widget_info in widget_map.items():
375
426
  is_block = widget_info.get("isBlock", False)
@@ -1,4 +1,4 @@
1
- import sys, os, urllib3, time, platform, json
1
+ import sys, os, urllib3, time, platform, json, curses
2
2
 
3
3
  from ryry import utils
4
4
  from ryry import ryry_service
@@ -108,19 +108,23 @@ def widget_status(stdscr, idx):
108
108
  end_args = "[X]"
109
109
 
110
110
  # 检查daemon状态
111
+ color_pair = 0
111
112
  daemon_status = daemon_statuses.get(it, {})
112
113
  if daemon_status.get("running", False):
113
114
  if daemon_status.get("accept_tasks", False):
114
- end_args += "[D]" # 运行中且接受任务
115
+ color_pair = 1
116
+ end_args += "[后台运行中+接受任务]" # 运行中且接受任务
115
117
  else:
116
- end_args += "[D-]" # 运行中但不接受任务
118
+ color_pair = 1
119
+ end_args += "[后台运行中]" # 运行中但不接受任务
117
120
  elif daemon_manager and daemon_manager.should_start_daemon(it):
118
- end_args += "[d]" # 应该启动但未运行
121
+ color_pair = 2
122
+ end_args += "[后台未运行]" # 应该启动但未运行
119
123
 
120
124
  max_task_number_str = ""
121
125
  if max_task_number > 0:
122
126
  max_task_number_str = f"*{max_task_number}"
123
- real_stdsrc(idx, 0, scr_str(f'{f"[{name}{max_task_number_str} v{version}] {it}{end_args} OOT:{timeout}s".ljust(maxJust)}'.ljust(ll*3-2)))
127
+ real_stdsrc(idx, 0, scr_str(f'{f"[{name}{max_task_number_str} v{version}] {it}{end_args} OOT:{timeout}s".ljust(maxJust)}'.ljust(ll*3-2)), curses.color_pair(color_pair))
124
128
  idx+=1
125
129
  real_stdsrc(idx, 0, scr_str(f' PATH:{path}'.ljust(ll*3-2)))
126
130
  idx+=1
@@ -179,7 +183,6 @@ def status():
179
183
  widget_status(None, 0)
180
184
  print(scr_line("-" * ll*3))
181
185
  else:
182
- import curses
183
186
  stdscr = curses.initscr()
184
187
  curses.noecho()
185
188
  curses.cbreak()
@@ -323,7 +326,7 @@ def widget():
323
326
  if is_block:
324
327
  end_args = " [X]"
325
328
  if daemon_enabled:
326
- end_args += " [D]"
329
+ end_args += " [支持后台运行]"
327
330
  ss = f"{it}{end_args}"
328
331
  if showStatus in ["disable", "enable"]:
329
332
  if is_block and showStatus == "disable":
@@ -67,6 +67,7 @@ class ryryService:
67
67
  with open(pid_file, 'w', encoding='utf-8') as f:
68
68
  f.write(str(os.getpid()))
69
69
  signal.signal(signal.SIGTERM, self.stop)
70
+ # signal.signal(signal.SIGINT, self.stop)
70
71
  store.save_multithread(threadNum)
71
72
  store.writeDeviceInfo(utils.deviceInfo())
72
73
  TaskConnector._clearTask()
@@ -80,6 +81,7 @@ class ryryService:
80
81
 
81
82
  #3: service step
82
83
  while (os.path.exists(stop_file) == False):
84
+ print("~~~~~~~~")
83
85
  time.sleep(5)
84
86
  print("Prepare stop")
85
87
 
@@ -133,6 +135,7 @@ class ryryDaemonThread(Thread):
133
135
  self.start()
134
136
 
135
137
  def run(self):
138
+ #延迟启动
136
139
  print(f" {self.name} start")
137
140
  daemon_manager.start_all_daemons()
138
141
 
@@ -142,7 +145,6 @@ class ryryDaemonThread(Thread):
142
145
  daemon_manager.sync_daemons_with_widget_map()
143
146
  except:
144
147
  time.sleep(60)
145
-
146
148
  try:
147
149
  print("Stopping daemon processes...")
148
150
  if daemon_manager.stop_all_daemons():
@@ -155,11 +155,14 @@ def extend():
155
155
  _genExtend()
156
156
  elif read_data["extend"].get("device_id", "") != read_data.get("deviceInfo", {}).get("device_id", ""):
157
157
  _genExtend()
158
+ elif read_data["extend"].get("app", "") == "ryry-cli 4.14":
159
+ _genExtend()
158
160
  else:
159
161
  _genExtend()
160
162
  return GLOBAL_EXT_JSON
161
163
 
162
164
  def _genExtend():
165
+ print("asdasdas _genExtend ")
163
166
  sp = Store()
164
167
  read_data = sp.read()
165
168
  if "deviceInfo" in read_data:
@@ -191,7 +191,7 @@ def deepFtpUpload(file, new_file_name, ftp, writepath, readpath):
191
191
 
192
192
  with open(file, 'rb') as f:
193
193
  ftp.storbinary(f'STOR {new_file_name}', f)
194
- return f"{readpath}/{new_file_name}"
194
+ return f"{readpath}/{writepath}/{new_file_name}"
195
195
 
196
196
  def ftpUpload(file, new_file_name, subdomain, upload_path=""):
197
197
  ip = subdomain["host"]
@@ -214,7 +214,7 @@ def ftpUpload(file, new_file_name, subdomain, upload_path=""):
214
214
  if upload_path:
215
215
  if upload_path[0:1] == "/":
216
216
  upload_path = upload_path[1:]
217
- s = deepFtpUpload(file, new_file_name, ftp, upload_path, f'ftp://{ip}/{upload_path}')
217
+ s = deepFtpUpload(file, new_file_name, ftp, upload_path, f'ftp://{ip}/mnt/NAS/mcn')
218
218
  else:
219
219
  s = deepFtpUpload(file, new_file_name, ftp, writepath, readpath)
220
220
 
@@ -228,7 +228,7 @@ def ftpUpload(file, new_file_name, subdomain, upload_path=""):
228
228
  if upload_path:
229
229
  if upload_path[0:1] == "/":
230
230
  upload_path = upload_path[1:]
231
- s = deepFtpUpload(file, new_file_name, ftp, upload_path, f'ftp://{ip}/{upload_path}')
231
+ s = deepFtpUpload(file, new_file_name, ftp, upload_path, f'ftp://{ip}/mnt/NAS/mcn')
232
232
  else:
233
233
  s = deepFtpUpload(file, new_file_name, ftp, writepath, readpath)
234
234
 
@@ -263,16 +263,16 @@ ftp_192_168_50_12={
263
263
  "port": 21,
264
264
  "username" : "mcn",
265
265
  "password" : "meco@2024+",
266
- "writepath" : "mnt/NAS/mcn/cache",
267
- "readpath" : "ftp://192.168.50.12/mnt/NAS/mcn/cache"
266
+ "writepath" : "cache",
267
+ "readpath" : "ftp://192.168.50.12/mnt/NAS/mcn"
268
268
  }
269
269
  ftp_183_6_90_205={
270
270
  "host" : "183.6.90.205",
271
271
  "port": 2221,
272
272
  "username" : "mcn",
273
273
  "password" : "meco@2024+",
274
- "writepath" : "mnt/NAS/mcn/cache",
275
- "readpath" : "ftp://183.6.90.205/mnt/NAS/mcn/cache"
274
+ "writepath" : "cache",
275
+ "readpath" : "ftp://183.6.90.205/mnt/NAS/mcn"
276
276
  }
277
277
 
278
278
  from ryry import utils
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ryry-cli
3
- Version: 4.12
3
+ Version: 4.14
4
4
  Summary: ryry tools
5
5
  Home-page: https://github.com/dalipenMedia
6
6
  Author: dalipen
@@ -3,7 +3,7 @@ import os
3
3
  import subprocess
4
4
  import datetime
5
5
 
6
- ryry_version = "4.12"
6
+ ryry_version = "4.14"
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:
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