Merge branch 'jxxghp:main' into feature_dev

This commit is contained in:
InfinityPacer
2024-03-30 16:26:45 +08:00
committed by GitHub
5 changed files with 107 additions and 59 deletions

View File

@@ -20,7 +20,7 @@ MoviePilot官方插件市场https://github.com/jxxghp/MoviePilot-Plugins
- 可在插件目录中放置`requirement.txt`文件用于指定插件依赖的第三方库MoviePilot会在插件安装时自动安装依赖库。
### 5. 界面开发
- 插件支持`插件配置``详情展示`两个展示页面,通过配置化的方式组装,使用[Vuetify](https://vuetifyjs.com/)组件库所有该组件库有的组件都可以通过Json配置使用。
- 插件支持`插件配置``详情展示`两个展示页面,通过配置化的方式组装,使用 [Vuetify](https://vuetifyjs.com/) 组件库所有该组件库有的组件都可以通过Json配置使用。
## 常见问题
@@ -48,34 +48,38 @@ MoviePilot官方插件市场https://github.com/jxxghp/MoviePilot-Plugins
PSMoviePilot中的其它事件也是同样方法实现响应
```python
class EventType(Enum):
# 插件重载
# 插件需要重载
PluginReload = "plugin.reload"
# 插件动作
PluginAction = "plugin.action"
# 执行命令
CommandExcute = "command.excute"
# 站点删除
# 站点删除
SiteDeleted = "site.deleted"
# Webhook消息
WebhookMessage = "webhook.message"
# 站点已更新
SiteUpdated = "site.updated"
# 转移完成
TransferComplete = "transfer.complete"
# 添加下载
# 下载已添加
DownloadAdded = "download.added"
# 删除历史记录
HistoryDeleted = "history.deleted"
# 删除下载源文件
DownloadFileDeleted = "downloadfile.deleted"
# 用户外来消息
# 收到用户外来消息
UserMessage = "user.message"
# 通知消息
# 收到Webhook消息
WebhookMessage = "webhook.message"
# 发送消息通知
NoticeMessage = "notice.message"
# 名称识别请求
NameRecognize = "name.recognize"
# 名称识别结果
NameRecognizeResult = "name.recognize.result"
# 站点信息更新
SiteUpdated = "site.updated"
# 订阅已添加
SubscribeAdded = "subscribe.added"
# 订阅已完成
SubscribeComplete = "subscribe.complete"
```
### 2. 如何在插件中实现远程命令响应?

View File

@@ -185,8 +185,8 @@
},
"CrossSeed": {
"name": "青蛙辅种助手",
"description": "参考ReseedPuppy和IYUU辅种插件实现自动辅种支持站点青蛙、AGSVPT、麒麟、UBits、聆音等。",
"version": "1.8",
"description": "参考ReseedPuppy和IYUU辅种插件实现自动辅种支持站点青蛙、AGSVPT、麒麟、UBits、聆音、憨憨等。",
"version": "1.9",
"icon": "qingwa.png",
"author": "233@qingwa",
"level": 2
@@ -450,7 +450,7 @@
"ContractCheck": {
"name": "契约检查",
"description": "定时检查保种契约达成情况。",
"version": "1.0",
"version": "1.1",
"icon": "contract.png",
"author": "DzAvril",
"level": 1
@@ -458,7 +458,7 @@
"DownloaderHelper": {
"name": "下载器助手",
"description": "自动做种、站点标签、自动删种。",
"version": "1.2",
"version": "1.3",
"icon": "DownloaderHelper.png",
"author": "hotlcc",
"level": 2

View File

@@ -39,7 +39,7 @@ class ContractCheck(_PluginBase):
# 插件图标
plugin_icon = "contract.png"
# 插件版本
plugin_version = "1.0"
plugin_version = "1.1"
# 插件作者
plugin_author = "DzAvril"
# 作者主页
@@ -108,11 +108,11 @@ class ContractCheck(_PluginBase):
)
self._site_schema.sort(key=lambda x: x.order)
# 站点数据
self._sites_data = {}
# 立即运行一次
if self._onlyonce:
# 站点数据
self._sites_data = {}
# 定时服务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info(f"保种契约检查服务启动,立即运行一次")

View File

@@ -33,18 +33,31 @@ class CSSiteConfig(object):
站点辅种配置类
"""
def __init__(self, site_name: str, site_url: str, site_passkey: str) -> None:
self.name = site_name
self.url = site_url.removesuffix("/")
self.passkey = site_passkey
def __init__(
self,
name: str = None,
url: str = None,
passkey: str = None,
id: int = None,
cookie: str = None,
ua: str = None,
proxy: bool = None,
) -> None:
self.name = name
self.url = url
self.passkey = passkey
self.id = id
self.cookie = cookie
self.ua = ua
self.proxy = proxy
def get_api_url(self):
if self.name == "憨憨":
return f"{self.url}/npapi/pieces-hash"
return f"{self.url}/api/pieces-hash"
return f"{self.url}npapi/pieces-hash"
return f"{self.url}api/pieces-hash"
def get_torrent_url(self, torrent_id: str):
return f"{self.url}/download.php?id={torrent_id}&passkey={self.passkey}"
return f"{self.url}download.php?id={torrent_id}&passkey={self.passkey}"
class TorInfo:
@@ -135,21 +148,20 @@ class CrossSeedHelper(object):
"User-Agent": "CrossSeedHelper",
}
data = {"passkey": site.passkey, "pieces_hash": pieces_hash_set}
remote_torrent_infos = []
try:
response = requests.post(
site.get_api_url(), headers=headers, json=data, timeout=10
)
response.raise_for_status()
rsp_body = response.json()
if isinstance(rsp_body["data"], dict):
for pieces_hash, torrent_id in rsp_body["data"].items():
remote_torrent_infos.append(
TorInfo.remote(site.name, pieces_hash, torrent_id)
)
except requests.exceptions.RequestException as e:
return None, f"站点{site.name}请求失败:{e}"
rsp_body = response.json()
remote_torrent_infos = []
if isinstance(rsp_body["data"], dict):
for pieces_hash, torrent_id in rsp_body["data"].items():
remote_torrent_infos.append(
TorInfo.remote(site.name, pieces_hash, torrent_id)
)
return remote_torrent_infos, None
@@ -157,11 +169,11 @@ class CrossSeed(_PluginBase):
# 插件名称
plugin_name = "青蛙辅种助手"
# 插件描述
plugin_desc = "参考ReseedPuppy和IYUU辅种插件实现自动辅种支持站点青蛙、AGSVPT、麒麟、UBits、聆音等。"
plugin_desc = "参考ReseedPuppy和IYUU辅种插件实现自动辅种支持站点青蛙、AGSVPT、麒麟、UBits、聆音、憨憨等。"
# 插件图标
plugin_icon = "qingwa.png"
# 插件版本
plugin_version = "1.8"
plugin_version = "1.9"
# 插件作者
plugin_author = "233@qingwa"
# 作者主页
@@ -206,7 +218,6 @@ class CrossSeed(_PluginBase):
# 辅种缓存出错的种子不再重复辅种且无法清除。种子被删除404等情况
_permanent_error_caches = []
_torrentpaths = []
_name_site_map = {}
_site_cs_infos = []
# 辅种计数
total = 0
@@ -247,18 +258,49 @@ class CrossSeed(_PluginBase):
self._sites = [site_id for site_id, site_name in all_sites if site_id in self._sites]
# 拆分出选中的站点
site_names = [site_name for site_id, site_name in all_sites if site_id in self._sites]
# 拆分为映射关系
self._name_site_map = {}
for site in self.siteoper.list_order_by_pri():
self._name_site_map[site.name] = site
# 只给选中的站点构造站点配置
self._site_cs_infos: List[CSSiteConfig] = []
# 整理所有可用内部站点信息
all_site_cs_info_map : dict[str, CSSiteConfig] = dict()
for site in inner_site_list:
if site.is_active:
all_site_cs_info_map[site.name] = CSSiteConfig(
name=site.name,
url=site.url,
id=site.id,
cookie=site.cookie,
ua=site.ua,
proxy=site.proxy,
)
for site in self.__custom_sites():
all_site_cs_info_map[site.get("name")] = CSSiteConfig(
name=site.get("name"),
url=site.get("url"),
id=site.get("id"),
cookie=site.get("cookie"),
ua=site.get("ua"),
proxy=site.get("proxy"),
)
self._sites = [site.id for site in all_site_cs_info_map.values() if site.id in self._sites]
site_names = [site.name for site in all_site_cs_info_map.values() if site.id in self._sites]
# 整理passkey映射关系
site_name_key_map = dict()
for site_key in self._token.strip().split("\n"):
site_key_arr = re.split("[\s:]+",site_key.strip())
site_name = site_key_arr[0]
db_site = self._name_site_map[site_name]
if site_name in site_names and db_site:
self._site_cs_infos.append(CSSiteConfig(site_name, db_site.url, site_key_arr[1]))
site_name_key_map[site_name] = site_key_arr[1]
# 只给选中的站点补全站点配置
self._site_cs_infos: List[CSSiteConfig] = []
# 根据配置来补充passkey
for site_name in site_names:
site_key = site_name_key_map.get(site_name)
if not site_key:
logger.warning(f"未找到站点{site_name}的passkey, 请检查passkey配置是否有误站点{site_name}将跳过辅种")
continue
site_cs_info = all_site_cs_info_map.get(site_name)
site_cs_info.passkey = site_key
self._site_cs_infos.append(site_cs_info)
self.__update_config()
@@ -709,14 +751,14 @@ class CrossSeed(_PluginBase):
if not torrent_path.exists():
if downloader == "qbittorrent":
# FIXME qb从4.4.0开始,种子文件以标题+序号的方式保存,目前只能尝试导出后再解析
# logger.info(f"正在导出种子 {torrent.get('name')}({hash_str})")
logger.warn(f"QB种子文件不存在{torrent_path} 尝试远程导出种子")
try:
torrent_data = torrent.export()
torrent_info, err = TorInfo.from_data(torrent_data)
except Exception as e:
err = str(e)
if not torrent_info:
logger.error(f"尝试导出种子 {hash_str} 出错 {err}")
logger.error(f"尝试远程导出种子 {hash_str} 出错 {err}")
continue
else:
logger.error(f"种子文件不存在:{torrent_path}")
@@ -873,12 +915,12 @@ class CrossSeed(_PluginBase):
# 逐个站点查询可辅种数据
chunk_size = 100
for site_config in self._site_cs_infos:
db_site_info = self._name_site_map[site_config.name]
if not db_site_info:
logger.info(f"未在支持站点中找到{site_config.name}")
remote_tors: List[TorInfo] = []
total_size = len(pieces_hashes)
for i in range(0, len(pieces_hashes), chunk_size):
if self._event.is_set():
logger.info(f"辅种服务停止")
return
# 切片操作
chunk = pieces_hashes[i:i + chunk_size]
# 处理分组
@@ -911,6 +953,9 @@ class CrossSeed(_PluginBase):
logger.info(f"站点{site_config.name}正在做种或已经辅种过的种子数为{local_cnt}")
for tor_info in not_local_tors:
if self._event.is_set():
logger.info(f"辅种服务停止")
return
if not tor_info:
continue
if not tor_info.torrent_id or not tor_info.pieces_hash:
@@ -922,7 +967,7 @@ class CrossSeed(_PluginBase):
logger.info(f"种子 {tor_info.get_name_id_tag()} 辅种失败且已缓存,跳过 ...")
continue
# 添加任务
self.__download_torrent(tor=tor_info, site_config=site_config, site_info=db_site_info,
self.__download_torrent(tor=tor_info, site_config=site_config,
downloader=downloader,
save_path=save_paths.get(tor_info.pieces_hash))
@@ -967,7 +1012,6 @@ class CrossSeed(_PluginBase):
self,
tor: TorInfo,
site_config: CSSiteConfig,
site_info: Site,
downloader: str,
save_path: str,
):
@@ -984,9 +1028,9 @@ class CrossSeed(_PluginBase):
# 下载种子文件
_, content, _, _, error_msg = self.torrent.download_torrent(
url=torrent_url,
cookie=site_info.cookie,
ua=site_info.ua or settings.USER_AGENT,
proxy=True if site_info.proxy else False)
cookie=site_config.cookie,
ua=site_config.ua or settings.USER_AGENT,
proxy=True if site_config.proxy else False)
# 兼容种子无法访问的情况
if not content or (isinstance(content, bytes) and "你没有该权限".encode(encoding="utf-8") in content):

View File

@@ -32,7 +32,7 @@ class DownloaderHelper(_PluginBase):
# 插件图标
plugin_icon = "DownloaderHelper.png"
# 插件版本
plugin_version = "1.2"
plugin_version = "1.3"
# 插件作者
plugin_author = "hotlcc"
# 作者主页
@@ -1541,12 +1541,12 @@ class DownloaderHelper(_PluginBase):
# 执行
logger.info('下载添加事件监听任务执行开始...')
context = TaskContext().enable_seeding(False).enable_tagging(True).enable_delete(False)
hash_str = event.event_data.get('hash')
if hash:
context.select_torrent(hash_str)
_hash = event.event_data.get('hash')
if _hash:
context.select_torrent(torrent=_hash)
username = event.event_data.get('username')
if username:
context.select_username(username)
context.set_username(username=username)
self.__run_for_all(context=context)
logger.info('下载添加事件监听任务执行结束')