mirror of
https://github.com/jxxghp/MoviePilot-Plugins.git
synced 2026-03-27 10:05:57 +00:00
Merge pull request #5 from thsrite/main
This commit is contained in:
10
package.json
10
package.json
@@ -74,7 +74,7 @@
|
||||
"LibraryScraper": {
|
||||
"name": "媒体库刮削",
|
||||
"description": "定时对媒体库进行刮削,补齐缺失元数据和图片。",
|
||||
"version": "1.0",
|
||||
"version": "1.1",
|
||||
"icon": "scraper.png",
|
||||
"color": "#FF7D00",
|
||||
"author": "jxxghp",
|
||||
@@ -92,7 +92,7 @@
|
||||
"MediaSyncDel": {
|
||||
"name": "媒体文件同步删除",
|
||||
"description": "同步删除历史记录、源文件和下载任务。",
|
||||
"version": "1.1",
|
||||
"version": "1.2",
|
||||
"icon": "mediasyncdel.png",
|
||||
"color": "#ff1a1a",
|
||||
"author": "thsrite",
|
||||
@@ -137,7 +137,7 @@
|
||||
"MediaServerMsg": {
|
||||
"name": "媒体库服务器通知",
|
||||
"description": "发送Emby/Jellyfin/Plex服务器的播放、入库等通知消息。",
|
||||
"version": "1.0",
|
||||
"version": "1.1",
|
||||
"icon": "mediaplay.png",
|
||||
"color": "#42A3DB",
|
||||
"author": "jxxghp",
|
||||
@@ -263,7 +263,7 @@
|
||||
"InvitesSignin": {
|
||||
"name": "药丸签到",
|
||||
"description": "药丸论坛签到。",
|
||||
"version": "1.0",
|
||||
"version": "1.1",
|
||||
"icon": "invites.png",
|
||||
"color": "#FFFFFF",
|
||||
"author": "thsrite",
|
||||
@@ -290,7 +290,7 @@
|
||||
"CloudDiskDel": {
|
||||
"name": "云盘文件删除",
|
||||
"description": "媒体库删除strm文件后同步删除云盘资源。",
|
||||
"version": "1.0",
|
||||
"version": "1.1",
|
||||
"icon": "clouddisk.png",
|
||||
"color": "#4285F5",
|
||||
"author": "thsrite",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
from pathlib import Path
|
||||
@@ -23,7 +22,7 @@ class CloudDiskDel(_PluginBase):
|
||||
# 主题色
|
||||
plugin_color = "#4285F5"
|
||||
# 插件版本
|
||||
plugin_version = "1.0"
|
||||
plugin_version = "1.1"
|
||||
# 插件作者
|
||||
plugin_author = "thsrite"
|
||||
# 作者主页
|
||||
@@ -40,15 +39,29 @@ class CloudDiskDel(_PluginBase):
|
||||
# 任务执行间隔
|
||||
_paths = {}
|
||||
_notify = False
|
||||
_del_history = False
|
||||
|
||||
_video_formats = ('.mp4', '.avi', '.rmvb', '.wmv', '.mov', '.mkv', '.flv', '.ts', '.webm', '.iso', '.mpg')
|
||||
|
||||
def init_plugin(self, config: dict = None):
|
||||
if config:
|
||||
self._enabled = config.get("enabled")
|
||||
self._notify = config.get("notify")
|
||||
self._del_history = config.get("del_history")
|
||||
for path in str(config.get("path")).split("\n"):
|
||||
paths = path.split(":")
|
||||
self._paths[paths[0]] = paths[1]
|
||||
|
||||
# 清理插件历史
|
||||
if self._del_history:
|
||||
self.del_data(key="history")
|
||||
self.update_config({
|
||||
"enabled": self._enabled,
|
||||
"notify": self._notify,
|
||||
"path": config.get("path"),
|
||||
"del_history": False
|
||||
})
|
||||
|
||||
@eventmanager.register(EventType.NetworkDiskDel)
|
||||
def clouddisk_del(self, event: Event):
|
||||
if not self._enabled:
|
||||
@@ -80,9 +93,19 @@ class CloudDiskDel(_PluginBase):
|
||||
pattern = path.stem.replace('[', '?').replace(']', '?')
|
||||
logger.info(f"开始筛选同名文件 {pattern}")
|
||||
files = path.parent.glob(f"{pattern}.*")
|
||||
|
||||
remove_flag = False
|
||||
for file in files:
|
||||
Path(file).unlink()
|
||||
logger.info(f"云盘文件 {file} 已删除")
|
||||
remove_flag = True
|
||||
|
||||
if not remove_flag:
|
||||
for ext in self._video_formats:
|
||||
file = path.stem + ext
|
||||
if Path(file).exists():
|
||||
Path(file).unlink()
|
||||
logger.info(f"云盘文件 {file} 已删除")
|
||||
else:
|
||||
# 非根目录,才删除目录
|
||||
shutil.rmtree(path)
|
||||
@@ -151,8 +174,8 @@ class CloudDiskDel(_PluginBase):
|
||||
"type": media_type.value,
|
||||
"title": media_name,
|
||||
"path": media_path,
|
||||
"season": season_num,
|
||||
"episode": episode_num,
|
||||
"season": season_num if season_num and str(season_num).isdigit() else None,
|
||||
"episode": episode_num if episode_num and str(episode_num).isdigit() else None,
|
||||
"image": poster_image,
|
||||
"del_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
|
||||
})
|
||||
@@ -185,7 +208,7 @@ class CloudDiskDel(_PluginBase):
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 6
|
||||
'md': 4
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
@@ -201,7 +224,7 @@ class CloudDiskDel(_PluginBase):
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 6
|
||||
'md': 4
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
@@ -212,6 +235,22 @@ class CloudDiskDel(_PluginBase):
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 4
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'del_history',
|
||||
'label': '删除历史',
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -289,7 +328,8 @@ class CloudDiskDel(_PluginBase):
|
||||
], {
|
||||
"enabled": False,
|
||||
"path": "",
|
||||
"notify": False
|
||||
"notify": False,
|
||||
"del_history": False
|
||||
}
|
||||
|
||||
def get_page(self) -> List[dict]:
|
||||
|
||||
@@ -24,7 +24,7 @@ class InvitesSignin(_PluginBase):
|
||||
# 主题色
|
||||
plugin_color = "#FFFFFF"
|
||||
# 插件版本
|
||||
plugin_version = "1.0"
|
||||
plugin_version = "1.1"
|
||||
# 插件作者
|
||||
plugin_author = "thsrite"
|
||||
# 作者主页
|
||||
@@ -157,6 +157,17 @@ class InvitesSignin(_PluginBase):
|
||||
text=f"累计签到 {totalContinuousCheckIn} \n"
|
||||
f"剩余药丸 {money}")
|
||||
|
||||
# 读取历史记录
|
||||
history = self.get_data('history') or []
|
||||
|
||||
history.append({
|
||||
"date": datetime.today().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
"totalContinuousCheckIn": totalContinuousCheckIn,
|
||||
"money": money
|
||||
})
|
||||
# 保存签到历史
|
||||
self.save_data(key="history", value=history)
|
||||
|
||||
def get_state(self) -> bool:
|
||||
return self._enabled
|
||||
|
||||
@@ -172,131 +183,232 @@ class InvitesSignin(_PluginBase):
|
||||
拼装插件配置页面,需要返回两块数据:1、页面配置;2、数据结构
|
||||
"""
|
||||
return [
|
||||
{
|
||||
'component': 'VForm',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 4
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'enabled',
|
||||
'label': '启用插件',
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 4
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'notify',
|
||||
'label': '开启通知',
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 4
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'onlyonce',
|
||||
'label': '立即运行一次',
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 6
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VTextField',
|
||||
'props': {
|
||||
'model': 'cron',
|
||||
'label': '签到周期'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 6
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VTextField',
|
||||
'props': {
|
||||
'model': 'cookie',
|
||||
'label': '药丸cookie'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
{
|
||||
'component': 'VForm',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 4
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VAlert',
|
||||
'props': {
|
||||
'type': 'info',
|
||||
'variant': 'tonal',
|
||||
'text': '整点定时签到失败?不妨换个时间试试'
|
||||
}
|
||||
}
|
||||
]
|
||||
'model': 'enabled',
|
||||
'label': '启用插件',
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 4
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'notify',
|
||||
'label': '开启通知',
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 4
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'onlyonce',
|
||||
'label': '立即运行一次',
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
], {
|
||||
"enabled": False,
|
||||
"onlyonce": False,
|
||||
"notify": False,
|
||||
"cookie": "",
|
||||
"cron": "0 9 * * *"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 6
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VTextField',
|
||||
'props': {
|
||||
'model': 'cron',
|
||||
'label': '签到周期'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 6
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VTextField',
|
||||
'props': {
|
||||
'model': 'cookie',
|
||||
'label': '药丸cookie'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VAlert',
|
||||
'props': {
|
||||
'type': 'info',
|
||||
'variant': 'tonal',
|
||||
'text': '整点定时签到失败?不妨换个时间试试'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
], {
|
||||
"enabled": False,
|
||||
"onlyonce": False,
|
||||
"notify": False,
|
||||
"cookie": "",
|
||||
"cron": "0 9 * * *"
|
||||
}
|
||||
|
||||
def get_page(self) -> List[dict]:
|
||||
# 查询同步详情
|
||||
historys = self.get_data('history')
|
||||
if not historys:
|
||||
return [
|
||||
{
|
||||
'component': 'div',
|
||||
'text': '暂无数据',
|
||||
'props': {
|
||||
'class': 'text-center',
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
if not isinstance(historys, list):
|
||||
historys = [historys]
|
||||
|
||||
# 按照签到时间倒序
|
||||
historys = sorted(historys, key=lambda x: x.get("date") or 0, reverse=True)
|
||||
|
||||
# 签到消息
|
||||
sign_msgs = [
|
||||
{
|
||||
'component': 'tr',
|
||||
'props': {
|
||||
'class': 'text-sm'
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'td',
|
||||
'props': {
|
||||
'class': 'whitespace-nowrap break-keep text-high-emphasis'
|
||||
},
|
||||
'text': history.get("date")
|
||||
},
|
||||
{
|
||||
'component': 'td',
|
||||
'text': history.get("totalContinuousCheckIn")
|
||||
},
|
||||
{
|
||||
'component': 'td',
|
||||
'text': history.get("money")
|
||||
}
|
||||
]
|
||||
} for history in historys
|
||||
]
|
||||
|
||||
# 拼装页面
|
||||
return [
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VTable',
|
||||
'props': {
|
||||
'hover': True
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'thead',
|
||||
'content': [
|
||||
{
|
||||
'component': 'th',
|
||||
'props': {
|
||||
'class': 'text-start ps-4'
|
||||
},
|
||||
'text': '时间'
|
||||
},
|
||||
{
|
||||
'component': 'th',
|
||||
'props': {
|
||||
'class': 'text-start ps-4'
|
||||
},
|
||||
'text': '连续签到次数'
|
||||
},
|
||||
{
|
||||
'component': 'th',
|
||||
'props': {
|
||||
'class': 'text-start ps-4'
|
||||
},
|
||||
'text': '剩余药丸'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'tbody',
|
||||
'content': sign_msgs
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
pass
|
||||
|
||||
def stop_service(self):
|
||||
|
||||
@@ -19,7 +19,6 @@ from app.utils.system import SystemUtils
|
||||
|
||||
|
||||
class LibraryScraper(_PluginBase):
|
||||
|
||||
# 插件名称
|
||||
plugin_name = "媒体库刮削"
|
||||
# 插件描述
|
||||
@@ -29,7 +28,7 @@ class LibraryScraper(_PluginBase):
|
||||
# 主题色
|
||||
plugin_color = "#FF7D00"
|
||||
# 插件版本
|
||||
plugin_version = "1.0"
|
||||
plugin_version = "1.1"
|
||||
# 插件作者
|
||||
plugin_author = "jxxghp"
|
||||
# 作者主页
|
||||
@@ -54,7 +53,7 @@ class LibraryScraper(_PluginBase):
|
||||
_exclude_paths = ""
|
||||
# 退出事件
|
||||
_event = Event()
|
||||
|
||||
|
||||
def init_plugin(self, config: dict = None):
|
||||
# 读取配置
|
||||
if config:
|
||||
@@ -90,7 +89,7 @@ class LibraryScraper(_PluginBase):
|
||||
logger.info(f"媒体库刮削服务,立即运行一次")
|
||||
self._scheduler.add_job(func=self.__libraryscraper, trigger='date',
|
||||
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3),
|
||||
name="Cloudflare优选")
|
||||
name="媒体库刮削")
|
||||
# 关闭一次性开关
|
||||
self._onlyonce = False
|
||||
self.update_config({
|
||||
@@ -245,6 +244,49 @@ class LibraryScraper(_PluginBase):
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VAlert',
|
||||
'props': {
|
||||
'type': 'info',
|
||||
'variant': 'tonal',
|
||||
'text': '刮削路径要配置到二级分类路径。(如果配置了LIBRARY_CATEGORY=true)'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VAlert',
|
||||
'props': {
|
||||
'type': 'info',
|
||||
'variant': 'tonal',
|
||||
'text': '刮削路径后拼接#电视剧/电影,强制指定该媒体路径媒体类型。'
|
||||
'不加默认根据文件名自动识别媒体类型。'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -272,11 +314,19 @@ class LibraryScraper(_PluginBase):
|
||||
for path in paths:
|
||||
if not path:
|
||||
continue
|
||||
# 强制指定该路径媒体类型
|
||||
mtype = None
|
||||
if str(path).count("#") == 1:
|
||||
mtype = next(
|
||||
(mediaType for mediaType in MediaType.__members__.values() if
|
||||
mediaType.value == str(str(path).split("#")[1])),
|
||||
None)
|
||||
path = str(path).split("#")[0]
|
||||
scraper_path = Path(path)
|
||||
if not scraper_path.exists():
|
||||
logger.warning(f"媒体库刮削路径不存在:{path}")
|
||||
continue
|
||||
logger.info(f"开始刮削媒体库:{path} ...")
|
||||
logger.info(f"开始刮削媒体库:{path} {mtype} ...")
|
||||
# 遍历一层文件夹
|
||||
for sub_path in scraper_path.iterdir():
|
||||
if self._event.is_set():
|
||||
@@ -302,11 +352,11 @@ class LibraryScraper(_PluginBase):
|
||||
logger.warn(f"{sub_path} 可能不是媒体目录,请检查刮削目录配置,跳过 ...")
|
||||
continue
|
||||
logger.info(f"开始刮削目录:{sub_path} ...")
|
||||
self.__scrape_dir(path=sub_path, dir_meta=dir_meta)
|
||||
self.__scrape_dir(path=sub_path, dir_meta=dir_meta, mtype=mtype)
|
||||
logger.info(f"目录 {sub_path} 刮削完成")
|
||||
logger.info(f"媒体库 {path} 刮削完成")
|
||||
|
||||
def __scrape_dir(self, path: Path, dir_meta: MetaBase):
|
||||
def __scrape_dir(self, path: Path, dir_meta: MetaBase, mtype: MediaType = None):
|
||||
"""
|
||||
削刮一个目录,该目录必须是媒体文件目录
|
||||
"""
|
||||
@@ -325,6 +375,10 @@ class LibraryScraper(_PluginBase):
|
||||
meta_info = MetaInfo(file.stem)
|
||||
# 合并
|
||||
meta_info.merge(dir_meta)
|
||||
# 强制指定类型
|
||||
if mtype:
|
||||
meta_info.type = mtype
|
||||
|
||||
# 是否刮削
|
||||
scrap_metadata = settings.SCRAP_METADATA
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class MediaServerMsg(_PluginBase):
|
||||
# 主题色
|
||||
plugin_color = "#42A3DB"
|
||||
# 插件版本
|
||||
plugin_version = "1.0"
|
||||
plugin_version = "1.1"
|
||||
# 插件作者
|
||||
plugin_author = "jxxghp"
|
||||
# 作者主页
|
||||
@@ -80,6 +80,8 @@ class MediaServerMsg(_PluginBase):
|
||||
{"title": "停止播放", "value": "playback.stop|media.stop|PlaybackStop"},
|
||||
{"title": "用户标记", "value": "item.rate"},
|
||||
{"title": "测试", "value": "system.webhooktest"},
|
||||
{"title": "登录成功", "value": "user.authenticated"},
|
||||
{"title": "登录失败", "value": "user.authenticationfailed"},
|
||||
]
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ class MediaSyncDel(_PluginBase):
|
||||
# 主题色
|
||||
plugin_color = "#ff1a1a"
|
||||
# 插件版本
|
||||
plugin_version = "1.1"
|
||||
plugin_version = "1.2"
|
||||
# 插件作者
|
||||
plugin_author = "thsrite"
|
||||
# 作者主页
|
||||
@@ -53,6 +53,7 @@ class MediaSyncDel(_PluginBase):
|
||||
_cron: str = ""
|
||||
_notify = False
|
||||
_del_source = False
|
||||
_del_history = False
|
||||
_exclude_path = None
|
||||
_library_path = None
|
||||
_transferchain = None
|
||||
@@ -79,22 +80,39 @@ class MediaSyncDel(_PluginBase):
|
||||
self._cron = config.get("cron")
|
||||
self._notify = config.get("notify")
|
||||
self._del_source = config.get("del_source")
|
||||
self._del_history = config.get("del_history")
|
||||
self._exclude_path = config.get("exclude_path")
|
||||
self._library_path = config.get("library_path")
|
||||
|
||||
# 清理插件历史
|
||||
if self._del_history:
|
||||
self.del_data(key="history")
|
||||
self.update_config({
|
||||
"enabled": self._enabled,
|
||||
"sync_type": self._sync_type,
|
||||
"cron": self._cron,
|
||||
"notify": self._notify,
|
||||
"del_source": self._del_source,
|
||||
"del_history": False,
|
||||
"exclude_path": self._exclude_path,
|
||||
"library_path": self._library_path
|
||||
})
|
||||
|
||||
if self._enabled and str(self._sync_type) == "log":
|
||||
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
|
||||
# 媒体库同步删除日志方式
|
||||
if self._cron:
|
||||
try:
|
||||
self._scheduler.add_job(func=self.sync_del_by_log,
|
||||
trigger=CronTrigger.from_crontab(self._cron),
|
||||
name="媒体库同步删除")
|
||||
name="媒体库同步删除日志方式")
|
||||
except Exception as err:
|
||||
logger.error(f"定时任务配置错误:{str(err)}")
|
||||
# 推送实时消息
|
||||
self.systemmessage.put(f"执行周期配置错误:{str(err)}")
|
||||
else:
|
||||
self._scheduler.add_job(self.sync_del_by_log, "interval", minutes=30, name="媒体库同步删除")
|
||||
self._scheduler.add_job(self.sync_del_by_log, "interval", minutes=30,
|
||||
name="媒体库同步删除日志方式")
|
||||
|
||||
# 启动任务
|
||||
if self._scheduler.get_jobs():
|
||||
@@ -127,7 +145,7 @@ class MediaSyncDel(_PluginBase):
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 4
|
||||
'md': 3
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
@@ -143,7 +161,7 @@ class MediaSyncDel(_PluginBase):
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 4
|
||||
'md': 3
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
@@ -159,7 +177,7 @@ class MediaSyncDel(_PluginBase):
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 4
|
||||
'md': 3
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
@@ -170,6 +188,22 @@ class MediaSyncDel(_PluginBase):
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 3
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'del_history',
|
||||
'label': '删除历史',
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -293,11 +327,11 @@ class MediaSyncDel(_PluginBase):
|
||||
'props': {
|
||||
'type': 'info',
|
||||
'variant': 'tonal',
|
||||
'text': '关于路径映射(转移后文件):'
|
||||
'emby:/data/series/A.mp4,'
|
||||
'moviepilot:/mnt/link/series/A.mp4。'
|
||||
'text': '关于路径映射(转移后文件路径):'
|
||||
'emby:/data/A.mp4,'
|
||||
'moviepilot:/mnt/link/A.mp4。'
|
||||
'路径映射填/data:/mnt/link。'
|
||||
'不正确配置会导致查询不到转移记录!'
|
||||
'不正确配置会导致查询不到转移记录!(路径一样可不填)'
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -324,6 +358,51 @@ class MediaSyncDel(_PluginBase):
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VAlert',
|
||||
'props': {
|
||||
'type': 'info',
|
||||
'variant': 'tonal',
|
||||
'text': 'Scripter X配置文档:'
|
||||
'https://github.com/thsrite/'
|
||||
'MediaSyncDel/blob/main/MoviePilot/MoviePilot.md'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VAlert',
|
||||
'props': {
|
||||
'type': 'info',
|
||||
'variant': 'tonal',
|
||||
'text': '路径映射配置文档:'
|
||||
'https://github.com/thsrite/MediaSyncDel/blob/main/path.md'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -331,6 +410,7 @@ class MediaSyncDel(_PluginBase):
|
||||
"enabled": False,
|
||||
"notify": True,
|
||||
"del_source": False,
|
||||
"del_history": False,
|
||||
"library_path": "",
|
||||
"sync_type": "webhook",
|
||||
"cron": "*/30 * * * *",
|
||||
@@ -518,6 +598,25 @@ class MediaSyncDel(_PluginBase):
|
||||
# 集数
|
||||
episode_num = event_data.episode_id
|
||||
|
||||
"""
|
||||
执行删除逻辑
|
||||
"""
|
||||
if self._exclude_path and media_path and any(
|
||||
os.path.abspath(media_path).startswith(os.path.abspath(path)) for path in
|
||||
self._exclude_path.split(",")):
|
||||
logger.info(f"媒体路径 {media_path} 已被排除,暂不处理")
|
||||
# 发送消息通知网盘删除插件删除网盘资源
|
||||
self.eventmanager.send_event(EventType.NetworkDiskDel,
|
||||
{
|
||||
"media_path": media_path,
|
||||
"media_name": media_name,
|
||||
"tmdb_id": tmdb_id,
|
||||
"media_type": media_type,
|
||||
"season_num": season_num,
|
||||
"episode_num": episode_num,
|
||||
})
|
||||
return
|
||||
|
||||
# 兼容emby webhook season删除没有发送tmdbid
|
||||
if not tmdb_id and str(media_type) != 'Season':
|
||||
logger.error(f"{media_name} 同步删除失败,未获取到TMDB ID,请检查媒体库媒体是否刮削")
|
||||
@@ -578,19 +677,6 @@ class MediaSyncDel(_PluginBase):
|
||||
# 集数
|
||||
episode_num = event_data.episode_id
|
||||
|
||||
if not tmdb_id or not str(tmdb_id).isdigit():
|
||||
logger.error(f"{media_name} 同步删除失败,未获取到TMDB ID,请检查媒体库媒体是否刮削")
|
||||
return
|
||||
|
||||
self.__sync_del(media_type=media_type,
|
||||
media_name=media_name,
|
||||
media_path=media_path,
|
||||
tmdb_id=tmdb_id,
|
||||
season_num=season_num,
|
||||
episode_num=episode_num)
|
||||
|
||||
def __sync_del(self, media_type: str, media_name: str, media_path: str,
|
||||
tmdb_id: int, season_num: str, episode_num: str):
|
||||
"""
|
||||
执行删除逻辑
|
||||
"""
|
||||
@@ -610,6 +696,19 @@ class MediaSyncDel(_PluginBase):
|
||||
})
|
||||
return
|
||||
|
||||
if not tmdb_id or not str(tmdb_id).isdigit():
|
||||
logger.error(f"{media_name} 同步删除失败,未获取到TMDB ID,请检查媒体库媒体是否刮削")
|
||||
return
|
||||
|
||||
self.__sync_del(media_type=media_type,
|
||||
media_name=media_name,
|
||||
media_path=media_path,
|
||||
tmdb_id=tmdb_id,
|
||||
season_num=season_num,
|
||||
episode_num=episode_num)
|
||||
|
||||
def __sync_del(self, media_type: str, media_name: str, media_path: str,
|
||||
tmdb_id: int, season_num: str, episode_num: str):
|
||||
if not media_type:
|
||||
logger.error(f"{media_name} 同步删除失败,未获取到媒体类型,请检查媒体是否刮削")
|
||||
return
|
||||
@@ -656,6 +755,7 @@ class MediaSyncDel(_PluginBase):
|
||||
try:
|
||||
# 2、判断种子是否被删除完
|
||||
delete_flag, success_flag, handle_torrent_hashs = self.handle_torrent(
|
||||
type=transferhis.type,
|
||||
src=transferhis.src,
|
||||
torrent_hash=transferhis.download_hash)
|
||||
if not success_flag:
|
||||
@@ -807,7 +907,7 @@ class MediaSyncDel(_PluginBase):
|
||||
"""
|
||||
# 读取历史记录
|
||||
history = self.get_data('history') or []
|
||||
last_time = self.get_data("last_time")
|
||||
last_time = self.get_data("last_time") or None
|
||||
del_medias = []
|
||||
|
||||
# 媒体服务器类型,多个以,分隔
|
||||
@@ -832,7 +932,7 @@ class MediaSyncDel(_PluginBase):
|
||||
for del_media in del_medias:
|
||||
# 删除时间
|
||||
del_time = del_media.get("time")
|
||||
last_del_time = del_time
|
||||
last_del_time = del_time or datetime.datetime.now()
|
||||
# 媒体类型 Movie|Series|Season|Episode
|
||||
media_type = del_media.get("type")
|
||||
# 媒体名称 蜀山战纪
|
||||
@@ -851,7 +951,7 @@ class MediaSyncDel(_PluginBase):
|
||||
os.path.abspath(media_path).startswith(os.path.abspath(path)) for path in
|
||||
self._exclude_path.split(",")):
|
||||
logger.info(f"媒体路径 {media_path} 已被排除,暂不处理")
|
||||
self.save_data("last_time", last_del_time or datetime.datetime.now())
|
||||
self.save_data("last_time", last_del_time)
|
||||
return
|
||||
|
||||
# 处理路径映射 (处理同一媒体多分辨率的情况)
|
||||
@@ -894,14 +994,14 @@ class MediaSyncDel(_PluginBase):
|
||||
episode=media_episode,
|
||||
dest=media_path)
|
||||
else:
|
||||
self.save_data("last_time", last_del_time or datetime.datetime.now())
|
||||
self.save_data("last_time", last_del_time)
|
||||
continue
|
||||
|
||||
logger.info(f"正在同步删除 {msg}")
|
||||
|
||||
if not transfer_history:
|
||||
logger.info(f"未获取到 {msg} 转移记录,请检查路径映射是否配置错误,请检查tmdbid获取是否正确")
|
||||
self.save_data("last_time", last_del_time or datetime.datetime.now())
|
||||
self.save_data("last_time", last_del_time)
|
||||
continue
|
||||
|
||||
logger.info(f"获取到删除历史记录数量 {len(transfer_history)}")
|
||||
@@ -916,7 +1016,7 @@ class MediaSyncDel(_PluginBase):
|
||||
if title not in media_name:
|
||||
logger.warn(
|
||||
f"当前转移记录 {transferhis.id} {title} {transferhis.tmdbid} 与删除媒体{media_name}不符,防误删,暂不自动删除")
|
||||
self.save_data("last_time", last_del_time or datetime.datetime.now())
|
||||
self.save_data("last_time", last_del_time)
|
||||
continue
|
||||
image = transferhis.image or image
|
||||
# 0、删除转移记录
|
||||
@@ -931,6 +1031,7 @@ class MediaSyncDel(_PluginBase):
|
||||
try:
|
||||
# 2、判断种子是否被删除完
|
||||
delete_flag, success_flag, handle_torrent_hashs = self.handle_torrent(
|
||||
type=transferhis.type,
|
||||
src=transferhis.src,
|
||||
torrent_hash=transferhis.download_hash)
|
||||
if not success_flag:
|
||||
@@ -981,9 +1082,9 @@ class MediaSyncDel(_PluginBase):
|
||||
# 保存历史
|
||||
self.save_data("history", history)
|
||||
|
||||
self.save_data("last_time", last_del_time or datetime.datetime.now())
|
||||
self.save_data("last_time", last_del_time)
|
||||
|
||||
def handle_torrent(self, src: str, torrent_hash: str):
|
||||
def handle_torrent(self, type: str, src: str, torrent_hash: str):
|
||||
"""
|
||||
判断种子是否局部删除
|
||||
局部删除则暂停种子
|
||||
@@ -1085,17 +1186,80 @@ class MediaSyncDel(_PluginBase):
|
||||
handle_torrent_hashs.append(download_id)
|
||||
|
||||
# 处理辅种
|
||||
handle_cnt = self.__del_seed(download=download,
|
||||
download_id=download_id,
|
||||
action_flag="del" if delete_flag else 'stop',
|
||||
handle_torrent_hashs=handle_torrent_hashs)
|
||||
|
||||
return delete_flag, True, handle_cnt
|
||||
handle_torrent_hashs = self.__del_seed(download=download,
|
||||
download_id=download_id,
|
||||
delete_flag=delete_flag,
|
||||
handle_torrent_hashs=handle_torrent_hashs)
|
||||
# 处理合集
|
||||
if str(type) == "电视剧":
|
||||
handle_torrent_hashs = self.__del_collection(src=src,
|
||||
delete_flag=delete_flag,
|
||||
torrent_hash=torrent_hash,
|
||||
download_files=download_files,
|
||||
handle_torrent_hashs=handle_torrent_hashs)
|
||||
return delete_flag, True, handle_torrent_hashs
|
||||
except Exception as e:
|
||||
logger.error(f"删种失败: {str(e)}")
|
||||
return False, False, 0
|
||||
|
||||
def __del_seed(self, download, download_id, action_flag, handle_torrent_hashs):
|
||||
def __del_collection(self, src: str, delete_flag: bool, torrent_hash: str, download_files: list,
|
||||
handle_torrent_hashs: list):
|
||||
"""
|
||||
处理合集
|
||||
"""
|
||||
try:
|
||||
download_file = self._downloadhis.get_file_by_fullpath(fullpath=src)
|
||||
# src查询记录 判断download_hash是否不一致
|
||||
if download_file and str(download_file.download_hash) != str(torrent_hash):
|
||||
# 查询新download_hash对应files数量
|
||||
hash_download_files = self._downloadhis.get_files_by_hash(
|
||||
download_hash=download_file.download_hash)
|
||||
# 新download_hash对应files数量 > 删种download_hash对应files数量 = 合集种子
|
||||
if hash_download_files \
|
||||
and len(hash_download_files) > len(download_files) \
|
||||
and hash_download_files[0].id > download_files[-1].id:
|
||||
# 查询未删除数
|
||||
no_del_cnt = 0
|
||||
for hash_download_file in hash_download_files:
|
||||
if hash_download_file and hash_download_file.state and int(
|
||||
hash_download_file.state) == 1:
|
||||
no_del_cnt += 1
|
||||
if no_del_cnt > 0:
|
||||
logger.info(f"合集种子 {download_file.download_hash} 文件未完全删除,执行暂停种子操作")
|
||||
delete_flag = False
|
||||
|
||||
# 删除合集种子
|
||||
if delete_flag:
|
||||
if str(download_file.downloader) == "transmission":
|
||||
self.tr.delete_torrents(delete_file=True,
|
||||
ids=download_file.download_hash)
|
||||
else:
|
||||
self.qb.delete_torrents(delete_file=True,
|
||||
ids=download_file.download_hash)
|
||||
|
||||
logger.info(f"删除合集种子 {download_file.downloader} {download_file.download_hash}")
|
||||
else:
|
||||
# 暂停合集种子
|
||||
if str(download_file.downloader) == "transmission":
|
||||
self.tr.stop_torrents(ids=download_file.download_hash)
|
||||
else:
|
||||
self.qb.stop_torrents(ids=download_file.download_hash)
|
||||
logger.info(f"暂停合集种子 {download_file.downloader} {download_file.download_hash}")
|
||||
# 已处理种子+1
|
||||
handle_torrent_hashs.append(download_file.download_hash)
|
||||
|
||||
# 处理合集辅种
|
||||
handle_torrent_hashs = self.__del_seed(download=download_file.downloader,
|
||||
download_id=download_file.download_hash,
|
||||
delete_flag=delete_flag,
|
||||
handle_torrent_hashs=handle_torrent_hashs)
|
||||
except Exception as e:
|
||||
logger.error(f"处理 {torrent_hash} 合集失败")
|
||||
print(str(e))
|
||||
|
||||
return handle_torrent_hashs
|
||||
|
||||
def __del_seed(self, download, download_id, delete_flag, handle_torrent_hashs):
|
||||
"""
|
||||
删除辅种
|
||||
"""
|
||||
@@ -1109,8 +1273,8 @@ class MediaSyncDel(_PluginBase):
|
||||
# 有辅种记录则处理辅种
|
||||
if seed_history and isinstance(seed_history, list):
|
||||
for history in seed_history:
|
||||
downloader = history['downloader']
|
||||
torrents = history['torrents']
|
||||
downloader = history.get("downloader")
|
||||
torrents = history.get("torrents")
|
||||
if not downloader or not torrents:
|
||||
return
|
||||
if not isinstance(torrents, list):
|
||||
@@ -1121,28 +1285,33 @@ class MediaSyncDel(_PluginBase):
|
||||
handle_torrent_hashs.append(torrent)
|
||||
if str(download) == "qbittorrent":
|
||||
# 删除辅种
|
||||
if action_flag == "del":
|
||||
if delete_flag:
|
||||
logger.info(f"删除辅种:{downloader} - {torrent}")
|
||||
self.qb.delete_torrents(delete_file=True,
|
||||
ids=torrent)
|
||||
# 暂停辅种
|
||||
if action_flag == "stop":
|
||||
self.qb.stop_torrents(torrent)
|
||||
else:
|
||||
self.qb.stop_torrents(ids=torrent)
|
||||
logger.info(f"辅种:{downloader} - {torrent} 暂停")
|
||||
else:
|
||||
# 删除辅种
|
||||
if action_flag == "del":
|
||||
if delete_flag:
|
||||
logger.info(f"删除辅种:{downloader} - {torrent}")
|
||||
self.tr.delete_torrents(delete_file=True,
|
||||
ids=torrent)
|
||||
# 暂停辅种
|
||||
if action_flag == "stop":
|
||||
self.tr.stop_torrents(torrent)
|
||||
else:
|
||||
self.tr.stop_torrents(ids=torrent)
|
||||
logger.info(f"辅种:{downloader} - {torrent} 暂停")
|
||||
break
|
||||
|
||||
# 处理辅种的辅种
|
||||
handle_torrent_hashs = self.__del_seed(download=downloader,
|
||||
download_id=torrent,
|
||||
delete_flag=delete_flag,
|
||||
handle_torrent_hashs=handle_torrent_hashs)
|
||||
|
||||
# 删除辅种历史
|
||||
if action_flag == "del":
|
||||
if delete_flag:
|
||||
self.del_data(key=history_key,
|
||||
plugin_id=plugin_id)
|
||||
return handle_torrent_hashs
|
||||
@@ -1153,7 +1322,7 @@ class MediaSyncDel(_PluginBase):
|
||||
获取emby日志列表、解析emby日志
|
||||
"""
|
||||
|
||||
def __parse_log(file_name: str, del_list: list):
|
||||
def __parse_log(file_name: str, del_list: list, last_time):
|
||||
"""
|
||||
解析emby日志
|
||||
"""
|
||||
@@ -1246,7 +1415,9 @@ class MediaSyncDel(_PluginBase):
|
||||
del_medias = []
|
||||
log_files.reverse()
|
||||
for log_file in log_files:
|
||||
del_medias = __parse_log(log_file, del_medias)
|
||||
del_medias = __parse_log(file_name=log_file,
|
||||
del_list=del_medias,
|
||||
last_time=last_time)
|
||||
|
||||
return del_medias
|
||||
|
||||
@@ -1256,7 +1427,7 @@ class MediaSyncDel(_PluginBase):
|
||||
获取jellyfin日志列表、解析jellyfin日志
|
||||
"""
|
||||
|
||||
def __parse_log(file_name: str, del_list: list):
|
||||
def __parse_log(file_name: str, del_list: list, last_time):
|
||||
"""
|
||||
解析jellyfin日志
|
||||
"""
|
||||
@@ -1349,7 +1520,9 @@ class MediaSyncDel(_PluginBase):
|
||||
del_medias = []
|
||||
log_files.reverse()
|
||||
for log_file in log_files:
|
||||
del_medias = __parse_log(log_file, del_medias)
|
||||
del_medias = __parse_log(file_name=log_file,
|
||||
del_list=del_medias,
|
||||
last_time=last_time)
|
||||
|
||||
return del_medias
|
||||
|
||||
@@ -1383,7 +1556,8 @@ class MediaSyncDel(_PluginBase):
|
||||
# 查询下载hash
|
||||
download_hash = self._downloadhis.get_hash_by_fullpath(src)
|
||||
if download_hash:
|
||||
self.handle_torrent(src=src, torrent_hash=download_hash)
|
||||
download_history = self._downloadhis.get_by_hash(download_hash)
|
||||
self.handle_torrent(type=download_history.type, src=src, torrent_hash=download_hash)
|
||||
else:
|
||||
logger.warn(f"未查询到文件 {src} 对应的下载记录")
|
||||
|
||||
|
||||
@@ -69,81 +69,102 @@ class MessageForward(_PluginBase):
|
||||
拼装插件配置页面,需要返回两块数据:1、页面配置;2、数据结构
|
||||
"""
|
||||
return [
|
||||
{
|
||||
'component': 'VForm',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 6
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'enabled',
|
||||
'label': '开启转发'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VTextarea',
|
||||
'props': {
|
||||
'model': 'wechat',
|
||||
'rows': '3',
|
||||
'label': '应用配置',
|
||||
'placeholder': 'appid:corpid:appsecret(一行一个配置)'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VTextarea',
|
||||
'props': {
|
||||
'model': 'pattern',
|
||||
'rows': '3',
|
||||
'label': '正则配置',
|
||||
'placeholder': '对应上方应用配置,一行一个,一一对应'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
], {
|
||||
"enabled": False,
|
||||
"wechat": "",
|
||||
"pattern": ""
|
||||
}
|
||||
{
|
||||
'component': 'VForm',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 6
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'enabled',
|
||||
'label': '开启转发'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VTextarea',
|
||||
'props': {
|
||||
'model': 'wechat',
|
||||
'rows': '3',
|
||||
'label': '应用配置',
|
||||
'placeholder': 'appid:corpid:appsecret(一行一个配置)'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VTextarea',
|
||||
'props': {
|
||||
'model': 'pattern',
|
||||
'rows': '3',
|
||||
'label': '正则配置',
|
||||
'placeholder': '对应上方应用配置,一行一个,一一对应'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VAlert',
|
||||
'props': {
|
||||
'type': 'info',
|
||||
'variant': 'tonal',
|
||||
'text': '根据正则表达式,把MoviePilot的消息转发到多个微信应用。'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
], {
|
||||
"enabled": False,
|
||||
"wechat": "",
|
||||
"pattern": ""
|
||||
}
|
||||
|
||||
def get_page(self) -> List[dict]:
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user