From 88d08b30b4fe0df0aed8b67544911bc5807aad09 Mon Sep 17 00:00:00 2001 From: thsrite Date: Tue, 12 Nov 2024 20:17:53 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix=20=E5=A2=9E=E5=8A=A0=E5=AF=B9MoviePilot?= =?UTF-8?q?=20V2=20=E7=89=88=E6=9C=AC=20SQLite=20WAL=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.v2.json | 1 + plugins.v2/autobackup/__init__.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package.v2.json b/package.v2.json index 5174025..79ea9cf 100644 --- a/package.v2.json +++ b/package.v2.json @@ -355,6 +355,7 @@ "author": "thsrite", "level": 1, "history": { + "v2.0.2": "增加对MoviePilot V2 版本 SQLite WAL模式的支持", "v2.0.1": "修复cookies文件夹备份失败", "v2.0": "支持备份app.env及cookies,支持自定义保存路径", "v1.3": "去除已废弃的环境变量引用", diff --git a/plugins.v2/autobackup/__init__.py b/plugins.v2/autobackup/__init__.py index 55a6267..61ec00d 100644 --- a/plugins.v2/autobackup/__init__.py +++ b/plugins.v2/autobackup/__init__.py @@ -25,7 +25,7 @@ class AutoBackup(_PluginBase): # 插件图标 plugin_icon = "Time_machine_B.png" # 插件版本 - plugin_version = "2.0.1" + plugin_version = "2.0.2" # 插件作者 plugin_author = "thsrite" # 作者主页 @@ -161,7 +161,7 @@ class AutoBackup(_PluginBase): category_file = config_path / "category.yaml" if category_file.exists(): shutil.copy(category_file, backup_path) - userdb_file = config_path / "user.db" + userdb_file = config_path / "user.db*" if userdb_file.exists(): shutil.copy(userdb_file, backup_path) app_file = config_path / "app.env" From 7ebc136a90283409a1eb8a068e4827afc84142e9 Mon Sep 17 00:00:00 2001 From: thsrite Date: Tue, 12 Nov 2024 20:18:03 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix=20=E5=A2=9E=E5=8A=A0=E5=AF=B9MoviePilot?= =?UTF-8?q?=20V2=20=E7=89=88=E6=9C=AC=20SQLite=20WAL=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.v2.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.v2.json b/package.v2.json index 79ea9cf..4c5a62e 100644 --- a/package.v2.json +++ b/package.v2.json @@ -350,7 +350,7 @@ "name": "自动备份", "description": "自动备份数据和配置文件。", "labels": "系统设置", - "version": "2.0.1", + "version": "2.0.2", "icon": "Time_machine_B.png", "author": "thsrite", "level": 1, From 0a7ee202b4e8d063d22ff563eecc821735e8eda4 Mon Sep 17 00:00:00 2001 From: Xu <692360102@qq.com> Date: Wed, 13 Nov 2024 09:22:40 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=A7=8D=E5=AD=90?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E5=85=BC=E5=AE=B9v2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 - package.v2.json | 14 ++ plugins.v2/downloadtorrent/__init__.py | 314 +++++++++++++++++++++++++ 3 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 plugins.v2/downloadtorrent/__init__.py diff --git a/package.json b/package.json index 93c2bc9..c4876ba 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,6 @@ "icon": "download.png", "author": "thsrite", "level": 1, - "v2": true, "history": { "v1.1": "支持选择MoviePilot配置的下载路径", "v1.0": "删除下载器中该站点辅种,保留该站点没有辅种的种子" diff --git a/package.v2.json b/package.v2.json index 4c5a62e..e6818bb 100644 --- a/package.v2.json +++ b/package.v2.json @@ -425,5 +425,19 @@ "history": { "v1.0": "重写Strm文件内容" } + }, + "DownloadTorrent": { + "name": "添加种子下载", + "description": "选择下载器,添加种子任务。", + "labels": "站点", + "version": "2.0", + "icon": "download.png", + "author": "thsrite", + "level": 1, + "history": { + "v2.0": "兼容V2版本", + "v1.1": "支持选择MoviePilot配置的下载路径", + "v1.0": "删除下载器中该站点辅种,保留该站点没有辅种的种子" + } } } diff --git a/plugins.v2/downloadtorrent/__init__.py b/plugins.v2/downloadtorrent/__init__.py new file mode 100644 index 0000000..c42042f --- /dev/null +++ b/plugins.v2/downloadtorrent/__init__.py @@ -0,0 +1,314 @@ +from typing import Any, List, Dict, Tuple, Optional +from app.db.site_oper import SiteOper +from app.plugins import _PluginBase +from app.log import logger +from app.utils.string import StringUtils +from app.schemas import ServiceInfo +from app.helper.downloader import DownloaderHelper +from app.helper.directory import DirectoryHelper + + + +class DownloadTorrent(_PluginBase): + # 插件名称 + plugin_name = "添加种子下载" + # 插件描述 + plugin_desc = "选择下载器,添加种子任务。" + # 插件图标 + plugin_icon = "download.png" + # 插件版本 + plugin_version = "2.0" + # 插件作者 + plugin_author = "thsrite" + # 作者主页 + author_url = "https://github.com/thsrite" + # 插件配置项ID前缀 + plugin_config_prefix = "downloadtorrent_" + # 加载顺序 + plugin_order = 28 + # 可使用的用户级别 + auth_level = 1 + + # 私有属性 + _is_paused = False + _save_path = None + _mp_path = None + _downloader = None + site = None + torrent_helper = None + downloader_helper = None + directory_helper = None + + def init_plugin(self, config: dict = None): + self.downloader_helper = DownloaderHelper() + self.directory_helper = DirectoryHelper() + self.site = SiteOper() + + if config: + self._is_paused = config.get("is_paused") + self._save_path = config.get("save_path") + self._mp_path = config.get("mp_path") + self._torrent_urls = config.get("torrent_urls") + self._downloader = config.get("downloader") + + # 下载种子 + if self._torrent_urls: + for torrent_url in str(self._torrent_urls).split("\n"): + # 获取种子对应站点cookie + domain = StringUtils.get_url_domain(torrent_url) + if not domain: + logger.error(f"种子 {torrent_url} 获取站点域名失败,跳过处理") + continue + + # 查询站点 + site = self.site.get_by_domain(domain) + if not site or not site.cookie: + logger.error(f"种子 {torrent_url} 获取站点cookie失败,跳过处理") + continue + + service = self.service_info(self._downloader) + download_id = self.__download(service=service, + content=torrent_url, + save_path=self._save_path or self._mp_path, + cookie=site.cookie) + + if download_id: + logger.info(f"种子添加下载成功 {torrent_url} 保存位置 {self._save_path or self._mp_path}") + else: + logger.error(f"种子添加下载失败 {torrent_url} 保存位置 {self._save_path or self._mp_path}") + + self.update_config({ + "downloader": self._downloader, + "save_path": self._save_path, + "mp_path": self._mp_path, + "is_paused": self._is_paused + }) + + def service_info(self, name: str) -> Optional[ServiceInfo]: + """ + 服务信息 + """ + if not name: + logger.warning("尚未配置下载器,请检查配置") + return None + + service = self.downloader_helper.get_service(name) + if not service or not service.instance: + logger.warning(f"获取下载器 {name} 实例失败,请检查配置") + return None + + if service.instance.is_inactive(): + logger.warning(f"下载器 {name} 未连接,请检查配置") + return None + return service + + def __download(self, service: ServiceInfo, content: bytes, + save_path: str, cookie: str) -> Optional[str]: + """ + 添加下载任务 + """ + if not service or not service.instance: + return + downloader = service.instance + if self.downloader_helper.is_downloader("qbittorrent", service=service): + torrent = downloader.add_torrent(content=content, + download_dir=save_path, + is_paused=self._is_paused, + cookie=cookie) + if not torrent: + return None + else: + return torrent + elif self.downloader_helper.is_downloader("transmission", service=service): + # 添加任务 + torrent = downloader.add_torrent(content=content, + download_dir=save_path, + is_paused=self._is_paused, + cookie=cookie) + if not torrent: + return None + else: + return torrent.hashString + + logger.error(f"不支持的下载器类型") + return None + + + def get_state(self) -> bool: + return False + + @staticmethod + def get_command() -> List[Dict[str, Any]]: + pass + + def get_api(self) -> List[Dict[str, Any]]: + pass + + def get_form(self) -> Tuple[List[dict], Dict[str, Any]]: + """ + 拼装插件配置页面,需要返回两块数据:1、页面配置;2、数据结构 + """ + dir_conf = [{'title': d.name, 'value': d.download_path} for d in self.directory_helper.get_local_download_dirs()] + downloader_options = [{"title": config.name, "value": config.name} for config in self.downloader_helper.get_configs().values()] + return [ + { + 'component': 'VForm', + 'content': [ + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 3 + }, + 'content': [ + { + 'component': 'VSelect', + 'props': { + 'model': 'downloader', + 'label': '下载器', + 'items': downloader_options + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 3 + }, + 'content': [ + { + 'component': 'VSelect', + 'props': { + 'model': 'is_paused', + 'label': '暂停种子', + 'items': [ + {'title': '开启', 'value': True}, + {'title': '不开启', 'value': False} + ] + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 3 + }, + 'content': [ + { + 'component': 'VSelect', + 'props': { + 'model': 'mp_path', + 'label': 'MoviePilot保存路径', + 'items': dir_conf + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 3 + }, + 'content': [ + { + 'component': 'VTextField', + 'props': { + 'model': 'save_path', + 'label': '自定义保存路径' + } + } + ] + }, + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + }, + 'content': [ + { + 'component': 'VTextarea', + 'props': { + 'model': 'torrent_urls', + 'rows': '3', + 'label': '种子链接', + 'placeholder': '种子链接,一行一个' + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + }, + 'content': [ + { + 'component': 'VAlert', + 'props': { + 'type': 'info', + 'variant': 'tonal', + 'text': '自定义保存路径优先级高于MoviePilot保存路径。' + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + }, + 'content': [ + { + 'component': 'VAlert', + 'props': { + 'type': 'info', + 'variant': 'tonal', + 'text': '保存路径为下载器保存路径,种子链接一行一个。' + '添加的种子链接需站点已在站点管理维护或公共站点。' + } + } + ] + } + ] + } + ] + } + ], { + "downloader": "qb", + "is_paused": False, + "save_path": "", + "mp_path": "", + "torrent_urls": "" + } + + def get_page(self) -> List[dict]: + pass + + def stop_service(self): + """ + 退出插件 + """ + pass \ No newline at end of file