Browse Source

推送下载完成
批量拷贝完成

admin 1 year ago
parent
commit
03d06fb1ed
8 changed files with 419 additions and 287 deletions
  1. 11 4
      config/config.ini
  2. 33 12
      src/api/Aria2Monitor.py
  3. 46 144
      src/api/alist.py
  4. 13 18
      src/api/radarr.py
  5. 17 109
      src/main.py
  6. 35 0
      src/services/FileProcessingService.py
  7. 145 0
      src/services/application_service.py
  8. 119 0
      tests/test.py

+ 11 - 4
config/config.ini

@@ -1,25 +1,32 @@
+[COMMON]
+LOG_LEVEL = INFO
+TIMEOUT = 3600
+CHECK_INTERVAL = 10
+REMOTE_DATA=remote.json
+HOME_DATA=home.json
+
 [ARIA2]
 RPC_URL = http://192.168.88.10
 RPC_SECRET = 4e34854d5d7390ef7801
 DESTINATION_PATH = /mnt/data1/downloads/movie
 
-[ALIST]
+[HOME_ALIST]
 API_URL = http://192.168.88.10:5244/api
 WEB_URL = http://192.168.88.10
 CLIENT_ID = 4e34854d5d7390ef7801
 USERNAME = admin
 PASSWORD = nokidc123@#
 DOWNLOAD_PATH = /downloads/movie
-DESTINATION_PATH = /media/sync/movie
+SCY_COPY_PATH = /downloads/movie
+DES_COPY_PATH = /media/sync/movie
 
-[ALIST2]
+[REMOTE_ALIST]
 API_URL = http://box.szfa.xyz:5244/api
 WEB_URL = http://box.szfa.xyz:5244
 CLIENT_ID = 4e34854d5d7390ef7801
 USERNAME = admin
 PASSWORD = nokidc123@#
 DOWNLOAD_PATH = /data/media/moive
-LOCAL_DESTINATION_PATH = /mnt/data1/downloads/movie
 
 [RADAR]
 URL = http://box.szfa.xyz:7878

+ 33 - 12
src/api/monitor_aria2.py → src/api/Aria2Monitor.py

@@ -2,14 +2,45 @@ import configparser
 import logging
 import os
 import shutil
+import threading
 import time
 from aria2p import API, Client
 
 
 class Aria2Monitor:
-    def __init__(self, config):
-        self.aria2_client = Client(config['ARIA2']['RPC_URL'], secret=config['ARIA2']['RPC_SECRET'])
+    def __init__(self, url, secret, file_processing_service):
+        self.aria2_client = Client(url, secret=secret)
         self.aria2_api = API(self.aria2_client)
+        self.is_running = False  # 控制监控循环的标志
+        self.thread = None  # 用于保存监控线程
+        self.file_processing_service = file_processing_service
+
+    def start_monitoring(self):
+        if not self.is_running:
+            self.is_running = True
+            self.thread = threading.Thread(target=self.monitor_aria2)
+            self.thread.start()
+
+    def stop_monitoring(self):
+        self.is_running = False
+        if self.thread:
+            self.thread.join()  # 等待线程完成
+            logging.info("stop thread")
+
+    def monitor_aria2(self):
+        """监控 Aria2 下载完成后,执行自定义操作"""
+        try:
+            while self.is_running:
+                downloads = self.aria2_api.get_downloads()
+                for download in downloads:
+                    if download.is_complete:
+                        if self.file_processing_service:
+                            self.file_processing_service.custom_action(self.aria2_api)
+                time.sleep(10)
+        except Exception as e:
+            logging.error(f"Error occurred: {e}")
+        finally:
+            logging.info("Monitoring completed")
 
     def monitor_and_move_shutil(self, destination_folder, check_interval=10):
         while True:
@@ -39,13 +70,3 @@ class Aria2Monitor:
                         download.remove()
 
             time.sleep(check_interval)
-
-
-# if __name__ == '__main__':
-#     logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
-#
-#     config = configparser.ConfigParser()
-#     config.read('config.ini')
-#
-#     destination_folder = "/media/sync/movie"
-#

+ 46 - 144
src/api/alist.py

@@ -1,12 +1,7 @@
 import json
 import logging
 import os
-import shutil
-import time
-
 import requests
-from aria2p import API, Client
-
 from src.models.task_type import TaskType, ActionType
 
 
@@ -21,15 +16,15 @@ def construct_path(base_path, sub_path, file_name):
 
 
 class AlistAPI:
-    def __init__(self, url, username, password, rpc_url, rpc_secret):
+    def __init__(self, url, username, password):
         self.url = url
         self.headers = {
             'UserAgent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                          'Chrome/87.0.4280.88 Safari/537.36',
             'Content-Type': 'application/json'
         }
-        self.aria2_client = Client(rpc_url, secret=rpc_secret)
-        self.aria2_api = API(self.aria2_client)
+        # self.aria2_client = Client(rpc_url, secret=rpc_secret)
+        # self.aria2_api = API(self.aria2_client)
         self.login(username, password)
 
     def login(self, username, password):
@@ -125,7 +120,9 @@ class AlistAPI:
             "refresh": refresh
         }
         response = requests.post(f"{self.url}/fs/get", data=json.dumps(payload), headers=self.headers)
-        return response.json()
+        if response.status_code == 200:
+            return response.json()
+        return None
 
     def move_file(self, src_dir, dst_dir, names):
         payload = json.dumps({
@@ -161,167 +158,72 @@ class AlistAPI:
         )
         return response.json()
 
-    def save_directory_contents_to_json(self, remote_path, local_base_path=None, original_path=None,
-                                        copy_or_move_destination_path=None,
-                                        json_file_path=''):
-        file_list = self.list_directory(remote_path)
-        if not file_list:
-            return
-
-        directory_contents = self._recursive_collect_contents(remote_path, local_base_path,
-                                                              original_path,
-                                                              copy_or_move_destination_path)
-
-        with open(json_file_path, 'w', encoding='utf-8') as f:
-            json.dump(directory_contents, f, indent=4, ensure_ascii=False)
-
-    def _recursive_collect_contents(self, path, local_base_path, original_path, copy_or_move_destination_path,
-                                    current_sub_path=''):
+    def recursive_collect_contents(self,
+                                   remote_download_path,
+                                   home_download_path,
+                                   src_dir,
+                                   dest_path,
+                                   current_sub_path=''):
         contents = []
-        file_list = self.list_directory(path)
+        file_list = self.list_directory(remote_download_path)
         for file_info in file_list['data']['content']:
             # 拼接完整的远程路径
-            full_path = os.path.join(path, file_info['name']).replace('\\', '/')
+            full_path = os.path.join(remote_download_path, file_info['name']).replace('\\', '/')
 
             # 初始化本地下载路径和复制/移动目的地路径
             local_download_path = ''
-            copy_move_dest_path = ''
-            scy_path = ''
+            new_dest_path = ''
+            new_src_dir = ''
             # 根据条件构建本地下载路径和复制/移动目的地路径
-            if local_base_path is not None:
-                local_download_path = os.path.join(local_base_path, current_sub_path, file_info['name']).replace('\\',
-                                                                                                                 '/')
-            if copy_or_move_destination_path is not None:
-                copy_move_dest_path = os.path.join(copy_or_move_destination_path, current_sub_path).replace('\\', '/')
+            if home_download_path is not None:
+                local_download_path = os.path.join(home_download_path, current_sub_path, file_info['name']).replace(
+                    '\\',
+                    '/')
+            if dest_path is not None:
+                new_dest_path = os.path.join(dest_path, current_sub_path).replace('\\', '/')
 
-            if copy_or_move_destination_path is not None:
-                scy_path = os.path.join(original_path, current_sub_path).replace('\\', '/')
+            if src_dir is not None:
+                new_src_dir = os.path.join(src_dir, current_sub_path).replace('\\', '/')
 
             item = {
                 'name': file_info['name'],
                 'is_dir': file_info['is_dir'],
                 'path': full_path,  # 存储完整的远程路径
                 'downloads_path': local_download_path,
-                'scy_path': scy_path,
-                'copy_des_path': copy_move_dest_path
+                'src_dir': new_src_dir,
+                'dst_dir': new_dest_path
             }
             contents.append(item)
 
             if file_info['is_dir']:
                 # 更新子路径为当前文件夹的路径
                 new_sub_path = os.path.join(current_sub_path, file_info['name'])
-                sub_contents = self._recursive_collect_contents(full_path, local_base_path,
-                                                                original_path,
-                                                                copy_or_move_destination_path, new_sub_path)
+                sub_contents = self.recursive_collect_contents(full_path,
+                                                               home_download_path,
+                                                               src_dir,
+                                                               dest_path,
+                                                               new_sub_path)
                 contents.extend(sub_contents)
 
         return contents
 
-    def download_directory(self, is_debug=False, json_file_path='directory_contents.json'):
-        # 读取 JSON 文件中的目录内容
-        with open(json_file_path, 'r', encoding='utf-8') as f:
+    def copy_files(self, local_json_path, is_debug=False):
+        """执行拷贝文件"""
+        # 读取本地 JSON 文件
+        with open(local_json_path, 'r', encoding='utf-8') as f:
             directory_contents = json.load(f)
 
-        # 获取 Aria2 的所有下载记录
-        downloads = self.aria2_api.get_downloads()
-
-        # 获取已完成下载的文件名列表
-        completed_files = []
-        for download in downloads:
-            if download.is_complete:
-                for file in download.files:
-                    if file.selected:
-                        file_name = os.path.basename(file.path)
-                        completed_files.append(file_name)
-
         for item in directory_contents:
-            if not item['is_dir']:
-                # 构建完整的本地路径
-                # full_local_path = os.path.join(os.path.relpath(item['path'], start="/")).replace('\\', '/')
-                # local_file_path = os.path.join(local_base_path, item['name']).replace('\\', '/')
-                full_local_path = item['downloads_path']
-                local_file = item['name']
-                if local_file not in completed_files:
-                    file_detail = self.get_file_or_directory_info(item['path'])
-                    if file_detail and file_detail['code'] == 200:
-                        raw_url = file_detail['data']['raw_url']
-                        if not is_debug:
-                            # 添加到 Aria2 下载队列
-                            self.aria2_api.add_uris([raw_url], options={"dir": full_local_path})
-                    else:
-                        logging.error(f"Failed to get detailed information for {local_file}")
-                else:
-                    logging.info(f"File already downloaded, skipping: {local_file}")
-
-    def monitor_copy_and_delete(self, local_json_path, destination_path, check_interval=10, is_debug=False,
-                                is_running=True):
-        """监控 Aria2 下载完成后,执行拷贝操作并删除原始文件及历史记录"""
-        try:
-            while is_running:
-                # 读取本地 JSON 文件
-                with open(local_json_path, 'r', encoding='utf-8') as f:
-                    directory_contents = json.load(f)
-
-                downloads = self.aria2_api.get_downloads()
-                for download in downloads:
-                    if download.is_complete:
-                        for file in download.files:
-                            if file.selected:
-                                file_name = os.path.basename(file.path)
-
-                                for item in directory_contents:
-                                    if item['name'] == file_name and not item['is_dir']:
-                                        original_path = item['downloads_path']  # 获取原始文件路径
-
-                                        if is_debug:
-                                            logging.info(f"Debug mode: Copy {file_name}")
-                                        else:
-                                            # 复制文件
-                                            self.copy_file(original_path, destination_path, [file_name])
-                                            logging.info(
-                                                f"Copied: {file_name} from {original_path} to {destination_path}")
-
-                                            # 删除原始文件和 Aria2 历史记录
-                                            self.remove_files_or_folders([file_name], original_path)
-                                            download.remove()
-                                            logging.info(f"Removed: {file_name} from Aria2 and original path")
 
-                time.sleep(check_interval)
-        except Exception as e:
-            logging.error(f"Error occurred: {e}")
-        finally:
-            logging.info("Monitoring, copying, and deleting completed")
-
-    def monitor_and_copy(self, local_json_path, check_interval=10, is_debug=False, is_running=True):
-        """监控 Aria2 下载完成后,执行拷贝操作"""
-        try:
-            while is_running:
-                # 读取本地 JSON 文件
-                with open(local_json_path, 'r', encoding='utf-8') as f:
-                    directory_contents = json.load(f)
-
-                downloads = self.aria2_api.get_downloads()
-                for download in downloads:
-                    if download.is_complete:
-                        for file in download.files:
-                            if file.selected:
-                                file_name = os.path.basename(file.path)
-
-                                for item in directory_contents:
-                                    if item['name'] == file_name and not item['is_dir']:
-                                        original_path = item['scy_path']  # 获取原始文件路径
-                                        des_path = item['copy_des_path']  # 获取原始文件路径
-
-                                        if is_debug:
-                                            logging.info(f"Debug mode: Copy {file_name}")
-                                        else:
-                                            # 复制文件
-                                            self.copy_file(original_path, des_path, [file_name])
-                                            logging.info(
-                                                f"Copied: {file_name} from {original_path} to {des_path}")
+            if not item['is_dir']:
+                file_name = item['name']
+                original_path = item['src_dir']  # 获取原始文件路径
+                des_path = item['dst_dir']  # 获取原始文件路径
 
-                time.sleep(check_interval)
-        except Exception as e:
-            logging.error(f"Error occurred: {e}")
-        finally:
-            logging.info("Monitoring and copying completed")
+                if is_debug:
+                    logging.info(f"Debug mode: Copy {file_name}")
+                else:
+                    # 复制文件
+                    self.copy_file(original_path, des_path, [file_name])
+                    logging.info(
+                        f"Copied: {file_name} from {original_path} to {des_path}")

+ 13 - 18
src/api/radarr.py

@@ -4,10 +4,10 @@ from pyarr import RadarrAPI
 import json
 
 
-class RadarrClient:
-    def __init__(self, config):
-        self.url = config['RADAR']['URL']
-        self.api_key = config['RADAR']['API_KEY']
+class RadarClient:
+    def __init__(self, url, key):
+        self.url = url
+        self.api_key = key
         self.client = RadarrAPI(self.url, self.api_key)
 
     def get_all_movies(self):
@@ -22,6 +22,15 @@ class RadarrClient:
             json.dump(movies, f, indent=4)
         print(f"Movies saved to {file_name}")
 
+    def get_already_movies(self):
+        """获取全部已经跟踪已经处理完成的电影"""
+        already_processed = set()  # 用于跟踪已处理的电影
+        movies = self.get_all_movies()
+        for movie in movies:
+            if 'movieFile' in movie and movie['movieFile'] and movie['id'] not in already_processed:
+                already_processed.add(movie['id'])
+        return already_processed
+
     def continuous_monitoring(self, check_interval=60, custom_action=None):
         """
         Continuously monitor movies and perform a custom action if movieFile exists.
@@ -36,17 +45,3 @@ class RadarrClient:
                     custom_action(movie)
                     already_processed.add(movie['id'])
             time.sleep(check_interval)
-
-    # 示例自定义操作
-
-# def my_custom_action(movie):
-#     print(f"Performing custom action on movie: {movie['title']}")
-#
-#
-# if __name__ == '__main__':
-#     # 使用
-#     radarr_client = RadarrClient("http://box.szfa.xyz:7878", "bd45569f422a4c159600964b7b85a0bd")
-#     # 保存所有电影到JSON文件
-#     radarr_client.save_movies_to_json()
-#     # 监控电影完成
-

+ 17 - 109
src/main.py

@@ -1,122 +1,30 @@
 import configparser
-import json
 import logging
-import signal
-import threading
-import time
-
-from src.api.alist import AlistAPI
-from src.api.radarr import RadarrClient
-
-# 创建一个全局变量,用来表示是否继续监控
-is_running = True
-
-
-# 定义信号处理函数
-def handle_signal(signum, frame):
-    global is_running
-    is_running = False
-    logging.info(f"is run {is_running}")
-
-
-def my_custom_action(movie,
-                     remote_alist_api,
-                     home_alist_api,
-                     remote_download_path,
-                     Local_download_path,
-                     home_download_path,
-                     copy_or_move_download_path,
-                     copy_or_move_destination_path,
-                     ):
-    global is_running
-
-    print(f"Performing custom action on movie: {movie['title']}")
-    # 这里的 movie_path 可能需要根据实际情况调整
-    movie_path = remote_download_path + "/" + movie['title']
-    print(f"movie path -> {movie_path}")
-    # 这里是 home 路径
-    remote_alist_api.save_directory_contents_to_json(remote_download_path, Local_download_path,
-                                                     json_file_path='data/remote.json')
-    logging.info(f" remote save_directory path {remote_download_path} to data/remote.json")
-    # 这里是 home 路径
-    home_alist_api.save_directory_contents_to_json(home_download_path,
-                                                   original_path=copy_or_move_download_path,
-                                                   copy_or_move_destination_path=copy_or_move_destination_path,
-                                                   json_file_path='data/home.json')
-    logging.info(f" remote save_directory path {home_download_path} to data/home.json")
-    # 从服务器上遍历需要下载的数据,推送到本地
-    # remote_alist_api.download_directory(is_debug=True,
-    #                                     json_file_path='data/remote.json')
-
-    home_alist_api.monitor_and_copy('data/home.json', is_running=is_running)
-
-    logging.info("Task completion")
+import os
 
+from src.services.application_service import ApplicationService
 
 if __name__ == '__main__':
-
-    # 注册信号处理函数
-    signal.signal(signal.SIGINT, handle_signal)
-
+    # 初始化日志和配置
     logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
-    config = configparser.ConfigParser()
-    config.read('./config/config.ini')
-    # 创建 AlistAPI 和 RadarrClient 实例
+    # 获取当前脚本的绝对路径
+    current_directory = os.path.dirname(os.path.abspath(__file__))
 
-    # 服务器上的alist,
-    remote_alist_api = AlistAPI(
-        config['ALIST2']['API_URL'],
-        config['ALIST2']['USERNAME'],
-        config['ALIST2']['PASSWORD'],
-        config['ARIA2']['RPC_URL'],
-        config['ARIA2']['RPC_SECRET'],
-    )
+    # 获取当前脚本所在目录的上级目录
+    parent_directory = os.path.dirname(current_directory)
 
-    # 本地的alist,
-    home_alist_api = AlistAPI(
-        config['ALIST']['API_URL'],
-        config['ALIST']['USERNAME'],
-        config['ALIST']['PASSWORD'],
-        config['ARIA2']['RPC_URL'],
-        config['ARIA2']['RPC_SECRET'],
-    )
+    # 构建 config.ini 文件的路径
+    config_path = os.path.join(parent_directory, 'config', 'config.ini')
 
-    radar_client = RadarrClient(config)
-
-    # 从配置文件中读取路径
-    # 服务器下载路径
-    remote_download_path = config['ALIST2']['DOWNLOAD_PATH']
-    logging.info(f"remote download path {remote_download_path}")
-    # Remote to Local
-    Local_download_path = config['ALIST2']['LOCAL_DESTINATION_PATH']
-    logging.info(f"LOCAL download path {Local_download_path}")
-
-    home_download_path = config['ALIST']['DOWNLOAD_PATH']
-    logging.info(f"home download path {home_download_path}")
-    # 拷贝路径
-    copy_or_move_download_path = config['ALIST']['DOWNLOAD_PATH']
-    copy_or_move_destination_path = config['ALIST']['DESTINATION_PATH']
+    # 读取配置文件
+    config = configparser.ConfigParser()
+    config.read(config_path)
 
-    radar_client.save_movies_to_json()
+    # 创建服务实例
+    app_service = ApplicationService(config, parent_directory)
 
     # 开始监控
-    radar_client.continuous_monitoring(
-        custom_action=lambda movie: my_custom_action(movie,
-                                                     remote_alist_api,
-                                                     home_alist_api,
-                                                     remote_download_path,
-                                                     Local_download_path,
-                                                     home_download_path,
-                                                     copy_or_move_download_path,
-                                                     copy_or_move_destination_path
-                                                     )
-    )
-    # 监控本地ar2 下载并复制
-    # home_alist_api.monitor_and_move_or_copy(copy_or_move_download_path, copy_or_move_destination_path, is_debug=False,
-    #                                         is_running=is_running, )
+    app_service.start_task()
 
-    # 主循环
-    while is_running:
-        print("Running...")
-        time.sleep(1)  # 模拟任务执行
-        print("Stopped.")
+    # 执行主循环
+    app_service.main_loop()

+ 35 - 0
src/services/FileProcessingService.py

@@ -0,0 +1,35 @@
+import logging
+import os
+import shutil
+
+
+class FileProcessingService:
+    def __init__(self, home_alist_api, local_json_path):
+        self.local_json_path = local_json_path
+        self.home_alist_api = home_alist_api
+
+    def custom_action(self, aria2_api):
+        downloads = aria2_api.get_downloads()
+        for download in downloads:
+            if download.is_complete:
+                for file in download.files:
+                    if file.selected:
+                        file_name = os.path.basename(file.path)
+                        # 调用 custom_action 并传递 file_name
+                        logging.info(f'start copy file to alist {file_name}')
+
+                        # self.home_alist_api.copy_files(self.local_json_path)
+
+                        # 从 Aria2 中移除已完成的下载记录
+                        # download.remove()
+
+        # source_file = os.path.join(self.download_path, file_name)
+        # destination_file = os.path.join(self.destination_path, file_name)
+        # try:
+        #     if os.path.exists(source_file):
+        #         shutil.move(source_file, destination_file)
+        #         print(f"File moved from {source_file} to {destination_file}")
+        #     else:
+        #         print(f"File not found: {source_file}")
+        # except Exception as e:
+        #     print(f"Error occurred while moving file: {e}")

+ 145 - 0
src/services/application_service.py

@@ -0,0 +1,145 @@
+import json
+import logging
+import os
+import signal
+import time
+from src.api.alist import AlistAPI
+from src.api.Aria2Monitor import Aria2Monitor
+from src.api.radarr import RadarClient
+from src.services.FileProcessingService import FileProcessingService
+
+
+def save_directory_contents_to_json(directory_contents,
+                                    json_file_path=''):
+    with open(json_file_path, 'w', encoding='utf-8') as f:
+        json.dump(directory_contents, f, indent=4, ensure_ascii=False)
+
+
+class ApplicationService:
+    def __init__(self, config, parent_directory):
+        self.file_processing_service = None
+        self.aria2_monitor = None
+        self.home_data = None
+        self.remote_data = None
+        self.des_copy_path = None
+        self.scy_copy_path = None
+        self.rpc_secret = None
+        self.rpc_url = None
+        self.home_download_path = None
+        self.local_download_path = None
+        self.remote_download_path = None
+        self.radar_client = None
+        self.remote_alist_api = None
+        self.home_alist_api = None
+        self.config = config
+        self.parent_dir = parent_directory
+        self.is_running = True
+        self.setup()
+
+        # 注册信号处理函数
+        signal.signal(signal.SIGINT, self.handle_signal)
+
+    def setup(self):
+        self.rpc_url = self.config['ARIA2']['RPC_URL']
+        self.rpc_secret = self.config['ARIA2']['RPC_SECRET']
+
+        # 初始化配置和API实例
+        self.remote_alist_api = AlistAPI(self.config['REMOTE_ALIST']['API_URL'],
+                                         self.config['REMOTE_ALIST']['USERNAME'],
+                                         self.config['REMOTE_ALIST']['PASSWORD'])
+
+        self.home_alist_api = AlistAPI(self.config['HOME_ALIST']['API_URL'],
+                                       self.config['HOME_ALIST']['USERNAME'],
+                                       self.config['HOME_ALIST']['PASSWORD'])
+
+        self.radar_client = RadarClient(self.config['RADAR']['URL'],
+                                        self.config['RADAR']['API_KEY']
+                                        )
+
+        # 读取路径等配置
+        self.remote_download_path = self.config['REMOTE_ALIST']['DOWNLOAD_PATH']
+        self.local_download_path = self.config['ARIA2']['DESTINATION_PATH']
+        self.home_download_path = self.config['HOME_ALIST']['DOWNLOAD_PATH']
+        self.scy_copy_path = self.config['HOME_ALIST']['SCY_COPY_PATH']
+        self.des_copy_path = self.config['HOME_ALIST']['DES_COPY_PATH']
+        self.remote_data = self.config['COMMON']['REMOTE_DATA']
+        self.home_data = self.config['COMMON']['HOME_DATA']
+
+        home_json_path = os.path.join(self.parent_dir, 'data', self.home_data)
+        self.file_processing_service = FileProcessingService(
+            self.home_alist_api,
+            local_json_path=home_json_path,
+        )
+        self.aria2_monitor = Aria2Monitor(self.rpc_url, self.rpc_secret, self.file_processing_service)
+
+    def handle_signal(self, signum, frame):
+        """处理中断信号,优雅地退出循环"""
+        self.is_running = False
+        print("Exiting...")
+
+    def save_directory_contents(self,
+                                remote_download_path,
+                                local_download_path,
+                                scy_copy_path,
+                                des_copy_path,
+                                parent_dir):
+        """获取远程和本地对应的目录结构,并保存为 JSON 文件"""
+        # 获取远程目录结构
+        remote_data = self.remote_alist_api.recursive_collect_contents(
+            remote_download_path, local_download_path, scy_copy_path, des_copy_path
+        )
+        remote_json_path = os.path.join(parent_dir, 'data', 'remote_data.json')
+        with open(remote_json_path, 'w', encoding='utf-8') as f:
+            json.dump(remote_data, f, indent=4)
+
+        # 获取本地目录结构
+        home_data = self.home_alist_api.recursive_collect_contents(
+            scy_copy_path, des_copy_path, scy_copy_path, des_copy_path
+        )
+        home_json_path = os.path.join(parent_dir, 'data', self.home_data)
+        with open(home_json_path, 'w', encoding='utf-8') as f:
+            json.dump(home_data, f, indent=4)
+
+        return remote_data, home_data
+
+    def start_task(self):
+        # 获取远程和本地对应的目录结构
+        remote_json_path, home_json_path = self.save_directory_contents(
+            self.remote_download_path,
+            self.local_download_path,
+            self.scy_copy_path,
+            self.des_copy_path,
+            self.parent_dir,
+        )
+
+        for home in home_json_path:
+            if not home['is_dir']:
+                cent_data = self.home_alist_api.get_file_or_directory_info(home['path'])
+                if cent_data is not None:
+                    json_data = json.dumps(cent_data, indent=4)
+                    logging.info(f"is file info: {json_data}")
+                else:
+                    logging.info(f"file no ")
+
+        # home_data = self
+        # 调用 RadarClient 的监控函数
+        already_movies = self.radar_client.get_already_movies()
+        for movie in already_movies:
+            logging.info(movie)
+
+        self.run()
+
+    def run(self):
+        # 启动监控
+        self.aria2_monitor.start_monitoring()
+
+        # 执行其他任务...
+
+    def main_loop(self):
+        # 主循环
+        while self.is_running:
+            # print("Running...")
+            time.sleep(1)  # 模拟任务执行
+        print("Stopped.")
+        # 停止监控
+        self.aria2_monitor.stop_monitoring()

+ 119 - 0
tests/test.py

@@ -0,0 +1,119 @@
+# def monitor_copy_and_delete(self, local_json_path, destination_path, check_interval=10, is_debug=False,
+#                             is_running=True):
+#     """监控 Aria2 下载完成后,执行拷贝操作并删除原始文件及历史记录"""
+#     try:
+#         while is_running:
+#             # 读取本地 JSON 文件
+#             with open(local_json_path, 'r', encoding='utf-8') as f:
+#                 directory_contents = json.load(f)
+#
+#             downloads = self.aria2_api.get_downloads()
+#             for download in downloads:
+#                 if download.is_complete:
+#                     for file in download.files:
+#                         if file.selected:
+#                             file_name = os.path.basename(file.path)
+#
+#                             for item in directory_contents:
+#                                 if item['name'] == file_name and not item['is_dir']:
+#                                     original_path = item['downloads_path']  # 获取原始文件路径
+#
+#                                     if is_debug:
+#                                         logging.info(f"Debug mode: Copy {file_name}")
+#                                     else:
+#                                         # 复制文件
+#                                         self.copy_file(original_path, destination_path, [file_name])
+#                                         logging.info(
+#                                             f"Copied: {file_name} from {original_path} to {destination_path}")
+#
+#                                         # 删除原始文件和 Aria2 历史记录
+#                                         self.remove_files_or_folders([file_name], original_path)
+#                                         download.remove()
+#                                         logging.info(f"Removed: {file_name} from Aria2 and original path")
+#
+#             time.sleep(check_interval)
+#     except Exception as e:
+#         logging.error(f"Error occurred: {e}")
+#     finally:
+#         logging.info("Monitoring, copying, and deleting completed")
+import json
+import logging
+import os
+
+
+def download_directory(self, is_debug=False, json_file_path='directory_contents.json'):
+    # 读取 JSON 文件中的目录内容
+    with open(json_file_path, 'r', encoding='utf-8') as f:
+        directory_contents = json.load(f)
+
+    # 获取 Aria2 的所有下载记录
+    downloads = self.aria2_api.get_downloads()
+
+    # 获取已完成下载的文件名列表
+    completed_files = []
+    for download in downloads:
+        if download.is_complete:
+            for file in download.files:
+                if file.selected:
+                    file_name = os.path.basename(file.path)
+                    completed_files.append(file_name)
+
+    for item in directory_contents:
+        if not item['is_dir']:
+            # 构建完整的本地路径
+            # full_local_path = os.path.join(os.path.relpath(item['path'], start="/")).replace('\\', '/')
+            # local_file_path = os.path.join(local_base_path, item['name']).replace('\\', '/')
+            full_local_path = item['downloads_path']
+            local_file = item['name']
+            if local_file not in completed_files:
+                file_detail = self.get_file_or_directory_info(item['path'])
+                if file_detail and file_detail['code'] == 200:
+                    raw_url = file_detail['data']['raw_url']
+                    if not is_debug:
+                        # 添加到 Aria2 下载队列
+                        self.aria2_api.add_uris([raw_url], options={"dir": full_local_path})
+                else:
+                    logging.error(f"Failed to get detailed information for {local_file}")
+            else:
+                logging.info(f"File already downloaded, skipping: {local_file}")
+
+
+
+# 创建一个全局变量,用来表示是否继续监控
+is_running = True
+
+
+# 定义信号处理函数
+# def handle_signal(signum, frame):
+#     global is_running
+#     is_running = False
+#     logging.info(f"is run {is_running}")
+
+
+# def my_custom_action(movie,
+#                      remote_alist_api,
+#                      home_alist_api,
+#                      remote_download_path,
+#                      Local_download_path,
+#                      home_download_path,
+#                      copy_or_move_download_path,
+#                      copy_or_move_destination_path,
+#                      ):
+#     global is_running
+#
+#     print(f"Performing custom action on movie: {movie['title']}")
+#     # 这里的 movie_path 可能需要根据实际情况调整
+#     movie_path = remote_download_path + "/" + movie['title']
+#     print(f"movie path -> {movie_path}")
+#     # 这里是 home 路径
+#     remote_alist_api.save_directory_contents_to_json(remote_download_path, Local_download_path,
+#                                                      json_file_path='data/remote.json')
+#     logging.info(f" remote save_directory path {remote_download_path} to data/remote.json")
+#     # 这里是 home 路径
+#     home_alist_api.save_directory_contents_to_json(home_download_path,
+#                                                    original_path=copy_or_move_download_path,
+#                                                    copy_or_move_destination_path=copy_or_move_destination_path,
+#                                                    json_file_path='data/home.json')
+#     logging.info(f" remote save_directory path {home_download_path} to data/home.json")
+#
+#     logging.info("Task completion")