alroyso 1 year ago
parent
commit
64ad680a62

+ 39 - 0
app_arai2.py

@@ -0,0 +1,39 @@
+import argparse
+import configparser
+import logging
+import os
+
+from src.app.application import Application
+
+
+if __name__ == '__main__':
+    # parser = argparse.ArgumentParser(description='Aria2 and Alist Integration Service')
+    # parser.add_argument('--aria2', action='store_true', help='Run Aria2 service only')
+    # args = parser.parse_args()
+
+    # 初始化日志和配置
+    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
+    # 获取当前脚本的绝对路径
+    current_directory = os.path.dirname(os.path.abspath(__file__))
+    logging.info(f"current_dir -> {current_directory} ")
+    # 获取当前脚本所在目录的上级目录
+    # parent_directory = os.path.dirname(current_directory)
+
+    # 构建 config.ini 文件的路径
+    config_path = os.path.join(current_directory, 'config', 'config.ini')
+
+    # 读取配置文件
+    config = configparser.ConfigParser()
+    config.read(config_path)
+
+    logging.info(f"config_paht: {config_path}")
+    logging.info(f"config_data: {config['ARIA2']}")
+
+    # 创建服务实例
+    app_service = Application(config, current_directory, remote_download_service=False)
+
+    # task
+    app_service.start_aria2_monitoring()
+
+    # 执行主循环
+    app_service.main_loop()

+ 7 - 5
config/config.ini

@@ -6,13 +6,15 @@ 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
+RPC_URL = http://192.168.88.29
+RPC_SECRET = 123456
+DOCKER_DOWNLOAD_PATH = /downloads
+DESTINATION_PATH = /home/downloads/aria2
+DES_COPY_PATH = /home/video/sync
 
 [HOME_ALIST]
-API_URL = http://192.168.88.10:5244/api
-WEB_URL = http://192.168.88.10
+API_URL = http://192.168.88.29:5244/api
+WEB_URL = http://192.168.88.29
 CLIENT_ID = 4e34854d5d7390ef7801
 USERNAME = admin
 PASSWORD = nokidc123@#

+ 11 - 6
src/main.py → main.py

@@ -1,30 +1,35 @@
+import argparse
 import configparser
 import logging
 import os
 
-from src.services.application_service import ApplicationService
+from src.app.application import Application
 
 if __name__ == '__main__':
+    # parser = argparse.ArgumentParser(description='Aria2 and Alist Integration Service')
+    # parser.add_argument('--aria2', action='store_true', help='Run Aria2 service only')
+    # args = parser.parse_args()
+
     # 初始化日志和配置
     logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
     # 获取当前脚本的绝对路径
     current_directory = os.path.dirname(os.path.abspath(__file__))
 
     # 获取当前脚本所在目录的上级目录
-    parent_directory = os.path.dirname(current_directory)
+     # parent_directory = os.path.dirname(current_directory)
 
     # 构建 config.ini 文件的路径
-    config_path = os.path.join(parent_directory, 'config', 'config.ini')
+    config_path = os.path.join(current_directory, 'config', 'config.ini')
 
     # 读取配置文件
     config = configparser.ConfigParser()
     config.read(config_path)
 
     # 创建服务实例
-    app_service = ApplicationService(config, parent_directory)
+    app_service = Application(config, current_directory)
 
-    # 开始监控
-    app_service.start_task()
+    # task
+    app_service.start_remote_download()
 
     # 执行主循环
     app_service.main_loop()

+ 3 - 0
requirements.txt

@@ -4,8 +4,11 @@ certifi==2023.11.17
 charset-normalizer==3.3.2
 idna==3.6
 loguru==0.7.2
+overrides==7.6.0
+pyarr==5.2.0
 requests==2.31.0
 tomli==2.0.1
+types-requests==2.31.0.20240125
 typing_extensions==4.9.0
 urllib3==2.1.0
 websocket-client==1.7.0

+ 0 - 0
src/__init__.py


+ 27 - 0
src/api/Aria2API.py

@@ -0,0 +1,27 @@
+from aria2p import API, Client
+
+from aria2p import API, Client
+
+
+class Aria2API:
+    def __init__(self, url, secret):
+        self.aria2_client = Client(url, secret=secret)
+        self.aria2_api = API(self.aria2_client)
+
+    def get_downloads(self):
+        # 返回所有下载信息的列表
+        return self.aria2_api.get_downloads()
+
+    def add_url(self, url, options=None):
+        """
+        将下载链接添加到 Aria2。
+        :param url: 下载链接
+        :param options: Aria2下载选项,例如下载目录
+        """
+        try:
+            # 添加下载链接到 Aria2
+            download = self.aria2_api.add_uris([url], options=options if options else {})
+            return download
+        except Exception as e:
+            print(f"Error adding URL to Aria2: {e}")
+            return None

+ 0 - 72
src/api/Aria2Monitor.py

@@ -1,72 +0,0 @@
-import configparser
-import logging
-import os
-import shutil
-import threading
-import time
-from aria2p import API, Client
-
-
-class Aria2Monitor:
-    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:
-            downloads = self.aria2_api.get_downloads()
-            for download in downloads:
-                if download.is_complete:
-                    all_files_moved = True
-                    for file in download.files:
-                        if file.selected:
-                            file_path = file.path
-                            file_name = os.path.basename(file_path)
-                            target_path = os.path.join(destination_folder, file_name)
-                            logging.info(f"Prepare to move: {file_path} -> {target_path}")
-
-                            try:
-                                if os.path.exists(file_path):
-                                    shutil.move(file_path, target_path)
-                                    logging.info(f"Successfully moved: {file_path} -> {target_path}")
-                                else:
-                                    logging.warning(f"File not found: {file_path}")
-                                    all_files_moved = False
-                            except Exception as e:
-                                logging.error("Error moving file", exc_info=True)
-                                all_files_moved = False
-
-                    if all_files_moved:
-                        download.remove()
-
-            time.sleep(check_interval)

+ 0 - 0
src/api/__init__.py


+ 2 - 2
src/api/alist.py

@@ -161,8 +161,8 @@ class AlistAPI:
     def recursive_collect_contents(self,
                                    remote_download_path,
                                    home_download_path,
-                                   src_dir,
-                                   dest_path,
+                                   src_dir=None,
+                                   dest_path=None,
                                    current_sub_path=''):
         contents = []
         file_list = self.list_directory(remote_download_path)

+ 0 - 0
src/app/__init__.py


+ 53 - 0
src/app/application.py

@@ -0,0 +1,53 @@
+import json
+import logging
+import os
+import signal
+import threading
+import time
+
+from src.services.Aria2Service import Aria2Service
+from src.services.RemoteDownloadSerivce import RemoteDownloadService
+
+
+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 Application:
+    def __init__(self, config, parent_directory, remote_download_service=True):
+        self.config = config
+        self.parent_dir = parent_directory
+
+        self.aria2_service = Aria2Service(config)
+
+        if remote_download_service:
+            self.remote_download_service = RemoteDownloadService(config, parent_directory)
+
+        self.is_running = True
+
+        # 注册信号处理函数
+        signal.signal(signal.SIGINT, self.handle_signal)
+
+    def handle_signal(self, signum, frame):
+        """处理中断信号,优雅地退出循环"""
+        self.is_running = False
+        print("Exiting...")
+
+    def start_remote_download(self):
+        self.remote_download_service.process_downloads()
+
+    def start_aria2_monitoring(self):
+        self.aria2_service.start_monitoring()
+
+    def stop_aria2_monitoring(self):
+        self.aria2_service.stop_monitoring()
+
+    def main_loop(self):
+        # 主循环
+        while self.is_running:
+            # print("Running...")
+            time.sleep(1)  # 模拟任务执行
+        print("Stopped.")
+        self.stop_aria2_monitoring()

+ 19 - 0
src/services/Aria2Service.py

@@ -0,0 +1,19 @@
+from src.api.Aria2API import Aria2API
+from src.task.Aria2FileCopyTask import Aria2FileCopyTask
+
+
+class Aria2Service:
+    def __init__(self, config):
+        self.config = config
+        self.aria2_api = Aria2API(config['ARIA2']['RPC_URL'], config['ARIA2']['RPC_SECRET'])
+        self.file_copy_service = Aria2FileCopyTask(
+            aria2_api=self.aria2_api,
+            download_path=config['ARIA2']['DESTINATION_PATH'],
+            destination_path=config['ARIA2']['DES_COPY_PATH']
+        )
+
+    def start_monitoring(self):
+        self.file_copy_service.start_monitoring()
+
+    def stop_monitoring(self):
+        self.file_copy_service.stop_monitoring()

+ 0 - 35
src/services/FileProcessingService.py

@@ -1,35 +0,0 @@
-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}")

+ 94 - 0
src/services/RemoteDownloadSerivce.py

@@ -0,0 +1,94 @@
+import json
+import logging
+import os
+
+from src.api.Aria2API import Aria2API
+from src.api.alist import AlistAPI
+from src.api.radarr import RadarClient
+
+
+class RemoteDownloadService:
+    def __init__(self, config, parent_dir):
+        self.config = config
+        self.remote_alist_api = AlistAPI(self.config['REMOTE_ALIST']['API_URL'],
+                                         self.config['REMOTE_ALIST']['USERNAME'],
+                                         self.config['REMOTE_ALIST']['PASSWORD'])
+        self.radar_client = RadarClient(self.config['RADAR']['URL'],
+                                        self.config['RADAR']['API_KEY'])
+
+        self.home_alist_api = AlistAPI(self.config['HOME_ALIST']['API_URL'],
+                                       self.config['HOME_ALIST']['USERNAME'],
+                                       self.config['HOME_ALIST']['PASSWORD'])
+
+        self.aria2_api = Aria2API(config['ARIA2']['RPC_URL'], config['ARIA2']['RPC_SECRET'])
+
+        # 读取路径等配置
+        self.remote_alist_download_path = self.config['REMOTE_ALIST']['DOWNLOAD_PATH']
+        self.home_alist_download_path = self.config['HOME_ALIST']['DOWNLOAD_PATH']
+        self.home_scy_alist_copy_path = self.config['HOME_ALIST']['SCY_COPY_PATH']
+        self.home_des_alist_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']
+        self.aria2_download_path = self.config['ARIA2']['DESTINATION_PATH']
+        self.aria2_docker_download_path = self.config['ARIA2']['DOCKER_DOWNLOAD_PATH']
+        self.parent_dir = parent_dir
+        self.home_json_path = os.path.join(parent_dir, 'data', self.home_data)
+
+    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
+        )
+        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
+
+    def process_downloads(self):
+
+        # 获取远程目录结构
+        remote_json_path = self.save_directory_contents(
+            self.remote_alist_download_path,
+            self.home_alist_download_path,
+            self.home_scy_alist_copy_path,
+            self.home_des_alist_copy_path,
+            self.parent_dir,
+        )
+        # 获取远程目录结构
+
+        # 从 Radarr 获取已完成电影的数量
+        already_movies = self.radar_client.get_already_movies()
+        for movie in already_movies:
+            logging.info(movie)
+            # 从 Alist 获取所有远程电影的路径并获取文件真实下载路径,然后添加到aira2开始下载
+            for home in remote_json_path:
+                if not home['is_dir']:
+                    cent_data = self.remote_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}")
+                        self.send_to_aria2(cent_data['data']['raw_url'])
+                        logging.info(f"start to download {cent_data['data']['raw_url']}")
+                    else:
+                        logging.info(f"file no ")
+
+    def send_to_aria2(self, file_path):
+        # 将文件添加到 Aria2 下载队列
+        self.aria2_api.add_url(file_path, options={"dir": self.aria2_docker_download_path + "/movie"})
+        logging.info(f"Added to Aria2: {file_path}")

+ 0 - 145
src/services/application_service.py

@@ -1,145 +0,0 @@
-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()

+ 102 - 0
src/task/Aria2FileCopyTask.py

@@ -0,0 +1,102 @@
+import threading
+import logging
+import os
+import shutil
+import time
+
+
+class Aria2FileCopyTask:
+    def __init__(self, aria2_api, download_path, destination_path):
+        self.aria2_api = aria2_api
+        self.download_path = download_path
+        self.destination_path = destination_path
+        self.is_running = False
+        self.thread = None
+
+    def start_monitoring(self):
+        if not self.is_running:
+            self.is_running = True
+            self.thread = threading.Thread(target=self.monitor_aria2_and_move)
+            self.thread.start()
+
+    def stop_monitoring(self):
+        self.is_running = False
+        if self.thread:
+            self.thread.join()
+
+    def monitor_aria2_and_move(self):
+        while self.is_running:
+            # 获取 Aria2 当前的下载列表
+            downloads = self.aria2_api.get_downloads()
+            for download in downloads:
+                if download.is_complete:
+                    # 从 Aria2 中移除已完成的下载记录
+                    # 无论 Aria2 是否有记录,都执行移动操作
+                    self.move_new_files(self.download_path, self.destination_path)
+                    # 添加移动完成的提示
+                    logging.info("Completed checking for new files to move.")
+                    download.remove()
+                    logging.info("delete ara2 ")
+
+            time.sleep(60)
+
+    def move_new_files(self, src_dir, dest_dir):
+        """移动新文件和目录,如果它们在目标目录中不存在"""
+        if not os.path.exists(dest_dir):
+            os.makedirs(dest_dir)
+        for item in os.listdir(src_dir):
+            src_item = os.path.join(src_dir, item)
+            dest_item = os.path.join(dest_dir, item)
+
+            # 如果是目录
+            if os.path.isdir(src_item):
+                # 检查目标目录是否存在
+                if not os.path.exists(dest_item):
+                    # 直接移动整个目录
+                    shutil.move(src_item, dest_item)
+                    logging.info(f"Moved directory from {src_item} to {dest_item}")
+                else:
+                    # 如果目标目录已存在,递归处理子目录内容
+                    self.move_new_files(src_item, dest_item)
+            else:
+                # 如果是文件,处理逻辑与之前相同
+                if not os.path.exists(dest_item):
+                    shutil.move(src_item, dest_item)
+                    logging.info(f"Moved file from {src_item} to {dest_item}")
+                else:
+                    logging.info(f"File already exists, skipping: {dest_item}")
+
+    def monitor_aria2_and_copy(self):
+        while self.is_running:
+            # 获取 Aria2 当前的下载列表
+            downloads = self.aria2_api.get_downloads()
+            for download in downloads:
+                if download.is_complete:
+                    # 从 Aria2 中移除已完成的下载记录
+                    download.remove()
+
+            # 无论 Aria2 是否有记录,都执行复制操作
+            self.copy_new_files(self.download_path, self.destination_path)
+            # 添加复制完成的提示
+            logging.info("Completed checking for new files to copy.")
+
+            time.sleep(10)
+
+    def copy_new_files(self, src_dir, dest_dir):
+        """复制新文件,如果它们在目标目录中不存在"""
+        if not os.path.exists(dest_dir):
+            os.makedirs(dest_dir)
+        for item in os.listdir(src_dir):
+            src_item = os.path.join(src_dir, item)
+            dest_item = os.path.join(dest_dir, item)
+
+            if os.path.isdir(src_item):
+                # 如果是目录,则递归复制
+                self.copy_new_files(src_item, dest_item)
+            else:
+                # 如果是文件,则检查目标文件是否存在
+                if not os.path.exists(dest_item):
+                    shutil.copy(src_item, dest_item)
+                    logging.info(f"Copied new file from {src_item} to {dest_item}")
+                else:
+                    logging.info(f"File already exists, skipping: {dest_item}")

+ 0 - 0
src/task/__init__.py


+ 9 - 3
tests/test.py

@@ -78,11 +78,9 @@ def download_directory(self, is_debug=False, json_file_path='directory_contents.
                 logging.info(f"File already downloaded, skipping: {local_file}")
 
 
-
 # 创建一个全局变量,用来表示是否继续监控
 is_running = True
 
-
 # 定义信号处理函数
 # def handle_signal(signum, frame):
 #     global is_running
@@ -116,4 +114,12 @@ is_running = True
 #                                                    json_file_path='data/home.json')
 #     logging.info(f" remote save_directory path {home_download_path} to data/home.json")
 #
-#     logging.info("Task completion")
+#     logging.info("Task completion")
+
+
+# def start_task(self):
+
+#
+
+#
+#     self.run()

+ 360 - 0
tests/test2.py

@@ -0,0 +1,360 @@
+"""RemoteDownloadSerivce 类"""
+"""主要实现功能"""
+"""通过读取radarr服务器上的api 获取已经完成的电影"""
+"""并通过alist api 获取到对应到文件到下载地址 """
+"""并送到本地 airai2 下载"""
+
+"""相关配置"""
+# [COMMON]
+# LOG_LEVEL = INFO
+# TIMEOUT = 3600
+# CHECK_INTERVAL = 10
+# REMOTE_DATA=remote.json
+# HOME_DATA=home.json
+#
+# [ARIA2]
+# RPC_URL = http://192.168.88.29
+# RPC_SECRET = 123456
+# DESTINATION_PATH = /home/downloads
+# DES_COPY_PATH = /home/video/sync
+#
+# [HOME_ALIST]
+# API_URL = http://192.168.88.29:5244/api
+# WEB_URL = http://192.168.88.29
+# CLIENT_ID = 4e34854d5d7390ef7801
+# USERNAME = admin
+# PASSWORD = nokidc123@#
+# DOWNLOAD_PATH = /downloads/movie
+# SCY_COPY_PATH = /downloads/movie
+# DES_COPY_PATH = /media/sync/movie
+#
+# [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
+#
+# [RADAR]
+# URL = http://box.szfa.xyz:7878
+# API_KEY = bd45569f422a4c159600964b7b85a0bd
+
+
+"""这里需要修改一下"""
+
+
+# def process_downloads(self):
+#     completed_movies = self.radar_client.get_already_movies()
+#     for movie in completed_movies:
+#         file_path = self.get_remote_file_path(movie)
+#         if file_path:
+#             self.send_to_aria2(file_path)
+"""这里radar_client 只能获取到数量,用来循环"""
+"""电影到下载地址,应该是 远程 apist 提供到,所以需要遍历了 远程路径"""
+# self.remote_download_path = config['REMOTE_ALIST']['DOWNLOAD_PATH']
+"""alist api 的实现"""
+# import json
+# import logging
+# import os
+# import requests
+# from src.models.task_type import TaskType, ActionType
+#
+
+# def construct_path(base_path, sub_path, file_name):
+#     # 确保路径部分不包含反斜杠
+#     base_path = base_path.replace('\\', '/')
+#     sub_path = sub_path.replace('\\', '/')
+#     file_name = file_name.replace('\\', '/')
+#
+#     # 使用 os.path.join 构建整个路径
+#     return os.path.join(base_path, sub_path, file_name)
+#
+#
+# class AlistAPI:
+#     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.login(username, password)
+#
+#     def login(self, username, password):
+#         data = {
+#             'username': username,
+#             'password': password
+#         }
+#         response = requests.post(f'{self.url}/auth/login', data=json.dumps(data), headers=self.headers)
+#         if response.status_code == 200:
+#             token = response.json()
+#             self.headers['Authorization'] = token['data']['token']
+#         else:
+#             raise Exception('Login failed')
+#
+#     def get_directory(self, path="", password="", page=1, per_page=0, refresh=False):
+#         payload = {
+#             "path": path,
+#             "password": password,
+#             "page": page,
+#             "per_page": per_page,
+#             "refresh": refresh
+#         }
+#         response = requests.post(f'{self.url}/fs/dirs', data=json.dumps(payload), headers=self.headers)
+#         return response.json()
+#
+#     def copy_file(self, src_dir, dst_dir, names):
+#         payload = {
+#             "src_dir": src_dir,
+#             "dst_dir": dst_dir,
+#             "names": names
+#         }
+#         response = requests.post(f'{self.url}/fs/copy', data=json.dumps(payload), headers=self.headers)
+#         return response.json()
+#
+#     def get_completed_tasks(self, task_type):
+#         """获取指定任务类型的已完成任务列表"""
+#         if task_type == TaskType.UPLOAD:
+#             api_endpoint = '/admin/task/upload/done'
+#         elif task_type == TaskType.COPY:
+#             api_endpoint = '/admin/task/copy/done'
+#         elif task_type == TaskType.ARIA2_DOWNLOAD:
+#             api_endpoint = '/admin/task/aria2_down/done'
+#         elif task_type == TaskType.ARIA2_TRANSFER:
+#             api_endpoint = '/admin/task/aria2_transfer/done'
+#         elif task_type == TaskType.QBITTORRENT_DOWNLOAD:
+#             api_endpoint = '/admin/task/qbit_down/done'
+#         elif task_type == TaskType.QBITTORRENT_TRANSFER:
+#             api_endpoint = '/admin/task/qbit_transfer/done'
+#         else:
+#             raise ValueError("Invalid task type")
+#
+#         response = requests.get(
+#             f'{self.url}{api_endpoint}',
+#             headers=self.headers
+#         )
+#         return response.json()
+#
+#     def copy_directory(self, src_path, dst_path):
+#         file_list = self.list_directory(src_path)
+#         if not file_list:
+#             return
+#         for file_info in file_list['data']['content']:
+#             if file_info['is_dir']:
+#                 new_src_path = src_path + "/" + file_info['name']
+#                 new_dst_path = dst_path + "/" + file_info['name']
+#                 # new_src_path = os.path.join(src_path, file_info['name'])
+#                 # new_dst_path = os.path.join(dst_path, file_info['name'])
+#                 print(f"Copying directory: {new_src_path} to {new_dst_path}")
+#                 self.copy_directory(new_src_path, new_dst_path)
+#             else:
+#                 file_name = file_info['name']
+#                 print(f"Copying file: {src_path}/{file_name} to {dst_path}/{file_name}")
+#                 # 这里原本是调用 self.copy_file,现在改为仅打印信息
+#                 self.copy_file(src_path, dst_path, [file_name])
+#
+#     def list_directory(self, path, password="", page=1, per_page=0, refresh=False):
+#         payload = {
+#             "path": path,
+#             "password": password,
+#             "page": page,
+#             "per_page": per_page,
+#             "refresh": refresh
+#         }
+#         response = requests.post(f'{self.url}/fs/list', data=json.dumps(payload), headers=self.headers)
+#         return response.json()
+#
+#     def get_file_or_directory_info(self, path, password="", page=1, per_page=0, refresh=False):
+#         payload = {
+#             "path": path,
+#             "password": password,
+#             "page": page,
+#             "per_page": per_page,
+#             "refresh": refresh
+#         }
+#         response = requests.post(f"{self.url}/fs/get", data=json.dumps(payload), headers=self.headers)
+#         if response.status_code == 200:
+#             return response.json()
+#         return None
+#
+#     def move_file(self, src_dir, dst_dir, names):
+#         payload = json.dumps({
+#             "src_dir": src_dir,
+#             "dst_dir": dst_dir,
+#             "names": names
+#         })
+#         response = requests.post(f"{self.url}/fs/move", data=payload, headers=self.headers)
+#         return response.json()
+#
+#     def remove_files_or_folders(self, dir_path, names):
+#         """删除指定目录下的文件或文件夹"""
+#         payload = {
+#             "dir": dir_path,
+#             "names": names
+#         }
+#         response = requests.post(
+#             f'{self.url}/fs/remove',
+#             headers=self.headers,
+#             json=payload
+#         )
+#         return response.json()
+#
+#     def remove_empty_directory(self, src_dir):
+#         """删除空文件夹"""
+#         payload = {
+#             "src_dir": src_dir
+#         }
+#         response = requests.post(
+#             f'{self.url}/fs/remove_empty_director',
+#             headers=self.headers,
+#             json=payload
+#         )
+#         return response.json()
+#
+#     def recursive_collect_contents(self,
+#                                    remote_download_path,
+#                                    home_download_path,
+#                                    src_dir,
+#                                    dest_path,
+#                                    current_sub_path=''):
+#         contents = []
+#         file_list = self.list_directory(remote_download_path)
+#         for file_info in file_list['data']['content']:
+#             # 拼接完整的远程路径
+#             full_path = os.path.join(remote_download_path, file_info['name']).replace('\\', '/')
+#
+#             # 初始化本地下载路径和复制/移动目的地路径
+#             local_download_path = ''
+#             new_dest_path = ''
+#             new_src_dir = ''
+#             # 根据条件构建本地下载路径和复制/移动目的地路径
+#             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 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,
+#                 '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,
+#                                                                home_download_path,
+#                                                                src_dir,
+#                                                                dest_path,
+#                                                                new_sub_path)
+#                 contents.extend(sub_contents)
+#
+#         return contents
+#
+#     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)
+#
+#         for item in directory_contents:
+#
+#             if not item['is_dir']:
+#                 file_name = item['name']
+#                 original_path = item['src_dir']  # 获取原始文件路径
+#                 des_path = item['dst_dir']  # 获取原始文件路径
+#
+#                 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}")
+
+"""这个函数已经是实现了,遍历"""
+# recursive_collect_contents
+"""结果是"""
+# [
+#     {
+#         "name": "Alien Covenant (2017)",
+#         "is_dir": true,
+#         "path": "/data/media/moive/Alien Covenant (2017)",
+#         "downloads_path": "/mnt/data1/downloads/movie/Alien Covenant (2017)",
+#         "scy_path": "",
+#         "copy_des_path": ""
+#     },
+#     {
+#         "name": "Alien Covenant (2017) Bluray-1080p Proper.mp4",
+#         "is_dir": false,
+#         "path": "/data/media/moive/Alien Covenant (2017)/Alien Covenant (2017) Bluray-1080p Proper.mp4",
+#         "downloads_path": "/mnt/data1/downloads/movie/Alien Covenant (2017)/Alien Covenant (2017) Bluray-1080p Proper.mp4",
+#         "scy_path": "",
+#         "copy_des_path": ""
+#     },
+# ]
+
+
+"""你说的我了解,我增加一个"""
+import time
+
+from pyarr import RadarrAPI
+import json
+
+
+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):
+        return self.client.get_movie()
+
+    def delete_movie(self, movie_id):
+        return self.client.del_movie(movie_id)
+
+    def save_movies_to_json(self, file_name='data/movies.json'):
+        movies = self.get_all_movies()
+        with open(file_name, 'w') as f:
+            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.
+        check_interval: Time in seconds between checks
+        custom_action: Function to be called for each downloaded movie
+        """
+        already_processed = set()  # 用于跟踪已处理的电影
+        while True:
+            movies = self.get_all_movies()
+            for movie in movies:
+                if 'movieFile' in movie and movie['movieFile'] and movie['id'] not in already_processed:
+                    custom_action(movie)
+                    already_processed.add(movie['id'])
+            time.sleep(check_interval)
+#### 是为了更好的检测远程服务器是不是下载完成了,不然没有下载完成的,开始下载这不符合我的预期
+
+# 你应该没有明白的需要,我需要获取处理完的电影,然后通过调用远程alist api获取到所有到电影的地址,然后下载,那么我RemoteDownloadService 给我一个解决方案