feat:插件移植公共定时服务

This commit is contained in:
jxxghp
2024-02-25 17:10:35 +08:00
parent 65705f82af
commit 22d5550a9c
30 changed files with 1085 additions and 775 deletions

View File

@@ -2,7 +2,7 @@
"AutoSignIn": {
"name": "站点自动签到",
"description": "自动模拟登录站点、签到。",
"version": "1.3.1",
"version": "1.4",
"icon": "signin.png",
"author": "thsrite",
"level": 2
@@ -18,7 +18,7 @@
"SiteStatistic": {
"name": "站点数据统计",
"description": "自动统计和展示站点数据。",
"version": "1.6",
"version": "1.7",
"icon": "statistic.png",
"author": "lightolly",
"level": 2
@@ -26,7 +26,7 @@
"SiteRefresh": {
"name": "站点自动更新",
"description": "使用浏览器模拟登录站点获取Cookie和UA。",
"version": "1.1",
"version": "1.2",
"icon": "Chrome_A.png",
"author": "thsrite",
"level": 2
@@ -34,7 +34,7 @@
"DoubanSync": {
"name": "豆瓣想看",
"description": "同步豆瓣想看数据,自动添加订阅。",
"version": "1.2",
"version": "1.3",
"icon": "douban.png",
"author": "jxxghp",
"level": 2
@@ -42,7 +42,7 @@
"DirMonitor": {
"name": "目录监控",
"description": "监控目录文件发生变化时实时整理到媒体库。",
"version": "1.6",
"version": "1.7",
"icon": "directory.png",
"author": "jxxghp",
"level": 1
@@ -58,7 +58,7 @@
"DoubanRank": {
"name": "豆瓣榜单订阅",
"description": "监控豆瓣热门榜单,自动添加订阅。",
"version": "1.4",
"version": "1.5",
"icon": "movie.jpg",
"author": "jxxghp",
"level": 2
@@ -66,7 +66,7 @@
"LibraryScraper": {
"name": "媒体库刮削",
"description": "定时对媒体库进行刮削,补齐缺失元数据和图片。",
"version": "1.2",
"version": "1.3",
"icon": "scraper.png",
"author": "jxxghp",
"level": 1
@@ -74,7 +74,7 @@
"TorrentRemover": {
"name": "自动删种",
"description": "自动删除下载器中的下载任务。",
"version": "1.1",
"version": "1.2",
"icon": "delete.jpg",
"author": "jxxghp",
"level": 2
@@ -82,7 +82,7 @@
"MediaSyncDel": {
"name": "媒体文件同步删除",
"description": "同步删除历史记录、源文件和下载任务。",
"version": "1.2",
"version": "1.3",
"icon": "mediasyncdel.png",
"author": "thsrite",
"level": 1
@@ -98,7 +98,7 @@
"SpeedLimiter": {
"name": "播放限速",
"description": "外网播放媒体库视频时,自动对下载器进行限速。",
"version": "1.0",
"version": "1.1",
"icon": "Librespeed_A.png",
"author": "Shurelol",
"level": 1
@@ -106,7 +106,7 @@
"CloudflareSpeedTest": {
"name": "Cloudflare IP优选",
"description": "🌩 测试 Cloudflare CDN 延迟和速度自动优选IP。",
"version": "1.0",
"version": "1.1",
"icon": "cloudflare.jpg",
"author": "thsrite",
"level": 1
@@ -114,7 +114,7 @@
"BestFilmVersion": {
"name": "收藏洗版",
"description": "Jellyfin/Emby/Plex点击收藏电影后自动订阅洗版。",
"version": "2.0",
"version": "2.1",
"icon": "like.jpg",
"author": "wlj",
"level": 2
@@ -130,7 +130,7 @@
"MediaServerRefresh": {
"name": "媒体库服务器刷新",
"description": "入库后自动刷新Emby/Jellyfin/Plex服务器海报墙。",
"version": "1.1",
"version": "1.2",
"icon": "refresh2.png",
"author": "jxxghp",
"level": 1
@@ -170,7 +170,7 @@
"AutoBackup": {
"name": "自动备份",
"description": "自动备份数据和配置文件。",
"version": "1.0",
"version": "1.1",
"icon": "Time_machine_B.png",
"author": "thsrite",
"level": 1
@@ -178,7 +178,7 @@
"IYUUAutoSeed": {
"name": "IYUU自动辅种",
"description": "基于IYUU官方Api实现自动辅种。",
"version": "1.2",
"version": "1.3",
"icon": "IYUU.png",
"author": "jxxghp",
"level": 2
@@ -186,7 +186,7 @@
"TorrentTransfer": {
"name": "自动转移做种",
"description": "定期转移下载器中的做种任务到另一个下载器。",
"version": "1.1",
"version": "1.2",
"icon": "seed.png",
"author": "jxxghp",
"level": 2
@@ -194,7 +194,7 @@
"RssSubscribe": {
"name": "自定义订阅",
"description": "定时刷新RSS报文识别内容后添加订阅或直接下载。",
"version": "1.0",
"version": "1.1",
"icon": "rss.png",
"author": "jxxghp",
"level": 2
@@ -202,7 +202,7 @@
"SyncDownloadFiles": {
"name": "下载器文件同步",
"description": "同步下载器的文件信息到数据库,删除文件时联动删除下载任务。",
"version": "1.0",
"version": "1.1",
"icon": "Youtube-dl_A.png",
"author": "thsrite",
"level": 1
@@ -210,7 +210,7 @@
"BrushFlow": {
"name": "站点刷流",
"description": "自动托管刷流,将会提高对应站点的访问频率。",
"version": "1.1",
"version": "1.2",
"icon": "brush.jpg",
"author": "jxxghp",
"level": 2
@@ -218,7 +218,7 @@
"DownloadingMsg": {
"name": "下载进度推送",
"description": "定时推送正在下载进度。",
"version": "1.0",
"version": "1.1",
"icon": "downloadmsg.png",
"author": "thsrite",
"level": 2
@@ -226,7 +226,7 @@
"AutoClean": {
"name": "定时清理媒体库",
"description": "定时清理用户下载的种子、源文件、媒体库文件。",
"version": "1.0",
"version": "1.1",
"icon": "clean.png",
"author": "thsrite",
"level": 2
@@ -234,7 +234,7 @@
"InvitesSignin": {
"name": "药丸签到",
"description": "药丸论坛签到。",
"version": "1.2",
"version": "1.3",
"icon": "invites.png",
"author": "thsrite",
"level": 2
@@ -242,7 +242,7 @@
"PersonMeta": {
"name": "演职人员刮削",
"description": "刮削演职人员图片以及中文名称。",
"version": "1.1",
"version": "1.2",
"icon": "actor.png",
"author": "jxxghp",
"level": 1
@@ -250,7 +250,7 @@
"MoviePilotUpdateNotify": {
"name": "MoviePilot更新推送",
"description": "MoviePilot推送release更新通知、自动重启。",
"version": "1.0",
"version": "1.1",
"icon": "Moviepilot_A.png",
"author": "thsrite",
"level": 1
@@ -306,7 +306,7 @@
"EpisodeGroupMeta": {
"name": "TMDB剧集组刮削",
"description": "从TMDB剧集组刮削季集的实际顺序",
"version": "1.0",
"version": "1.1",
"icon": "Element_A.png",
"author": "叮叮当",
"level": 1
@@ -338,7 +338,7 @@
"DownloadSiteTag": {
"name": "下载任务分类与标签",
"description": "自动给下载任务分类与打站点标签、剧集名称标签",
"version": "1.7",
"version": "1.8",
"icon": "Youtube-dl_B.png",
"author": "叮叮当",
"level": 1
@@ -346,7 +346,7 @@
"RemoveLink": {
"name": "清理硬链接",
"description": "监控目录内文件被删除时,同步删除监控目录内所有和它硬链接的文件",
"version": "1.1",
"version": "1.2",
"icon": "Ombi_A.png",
"author": "DzAvril",
"level": 1
@@ -354,7 +354,7 @@
"LinkMonitor": {
"name": "实时硬链接",
"description": "监控目录文件变化,实时硬链接。",
"version": "1.2",
"version": "1.3",
"icon": "Linkace_C.png",
"author": "jxxghp",
"level": 1

View File

@@ -25,7 +25,7 @@ class AutoBackup(_PluginBase):
# 插件图标
plugin_icon = "Time_machine_B.png"
# 插件版本
plugin_version = "1.0"
plugin_version = "1.1"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -60,32 +60,21 @@ class AutoBackup(_PluginBase):
self._onlyonce = config.get("onlyonce")
# 加载模块
if self._enabled:
# 定时服务
if self._onlyonce:
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if self._cron:
try:
self._scheduler.add_job(func=self.__backup,
trigger=CronTrigger.from_crontab(self._cron),
name="自动备份")
except Exception as err:
logger.error(f"定时任务配置错误:{str(err)}")
if self._onlyonce:
logger.info(f"自动备份服务启动,立即运行一次")
self._scheduler.add_job(func=self.__backup, trigger='date',
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3),
name="自动备份")
# 关闭一次性开关
self._onlyonce = False
self.update_config({
"onlyonce": False,
"cron": self._cron,
"enabled": self._enabled,
"cnt": self._cnt,
"notify": self._notify,
})
logger.info(f"自动备份服务启动,立即运行一次")
self._scheduler.add_job(func=self.__backup, trigger='date',
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3),
name="自动备份")
# 关闭一次性开关
self._onlyonce = False
self.update_config({
"onlyonce": False,
"cron": self._cron,
"enabled": self._enabled,
"cnt": self._cnt,
"notify": self._notify,
})
# 启动任务
if self._scheduler.get_jobs():
@@ -186,6 +175,26 @@ class AutoBackup(_PluginBase):
"description": "MoviePilot备份",
}]
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._cron:
return [{
"id": "AutoBackup",
"name": "自动备份定时服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.__backup,
"kwargs": {}
}]
def backup(self) -> schemas.Response:
"""
API调用备份

View File

@@ -27,7 +27,7 @@ class AutoClean(_PluginBase):
# 插件图标
plugin_icon = "clean.png"
# 插件版本
plugin_version = "1.0"
plugin_version = "1.1"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -72,18 +72,10 @@ class AutoClean(_PluginBase):
if self._enabled:
self._downloadhis = DownloadHistoryOper()
self._transferhis = TransferHistoryOper()
# 定时服务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if self._cron:
try:
self._scheduler.add_job(func=self.__clean,
trigger=CronTrigger.from_crontab(self._cron),
name="定时清理媒体库")
except Exception as err:
logger.error(f"定时任务配置错误:{str(err)}")
if self._onlyonce:
# 定时服务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info(f"定时清理媒体库服务启动,立即运行一次")
self._scheduler.add_job(func=self.__clean, trigger='date',
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3),
@@ -100,10 +92,10 @@ class AutoClean(_PluginBase):
"notify": self._notify,
})
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
def __get_clean_date(self, deltatime: str = None):
# 清理日期
@@ -263,6 +255,28 @@ class AutoClean(_PluginBase):
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._cron:
return [
{
"id": "AutoClean",
"name": "清理媒体库定时服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.__clean,
"kwargs": {}
}
]
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构

View File

@@ -36,7 +36,7 @@ class AutoSignIn(_PluginBase):
# 插件图标
plugin_icon = "signin.png"
# 插件版本
plugin_version = "1.3.1"
plugin_version = "1.4"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -107,11 +107,10 @@ class AutoSignIn(_PluginBase):
self._site_schema = ModuleHelper.load('app.plugins.autosignin.sites',
filter_func=lambda _, obj: hasattr(obj, 'match'))
# 定时服务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
# 立即运行一次
if self._onlyonce:
# 定时服务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info("站点自动签到服务启动,立即运行一次")
self._scheduler.add_job(func=self.sign_in, trigger='date',
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3),
@@ -122,75 +121,10 @@ class AutoSignIn(_PluginBase):
# 保存配置
self.__update_config()
# 周期运行
if self._enabled:
if self._cron:
try:
if str(self._cron).strip().count(" ") == 4:
self._scheduler.add_job(func=self.sign_in,
trigger=CronTrigger.from_crontab(self._cron),
name="站点自动签到")
logger.info(f"站点自动签到服务启动,执行周期 {self._cron}")
else:
# 2.3/9-23
crons = str(self._cron).strip().split("/")
if len(crons) == 2:
# 2.3
cron = crons[0]
# 9-23
times = crons[1].split("-")
if len(times) == 2:
# 9
self._start_time = int(times[0])
# 23
self._end_time = int(times[1])
if self._start_time and self._end_time:
self._scheduler.add_job(func=self.sign_in,
trigger="interval",
hours=float(str(cron).strip()),
name="站点自动签到")
logger.info(
f"站点自动签到服务启动,执行周期 {self._start_time}点-{self._end_time}点 每{cron}小时执行一次")
else:
logger.error("站点自动签到服务启动失败,周期格式错误")
# 推送实时消息
self.systemmessage.put(f"执行周期配置错误")
self._cron = ""
self._enabled = False
self.__update_config()
else:
# 默认0-24 按照周期运行
self._start_time = 0
self._end_time = 24
self._scheduler.add_job(func=self.sign_in,
trigger="interval",
hours=float(str(self._cron).strip()),
name="站点自动签到")
logger.info(
f"站点自动签到服务启动,执行周期 {self._start_time}点-{self._end_time}点 每{self._cron}小时执行一次")
except Exception as err:
logger.error(f"定时任务配置错误:{str(err)}")
# 推送实时消息
self.systemmessage.put(f"执行周期配置错误:{str(err)}")
self._cron = ""
self._enabled = False
self.__update_config()
else:
# 随机时间
triggers = TimerUtils.random_scheduler(num_executions=2,
begin_hour=9,
end_hour=23,
max_interval=6 * 60,
min_interval=2 * 60)
for trigger in triggers:
self._scheduler.add_job(self.sign_in, "cron",
hour=trigger.hour, minute=trigger.minute,
name="站点自动签到")
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
def get_state(self) -> bool:
return self._enabled
@@ -246,6 +180,87 @@ class AutoSignIn(_PluginBase):
"description": "使用站点域名签到站点",
}]
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._cron:
try:
if str(self._cron).strip().count(" ") == 4:
return [{
"id": "AutoSignIn",
"name": "站点自动签到服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.sign_in,
"kwargs": {}
}]
else:
# 2.3/9-23
crons = str(self._cron).strip().split("/")
if len(crons) == 2:
# 2.3
cron = crons[0]
# 9-23
times = crons[1].split("-")
if len(times) == 2:
# 9
self._start_time = int(times[0])
# 23
self._end_time = int(times[1])
if self._start_time and self._end_time:
return [{
"id": "AutoSignIn",
"name": "站点自动签到服务",
"trigger": "interval",
"func": self.sign_in,
"kwargs": {
"hours": float(str(cron).strip()),
}
}]
else:
logger.error("站点自动签到服务启动失败,周期格式错误")
else:
# 默认0-24 按照周期运行
return [{
"id": "AutoSignIn",
"name": "站点自动签到服务",
"trigger": "interval",
"func": self.sign_in,
"kwargs": {
"hours": float(str(self._cron).strip()),
}
}]
except Exception as err:
logger.error(f"定时任务配置错误:{str(err)}")
elif self._enabled:
# 随机时间
triggers = TimerUtils.random_scheduler(num_executions=2,
begin_hour=9,
end_hour=23,
max_interval=6 * 60,
min_interval=2 * 60)
ret_jobs = []
for trigger in triggers:
ret_jobs.append({
"id": f"AutoSignIn|{trigger.hour}:{trigger.minute}",
"name": "站点自动签到服务",
"trigger": "cron",
"func": self.sign_in,
"kwargs": {
"hour": trigger.hour,
"minute": trigger.minute
}
})
return ret_jobs
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构

View File

@@ -34,7 +34,7 @@ class BestFilmVersion(_PluginBase):
# 插件图标
plugin_icon = "like.jpg"
# 插件版本
plugin_version = "2.0"
plugin_version = "2.1"
# 插件作者
plugin_author = "wlj"
# 作者主页
@@ -73,33 +73,19 @@ class BestFilmVersion(_PluginBase):
self._webhook_enabled = config.get("webhook_enabled")
self._only_once = config.get("only_once")
if self._enabled:
if self._only_once:
self._only_once = False
self.update_config({
"enabled": self._enabled,
"cron": self._cron,
"notify": self._notify,
"webhook_enabled": self._webhook_enabled,
"only_once": self._only_once
})
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if not self._webhook_enabled:
if self._cron:
try:
self._scheduler.add_job(func=self.sync,
trigger=CronTrigger.from_crontab(self._cron),
name="收藏洗版")
except Exception as err:
logger.error(f"定时任务配置错误:{str(err)}")
# 推送实时消息
self.systemmessage.put(f"执行周期配置错误:{str(err)}")
else:
self._scheduler.add_job(self.sync, "interval", minutes=30, name="收藏洗版")
if self._only_once:
self._only_once = False
self.update_config({
"enabled": self._enabled,
"cron": self._cron,
"notify": self._notify,
"webhook_enabled": self._webhook_enabled,
"only_once": self._only_once
})
self._scheduler.add_job(self.sync, 'date',
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3),
name="立即运行收藏洗版")
self._scheduler.add_job(self.sync, 'date',
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3),
name="立即运行收藏洗版")
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
@@ -124,6 +110,39 @@ class BestFilmVersion(_PluginBase):
"""
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and not self._webhook_enabled:
if self._cron:
return [{
"id": "BestFilmVersion",
"name": "收藏洗版定时服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.sync,
"kwargs": {}
}]
return [
{
"id": "BestFilmVersion",
"name": "收藏洗版定时服务",
"trigger": "interval",
"func": self.sync,
"kwargs": {
"minutes": 30
}
}
]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构

View File

@@ -31,7 +31,7 @@ class BrushFlow(_PluginBase):
# 插件图标
plugin_icon = "brush.jpg"
# 插件版本
plugin_version = "1.1"
plugin_version = "1.2"
# 插件作者
plugin_author = "jxxghp"
# 作者主页
@@ -50,8 +50,9 @@ class BrushFlow(_PluginBase):
sites = None
qb = None
tr = None
# 添加种子定时
# 添加种子定时 分钟
_cron = 10
_task_enable = False
# 检查种子定时
_check_interval = 5
# 退出事件
@@ -137,6 +138,7 @@ class BrushFlow(_PluginBase):
self.stop_service()
# 启动定时任务 & 立即运行一次
self._task_enable = False
if self.get_state() or self._onlyonce:
self.qb = Qbittorrent()
self.tr = Transmission()
@@ -231,15 +233,11 @@ class BrushFlow(_PluginBase):
return
# 启动任务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info(f"站点刷流服务启动,周期:{self._cron}分钟")
try:
self._scheduler.add_job(self.brush, 'interval', minutes=self._cron)
except Exception as e:
logger.error(f"站点刷流服务启动失败:{str(e)}")
self.systemmessage.put(f"站点刷流服务启动失败:{str(e)}")
return
self._task_enable = True
# 仅一次
if self._onlyonce:
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info(f"站点刷流服务启动,立即运行一次")
self._scheduler.add_job(self.brush, 'date',
run_date=datetime.now(
@@ -249,14 +247,14 @@ class BrushFlow(_PluginBase):
# 关闭一次性开关
self._onlyonce = False
self.__update_config()
if self._scheduler.get_jobs():
# 增加检查任务
self._scheduler.add_job(self.check, 'interval',
minutes=self._check_interval,
name="站点刷流检查服务")
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
if self._scheduler.get_jobs():
# 增加检查任务
self._scheduler.add_job(self.check, 'interval',
minutes=self._check_interval,
name="站点刷流检查服务")
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
def get_state(self) -> bool:
return True if self._enabled and self._brushsites and self._downloader else False
@@ -268,6 +266,27 @@ class BrushFlow(_PluginBase):
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._task_enable:
return [{
"id": "BrushFlow",
"name": "站点刷流服务",
"trigger": "interval",
"func": self.brush,
"kwargs": {"minutes": self._cron}
}]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构
@@ -756,26 +775,26 @@ class BrushFlow(_PluginBase):
]
},
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
'cols': 12,
},
'content': [
{
'component': 'VAlert',
'props': {
'type': 'info',
'variant': 'tonal',
'text': '注意排除H&R并不保证能完全适配所有站点部分站点在列表页不显示H&R标志但实际上是有H&R的请注意核对使用'
}
}
]
}
]
}
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
'cols': 12,
},
'content': [
{
'component': 'VAlert',
'props': {
'type': 'info',
'variant': 'tonal',
'text': '注意排除H&R并不保证能完全适配所有站点部分站点在列表页不显示H&R标志但实际上是有H&R的请注意核对使用'
}
}
]
}
]
}
]
}
], {
@@ -1921,7 +1940,7 @@ class BrushFlow(_PluginBase):
return len(torrents) or 0
@staticmethod
def __get_pubminutes(pubdate: str) -> int:
def __get_pubminutes(pubdate: str) -> float:
"""
将字符串转换为时间,并计算与当前时间差)(分钟)
"""

View File

@@ -32,7 +32,7 @@ class CloudflareSpeedTest(_PluginBase):
# 插件图标
plugin_icon = "cloudflare.jpg"
# 插件版本
plugin_version = "1.0"
plugin_version = "1.1"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -81,34 +81,25 @@ class CloudflareSpeedTest(_PluginBase):
self._notify = config.get("notify")
self._check = config.get("check")
if self.get_state() or self._onlyonce:
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if self.get_state() and self._onlyonce:
try:
if self.get_state() and self._cron:
logger.info(f"Cloudflare CDN优选服务启动周期:{self._cron}")
self._scheduler.add_job(func=self.__cloudflareSpeedTest,
trigger=CronTrigger.from_crontab(self._cron),
name="Cloudflare优选")
if self._onlyonce:
logger.info(f"Cloudflare CDN优选服务启动立即运行一次")
self._scheduler.add_job(func=self.__cloudflareSpeedTest, trigger='date',
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3),
name="Cloudflare优选")
# 关闭一次性开关
self._onlyonce = False
self.__update_config()
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info(f"Cloudflare CDN优选服务启动立即运行一次")
self._scheduler.add_job(func=self.__cloudflareSpeedTest, trigger='date',
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3),
name="Cloudflare优选")
# 关闭一次性开关
self._onlyonce = False
self.__update_config()
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
except Exception as err:
logger.error(f"Cloudflare CDN优选服务出错{str(err)}")
self.systemmessage.put(f"Cloudflare CDN优选服务出错{str(err)}")
return
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
@eventmanager.register(EventType.PluginAction)
def __cloudflareSpeedTest(self, event: Event = None):
"""
@@ -324,9 +315,11 @@ class CloudflareSpeedTest(_PluginBase):
install_flag = True
# 重装后数据库有版本数据,但是本地没有则重装
if not install_flag and release_version == self._version and not Path(
f'{self._cf_path}/{self._binary_name}').exists() and not Path(
f'{self._cf_path}/CloudflareST.exe').exists():
if not install_flag \
and release_version == self._version \
and not Path(
f'{self._cf_path}/{self._binary_name}').exists() \
and not Path(f'{self._cf_path}/CloudflareST.exe').exists():
logger.warn(f"未检测到CloudflareSpeedTest本地版本重新安装")
install_flag = True
@@ -503,6 +496,29 @@ class CloudflareSpeedTest(_PluginBase):
"description": "Cloudflare IP优选",
}]
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self.get_state():
return [
{
"id": "CloudflareSpeedTest",
"name": "Cloudflare IP优选服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.__cloudflareSpeedTest,
"kwargs": {}
}
]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构

View File

@@ -59,7 +59,7 @@ class DirMonitor(_PluginBase):
# 插件图标
plugin_icon = "directory.png"
# 插件版本
plugin_version = "1.6"
plugin_version = "1.7"
# 插件作者
plugin_author = "jxxghp"
# 作者主页
@@ -216,17 +216,6 @@ class DirMonitor(_PluginBase):
# 保存配置
self.__update_config()
# 全量同步定时
if self._enabled and self._cron:
try:
self._scheduler.add_job(func=self.sync_all,
trigger=CronTrigger.from_crontab(self._cron),
name="目录监控全量同步")
except Exception as err:
logger.error(f"定时任务配置错误:{str(err)}")
# 推送实时消息
self.systemmessage.put(f"执行周期配置错误:{str(err)}")
# 启动定时服务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
@@ -643,6 +632,27 @@ class DirMonitor(_PluginBase):
"description": "目录监控同步",
}]
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._cron:
return [{
"id": "DirMonitor",
"name": "目录监控全量同步服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.sync_all,
"kwargs": {}
}]
return []
def sync(self) -> schemas.Response:
"""
API调用目录同步

View File

@@ -27,7 +27,7 @@ class DoubanRank(_PluginBase):
# 插件图标
plugin_icon = "movie.jpg"
# 插件版本
plugin_version = "1.4"
plugin_version = "1.5"
# 插件作者
plugin_author = "jxxghp"
# 作者主页
@@ -90,28 +90,19 @@ class DoubanRank(_PluginBase):
# 启动服务
if self._enabled or self._onlyonce:
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if self._cron:
logger.info(f"豆瓣榜单订阅服务启动,周期:{self._cron}")
try:
self._scheduler.add_job(func=self.__refresh_rss,
trigger=CronTrigger.from_crontab(self._cron),
name="豆瓣榜单订阅")
except Exception as e:
logger.error(f"豆瓣榜单订阅服务启动失败,错误信息:{str(e)}")
self.systemmessage.put(f"豆瓣榜单订阅服务启动失败,错误信息:{str(e)}")
else:
self._scheduler.add_job(func=self.__refresh_rss, trigger=CronTrigger.from_crontab("0 8 * * *"),
name="豆瓣榜单订阅")
logger.info("豆瓣榜单订阅服务启动,周期:每天 08:00")
if self._onlyonce:
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info("豆瓣榜单订阅服务启动,立即运行一次")
self._scheduler.add_job(func=self.__refresh_rss, trigger='date',
run_date=datetime.datetime.now(
tz=pytz.timezone(settings.TZ)) + datetime.timedelta(seconds=3)
)
if self._scheduler.get_jobs():
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
if self._onlyonce or self._clear:
# 关闭一次性开关
self._onlyonce = False
@@ -122,11 +113,6 @@ class DoubanRank(_PluginBase):
# 保存配置
self.__update_config()
if self._scheduler.get_jobs():
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
def get_state(self) -> bool:
return self._enabled
@@ -137,6 +123,39 @@ class DoubanRank(_PluginBase):
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._cron:
return [
{
"id": "DoubanRank",
"name": "豆瓣榜单订阅服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.__refresh_rss,
"kwargs": {}
}
]
elif self._enabled:
return [
{
"id": "DoubanRank",
"name": "豆瓣榜单订阅服务",
"trigger": CronTrigger.from_crontab("0 8 * * *"),
"func": self.__refresh_rss,
"kwargs": {}
}
]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
return [
{

View File

@@ -31,7 +31,7 @@ class DoubanSync(_PluginBase):
# 插件图标
plugin_icon = "douban.png"
# 插件版本
plugin_version = "1.2"
plugin_version = "1.3"
# 插件作者
plugin_author = "jxxghp"
# 作者主页
@@ -82,27 +82,19 @@ class DoubanSync(_PluginBase):
self._clear = config.get("clear")
if self._enabled or self._onlyonce:
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if self._cron:
try:
self._scheduler.add_job(func=self.sync,
trigger=CronTrigger.from_crontab(self._cron),
name="豆瓣想看")
except Exception as err:
logger.error(f"定时任务配置错误:{str(err)}")
# 推送实时消息
self.systemmessage.put(f"执行周期配置错误:{str(err)}")
else:
self._scheduler.add_job(self.sync, "interval", minutes=30, name="豆瓣想看")
if self._onlyonce:
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info(f"豆瓣想看服务启动,立即运行一次")
self._scheduler.add_job(func=self.sync, trigger='date',
run_date=datetime.datetime.now(
tz=pytz.timezone(settings.TZ)) + datetime.timedelta(seconds=3)
)
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
if self._onlyonce or self._clear:
# 关闭一次性开关
self._onlyonce = False
@@ -113,11 +105,6 @@ class DoubanSync(_PluginBase):
# 保存配置
self.__update_config()
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
def get_state(self) -> bool:
return self._enabled
@@ -149,6 +136,39 @@ class DoubanSync(_PluginBase):
"""
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._cron:
return [
{
"id": "DoubanSync",
"name": "豆瓣想看同步服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.sync,
"kwargs": {}
}
]
elif self._enabled:
return [
{
"id": "DoubanSync",
"name": "豆瓣想看同步服务",
"trigger": "interval",
"func": self.sync,
"kwargs": {"minutes": 30}
}
]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构

View File

@@ -1,12 +1,11 @@
from apscheduler.schedulers.background import BackgroundScheduler
from typing import Any, List, Dict, Tuple, Optional, Union
from app.chain.download import DownloadChain
from app.chain.media import MediaChain
from app.core.config import settings
from app.core.metainfo import MetaInfo
from app.db.downloadhistory_oper import DownloadHistoryOper
from app.plugins import _PluginBase
from typing import Any, List, Dict, Tuple, Optional, Union
from app.log import logger
from app.plugins import _PluginBase
from app.schemas import NotificationType, TransferTorrent, DownloadingTorrent
from app.schemas.types import TorrentStatus, MessageChannel
from app.utils.string import StringUtils
@@ -20,7 +19,7 @@ class DownloadingMsg(_PluginBase):
# 插件图标
plugin_icon = "downloadmsg.png"
# 插件版本
plugin_version = "1.0"
plugin_version = "1.1"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -40,10 +39,8 @@ class DownloadingMsg(_PluginBase):
_adminuser = None
_downloadhis = None
# 定时器
_scheduler: Optional[BackgroundScheduler] = None
def init_plugin(self, config: dict = None):
self._downloadhis = DownloadHistoryOper()
# 停止现有任务
self.stop_service()
@@ -53,26 +50,6 @@ class DownloadingMsg(_PluginBase):
self._type = config.get("type") or 'admin'
self._adminuser = config.get("adminuser")
# 加载模块
if self._enabled:
self._downloadhis = DownloadHistoryOper()
# 定时服务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if self._seconds:
try:
self._scheduler.add_job(func=self.__downloading,
trigger='interval',
seconds=int(self._seconds),
name="下载进度推送")
except Exception as err:
logger.error(f"定时任务配置错误:{str(err)}")
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
def __downloading(self):
"""
定时推送正在下载进度
@@ -154,10 +131,10 @@ class DownloadingMsg(_PluginBase):
channel_value = downloadhis.channel
else:
try:
context = MediaChain().recognize_by_title(title=torrent.title)
if not context or not context.media_info:
meta = MetaInfo(torrent.title)
media_info = MediaChain().recognize_media(meta)
if not media_info:
continue
media_info = context.media_info
year = media_info.year
name = media_info.title
if media_info.number_of_seasons:
@@ -207,6 +184,31 @@ class DownloadingMsg(_PluginBase):
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._seconds:
return [
{
"id": "downloading",
"name": "下载进度推送服务",
"trigger": "interval",
"func": self.__downloading,
"kwargs": {
"seconds": int(self._seconds)
}
}
]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构
@@ -309,14 +311,4 @@ class DownloadingMsg(_PluginBase):
pass
def stop_service(self):
"""
退出插件
"""
try:
if self._scheduler:
self._scheduler.remove_all_jobs()
if self._scheduler.running:
self._scheduler.shutdown()
self._scheduler = None
except Exception as e:
logger.error("退出插件失败:%s" % str(e))
pass

View File

@@ -1,7 +1,7 @@
import datetime
import pytz
import threading
from typing import List, Tuple, Dict, Any
from typing import List, Tuple, Dict, Any, Optional
from app.core.context import Context
from app.core.event import eventmanager, Event
@@ -19,6 +19,7 @@ from apscheduler.triggers.cron import CronTrigger
from app.helper.sites import SitesHelper
from app.utils.string import StringUtils
class DownloadSiteTag(_PluginBase):
# 插件名称
plugin_name = "下载任务分类与标签"
@@ -27,7 +28,7 @@ class DownloadSiteTag(_PluginBase):
# 插件图标
plugin_icon = "Youtube-dl_B.png"
# 插件版本
plugin_version = "1.7"
plugin_version = "1.8"
# 插件作者
plugin_author = "叮叮当"
# 作者主页
@@ -40,7 +41,7 @@ class DownloadSiteTag(_PluginBase):
auth_level = 1
# 日志前缀
LOG_TAG = "[DownloadSiteTag] "
# 退出事件
_event = threading.Event()
# 私有属性
@@ -86,57 +87,32 @@ class DownloadSiteTag(_PluginBase):
if not ("interval_cron" in config):
# 新版本v1.6更新插件配置默认配置
config["interval"] = self._interval
config["interval_cron"] = self._interval_cron
config["interval_cron"] = self._interval_cron
config["interval_time"] = self._interval_time
config["interval_unit"] = self._interval_unit
self.update_config(config)
logger.warn(f"{self.LOG_TAG}新版本v{self.plugin_version} 配置修正 ...")
# 停止现有任务
self.stop_service()
if self._enabled or self._onlyonce:
if self._onlyonce:
# 创建定时任务控制器
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if self._onlyonce:
# 执行一次, 关闭onlyonce
self._onlyonce = False
config.update({"onlyonce": self._onlyonce})
self.update_config(config)
# 添加 补全下载历史的标签与分类 任务
self._scheduler.add_job(func= self._complemented_history, trigger='date',
run_date=datetime.datetime.now(
tz=pytz.timezone(settings.TZ)) + datetime.timedelta(seconds=3)
)
if self._enabled:
if self._interval == "计划任务" or self._interval == "固定间隔":
args = {}
if self._interval == "固定间隔":
args["trigger"] = "interval"
if self._interval_unit == "小时":
args["hours"] = self._interval_time
else:
args["minutes"] = self._interval_time
if args["minutes"] < 5:
args["minutes"] = 5
logger.info(f"{self.LOG_TAG}启动定时服务: 最小不少于5分钟, 防止执行间隔太短任务冲突")
else:
args["trigger"] = CronTrigger.from_crontab(self._interval_cron)
try:
self._scheduler.add_job(func=lambda: self._complemented_history(interval=True),
**args,
name="补全下载历史的标签与分类")
logger.info(
f"{self.LOG_TAG}添加定时服务: 补全下载历史的标签与分类" + (f"(每){args.get('hours') or args.get('minutes')}{self._interval_unit}执行一次" if args["trigger"] == "interval" else f",计划任务: {self._interval_cron}"))
except Exception as e:
logger.error(
f"{self.LOG_TAG}添加定时服务发生了错误: {str(e)}")
self._scheduler.add_job(func=self._complemented_history, trigger='date',
run_date=datetime.datetime.now(
tz=pytz.timezone(settings.TZ)) + datetime.timedelta(seconds=3)
)
if self._scheduler and self._scheduler.get_jobs():
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
if self._scheduler and self._scheduler.get_jobs():
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
def get_state(self) -> bool:
return self._enabled
@@ -145,21 +121,68 @@ class DownloadSiteTag(_PluginBase):
def get_command() -> List[Dict[str, Any]]:
pass
def get_api(self) -> List[Dict[str, Any]]:
pass
def str_to_number(self, s: str, i: int) -> int:
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled:
if self._interval == "计划任务" or self._interval == "固定间隔":
if self._interval == "固定间隔":
if self._interval_unit == "小时":
return [{
"id": "DownloadSiteTag",
"name": "补全下载历史的标签与分类",
"trigger": "interval",
"func": self._complemented_history,
"kwargs": {
"hours": self._interval_time
}
}]
else:
if self._interval_time < 5:
self._interval_time = 5
logger.info(f"{self.LOG_TAG}启动定时服务: 最小不少于5分钟, 防止执行间隔太短任务冲突")
return [{
"id": "DownloadSiteTag",
"name": "补全下载历史的标签与分类",
"trigger": "interval",
"func": self._complemented_history,
"kwargs": {
"minutes": self._interval_time
}
}]
else:
return [{
"id": "DownloadSiteTag",
"name": "补全下载历史的标签与分类",
"trigger": CronTrigger.from_crontab(self._interval_cron),
"func": self._complemented_history,
"kwargs": {}
}]
return []
@staticmethod
def str_to_number(s: str, i: int) -> int:
try:
return int(s)
except:
except ValueError:
return i
def _complemented_history(self, interval: bool = False):
def _complemented_history(self):
"""
补全下载历史的标签与分类
"""
logger.info(f"{self.LOG_TAG}开始执行{'(定时任务)' if interval else ''}: 补全下载历史的标签与分类 ...")
logger.info(f"{self.LOG_TAG}开始执行 ...")
# 记录处理的种子, 供辅种(无下载历史)使用
dispose_history = {}
# 所有站点索引
@@ -185,7 +208,8 @@ class DownloadSiteTag(_PluginBase):
for torrent in torrents:
try:
if self._event.is_set():
logger.info(f"{self.LOG_TAG}停止服务{'(定时任务)' if interval else ''}: 补全下载历史的标签与分类")
logger.info(
f"{self.LOG_TAG}停止服务")
return
# 获取已处理种子的key (size, name)
_key = self._torrent_key(torrent=torrent, dl_type=DOWNLOADER)
@@ -205,11 +229,7 @@ class DownloadSiteTag(_PluginBase):
# 因为辅种站点必定不同, 所以需要更新站点名字 history.torrent_site
history.torrent_site = None
else:
history = DownloadHistory(
torrent_site=None,
title=None,
type=None,
tmdbid=None)
history = DownloadHistory()
else:
# 加入历史记录
if _key:
@@ -243,13 +263,14 @@ class DownloadSiteTag(_PluginBase):
# 如果是电视剧 需要区分是否动漫
genre_ids = None
# 因允许tmdbid为空时运行到此, 因此需要判断tmdbid不为空
if history.tmdbid and (history.type == MediaType.TV or history.type == MediaType.TV.value):
history_type = MediaType(history.type) if history.type else None
if history.tmdbid and history_type == MediaType.TV:
# tmdb_id获取tmdb信息
tmdb_info = self.tmdb_helper.get_info(mtype=history.type, tmdbid=history.tmdbid)
tmdb_info = self.tmdb_helper.get_info(mtype=history_type, tmdbid=history.tmdbid)
if tmdb_info:
genre_ids = tmdb_info.get("genre_ids")
_cat = self._genre_ids_get_cat(history.type, genre_ids)
# 去除种子已经存在的标签
if _tags and torrent_tags:
_tags = list(set(_tags) - set(torrent_tags))
@@ -260,15 +281,15 @@ class DownloadSiteTag(_PluginBase):
if not _cat and not _tags:
continue
# 执行通用方法, 设置种子标签与分类
self._set_torrent_info(DOWNLOADER=DOWNLOADER, _hash=_hash, _torrent=torrent, _tags=_tags, _cat=_cat, _original_tags=torrent_tags)
self._set_torrent_info(DOWNLOADER=DOWNLOADER, _hash=_hash, _torrent=torrent, _tags=_tags, _cat=_cat,
_original_tags=torrent_tags)
except Exception as e:
logger.error(
f"{self.LOG_TAG}分析种子信息时发生了错误: {str(e)}")
logger.info(f"{self.LOG_TAG}执行完成")
logger.info(f"{self.LOG_TAG}执行完成{'(定时任务)' if interval else ''}: 补全下载历史的标签与分类 ...")
def _genre_ids_get_cat(self, mtype, genre_ids = None):
def _genre_ids_get_cat(self, mtype, genre_ids=None):
"""
根据genre_ids判断是否<动漫>分类
"""
@@ -298,12 +319,11 @@ class DownloadSiteTag(_PluginBase):
else:
return None
def _torrent_key(self, torrent: Any, dl_type: str):
@staticmethod
def _torrent_key(torrent: Any, dl_type: str) -> Optional[Tuple[int, str]]:
"""
按种子大小和时间返回key
"""
size = None
name = None
if dl_type == "qbittorrent":
size = torrent.get('size')
name = torrent.get('name')
@@ -313,9 +333,10 @@ class DownloadSiteTag(_PluginBase):
if not size or not name:
return None
else:
return (size, name)
return size, name
def _torrents_sort(self, torrents: Any, dl_type: str):
@staticmethod
def _torrents_sort(torrents: Any, dl_type: str):
"""
按种子添加时间排序
"""
@@ -325,7 +346,8 @@ class DownloadSiteTag(_PluginBase):
torrents = sorted(torrents, key=lambda x: x.added_date, reverse=False)
return torrents
def _get_hash(self, torrent: Any, dl_type: str):
@staticmethod
def _get_hash(torrent: Any, dl_type: str):
"""
获取种子hash
"""
@@ -335,7 +357,8 @@ class DownloadSiteTag(_PluginBase):
print(str(e))
return ""
def _get_trackers(self, torrent: Any, dl_type: str):
@staticmethod
def _get_trackers(torrent: Any, dl_type: str):
"""
获取种子trackers
"""
@@ -351,7 +374,8 @@ class DownloadSiteTag(_PluginBase):
num_downloaded 整数 跟踪器报告的当前 torrent 的已完成下载次数
msg 字符串 跟踪器消息(无法知道此消息是什么 - 由跟踪器管理员决定)
"""
return [tracker.get("url") for tracker in (torrent.trackers or []) if tracker.get("tier", -1) >= 0 and tracker.get("url")]
return [tracker.get("url") for tracker in (torrent.trackers or []) if
tracker.get("tier", -1) >= 0 and tracker.get("url")]
else:
"""
class Tracker(Container):
@@ -371,12 +395,14 @@ class DownloadSiteTag(_PluginBase):
def tier(self) -> int:
return self.fields["tier"]
"""
return [tracker.announce for tracker in (torrent.trackers or []) if tracker.tier >= 0 and tracker.announce]
return [tracker.announce for tracker in (torrent.trackers or []) if
tracker.tier >= 0 and tracker.announce]
except Exception as e:
print(str(e))
return []
def _get_label(self, torrent: Any, dl_type: str):
@staticmethod
def _get_label(torrent: Any, dl_type: str):
"""
获取种子标签
"""
@@ -387,7 +413,8 @@ class DownloadSiteTag(_PluginBase):
print(str(e))
return []
def _get_category(self, torrent: Any, dl_type: str):
@staticmethod
def _get_category(torrent: Any, dl_type: str):
"""
获取种子分类
"""
@@ -397,20 +424,23 @@ class DownloadSiteTag(_PluginBase):
print(str(e))
return None
def _set_torrent_info(self, DOWNLOADER: str, _hash: str, _torrent: Any = None, _tags: list = [], _cat: str = None, _original_tags: list = None):
def _set_torrent_info(self, DOWNLOADER: str, _hash: str, _torrent: Any = None, _tags=None, _cat: str = None,
_original_tags: list = None):
"""
设置种子标签与分类
"""
# 当前下载器
if _tags is None:
_tags = []
downloader_obj = self._get_downloader(DOWNLOADER)
if not _torrent:
_torrent, error = downloader_obj.get_torrents(ids=_hash)
if not _torrent or error:
logger.error(
f"{self.LOG_TAG}设置种子标签与分类时发生了错误: 通过 {_hash} 查询不到任何种子!")
f"{self.LOG_TAG}设置种子标签与分类时发生了错误: 通过 {_hash} 查询不到任何种子!")
return
logger.info(
f"{self.LOG_TAG}设置种子标签与分类: {_hash} 查询到 {len(_torrent)} 个种子")
f"{self.LOG_TAG}设置种子标签与分类: {_hash} 查询到 {len(_torrent)} 个种子")
_torrent = _torrent[0]
# 判断是否可执行
if DOWNLOADER and downloader_obj and _hash and _torrent:
@@ -424,20 +454,23 @@ class DownloadSiteTag(_PluginBase):
# 尝试设置种子分类, 如果失败, 则创建再设置一遍
try:
_torrent.setCategory(category=_cat)
except:
except Exception as e:
logger.warn(f"下载器 {DOWNLOADER} 种子id: {_hash} 设置分类 {_cat} 失败:{str(e)}, "
f"尝试创建分类再设置 ...")
downloader_obj.qbc.torrents_createCategory(name=_cat)
_torrent.setCategory(category=_cat)
else:
# 设置标签
if _tags:
# _original_tags = None表示未指定, 因此需要获取原始标签
if _original_tags == None:
if _original_tags is None:
_original_tags = self._get_label(torrent=_torrent, dl_type=DOWNLOADER)
# 如果原始标签不是空的, 那么合并原始标签
if _original_tags:
_tags = list(set(_original_tags).union(set(_tags)))
downloader_obj.set_torrent_tag(ids=_hash, tags=_tags)
logger.warn(f"{self.LOG_TAG}下载器: {DOWNLOADER} 种子id: {_hash} {(' 标签: ' + ','.join(_tags)) if _tags else ''} {(' 分类: ' + _cat) if _cat else ''}")
logger.warn(
f"{self.LOG_TAG}下载器: {DOWNLOADER} 种子id: {_hash} {(' 标签: ' + ','.join(_tags)) if _tags else ''} {(' 分类: ' + _cat) if _cat else ''}")
@eventmanager.register(EventType.DownloadAdded)
def DownloadAdded(self, event: Event):
@@ -449,7 +482,7 @@ class DownloadSiteTag(_PluginBase):
if not event.event_data:
return
try:
context: Context = event.event_data.get("context")
_hash = event.event_data.get("hash")
@@ -473,7 +506,6 @@ class DownloadSiteTag(_PluginBase):
logger.error(
f"{self.LOG_TAG}分析下载事件时发生了错误: {str(e)}")
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构

View File

@@ -5,8 +5,10 @@ import time
from pathlib import Path
from typing import Any, List, Dict, Tuple, Optional, Union
from pydantic import BaseModel
from requests import RequestException
from app import schemas
from app.chain.mediaserver import MediaServerChain
from app.core.config import settings
from app.core.event import eventmanager, Event
@@ -15,16 +17,12 @@ from app.log import logger
from app.modules.emby import Emby
from app.modules.jellyfin import Jellyfin
from app.modules.plex import Plex
from app.modules.themoviedb.tmdbv3api import TV
from app.plugins import _PluginBase
from app import schemas
from app.schemas.types import EventType, MediaType
from app.schemas.types import EventType
from app.utils.common import retry
from app.utils.http import RequestUtils
from app.modules.themoviedb.tmdbv3api import TV
from pydantic import BaseModel
class ExistMediaInfo(BaseModel):
# 类型 电影、电视剧
@@ -49,7 +47,7 @@ class EpisodeGroupMeta(_PluginBase):
# 主题色
plugin_color = "#098663"
# 插件版本
plugin_version = "1.0"
plugin_version = "1.1"
# 插件作者
plugin_author = "叮叮当"
# 作者主页
@@ -67,6 +65,10 @@ class EpisodeGroupMeta(_PluginBase):
# 私有属性
mschain = None
tv = None
emby = None
plex = None
jellyfin = None
_enabled = False
_ignorelock = False
_delay = 0
@@ -75,6 +77,9 @@ class EpisodeGroupMeta(_PluginBase):
def init_plugin(self, config: dict = None):
self.mschain = MediaServerChain()
self.tv = TV()
self.emby = Emby()
self.plex = Plex()
self.jellyfin = Jellyfin()
if config:
self._enabled = config.get("enabled")
self._ignorelock = config.get("ignorelock")
@@ -259,7 +264,8 @@ class EpisodeGroupMeta(_PluginBase):
if not mediainfo.tmdb_id:
self.log_warn(f"{mediainfo.title} 没有tmdbID, 无需处理")
return
if len(self._allowlist) != 0 and not mediainfo.title in self._allowlist:
if len(self._allowlist) != 0 \
and mediainfo.title not in self._allowlist:
self.log_warn(f"{mediainfo.title} 不在白名单, 无需处理")
return
# 获取剧集组信息
@@ -335,8 +341,8 @@ class EpisodeGroupMeta(_PluginBase):
# 是否无视项目锁定
if not self._ignorelock:
if iteminfo.get("LockData") or (
"Name" in iteminfo.get("LockedFields", []) and "Overview" in iteminfo.get(
"LockedFields", [])):
"Name" in iteminfo.get("LockedFields", [])
and "Overview" in iteminfo.get("LockedFields", [])):
self.log_warn(f"已锁定媒体项 - itemid: {_id}, 第 {order} 季, 第 {ep_num}")
continue
# 替换项目数据
@@ -371,7 +377,8 @@ class EpisodeGroupMeta(_PluginBase):
self.log_info(f"{mediainfo.title_year} 已经运行完毕了..")
def __append_to_list(self, list, item):
@staticmethod
def __append_to_list(list, item):
if item not in list:
list.append(item)
@@ -387,15 +394,15 @@ class EpisodeGroupMeta(_PluginBase):
# 获取系列id
item_id = None
try:
res = Emby().get_data(("[HOST]emby/Items?"
"IncludeItemTypes=Series"
"&Fields=ProductionYear"
"&StartIndex=0"
"&Recursive=true"
"&SearchTerm=%s"
"&Limit=10"
"&IncludeSearchTypes=false"
"&api_key=[APIKEY]") % (mediainfo.title))
res = self.emby.get_data(("[HOST]emby/Items?"
"IncludeItemTypes=Series"
"&Fields=ProductionYear"
"&StartIndex=0"
"&Recursive=true"
"&SearchTerm=%s"
"&Limit=10"
"&IncludeSearchTypes=false"
"&api_key=[APIKEY]") % mediainfo.title)
res_items = res.json().get("Items")
if res_items:
for res_item in res_items:
@@ -407,15 +414,15 @@ class EpisodeGroupMeta(_PluginBase):
if not item_id:
return None
# 验证tmdbid是否相同
item_info = Emby().get_iteminfo(item_id)
item_info = self.emby.get_iteminfo(item_id)
if item_info:
if mediainfo.tmdb_id and item_info.tmdbid:
if str(mediainfo.tmdb_id) != str(item_info.tmdbid):
self.log_error(f"tmdbid不匹配或不存在")
return None
try:
res_json = Emby().get_data(
"[HOST]emby/Shows/%s/Episodes?Season=&IsMissing=false&api_key=[APIKEY]" % (item_id))
res_json = self.emby.get_data(
"[HOST]emby/Shows/%s/Episodes?Season=&IsMissing=false&api_key=[APIKEY]" % item_id)
if res_json:
tv_item = res_json.json()
res_items = tv_item.get("Items")
@@ -452,9 +459,10 @@ class EpisodeGroupMeta(_PluginBase):
# 获取系列id
item_id = None
try:
res = Jellyfin.get_data(("[HOST]Users/[USER]/Items?"
"api_key=[APIKEY]&searchTerm=%s&IncludeItemTypes=Series&Limit=10&Recursive=true") % (
mediainfo.title))
res = self.jellyfin.get_data(url=f"[HOST]Users/[USER]/Items?api_key=[APIKEY]"
f"&searchTerm={mediainfo.title}"
f"&IncludeItemTypes=Series"
f"&Limit=10&Recursive=true")
res_items = res.json().get("Items")
if res_items:
for res_item in res_items:
@@ -466,15 +474,15 @@ class EpisodeGroupMeta(_PluginBase):
if not item_id:
return None
# 验证tmdbid是否相同
item_info = Jellyfin().get_iteminfo(item_id)
item_info = self.jellyfin.get_iteminfo(item_id)
if item_info:
if mediainfo.tmdb_id and item_info.tmdbid:
if str(mediainfo.tmdb_id) != str(item_info.tmdbid):
self.log_error(f"tmdbid不匹配或不存在")
return None
try:
res_json = Jellyfin().get_data(
"[HOST]emby/Shows/%s/Episodes?Season=&IsMissing=false&api_key=[APIKEY]" % (item_id))
res_json = self.jellyfin.get_data(
"[HOST]emby/Shows/%s/Episodes?Season=&IsMissing=false&api_key=[APIKEY]" % item_id)
if res_json:
tv_item = res_json.json()
res_items = tv_item.get("Items")
@@ -509,7 +517,7 @@ class EpisodeGroupMeta(_PluginBase):
def __plex_media_exists():
try:
_plex = Plex().get_plex()
_plex = self.plex.get_plex()
if not _plex:
return None
if existsinfo.itemid:
@@ -609,7 +617,7 @@ class EpisodeGroupMeta(_PluginBase):
try:
url = f'[HOST]emby/Users/[USER]/Items/{itemid}?' \
f'Fields=ChannelMappingInfo&api_key=[APIKEY]'
res = Emby().get_data(url=url)
res = self.emby.get_data(url=url)
if res:
return res.json()
except Exception as err:
@@ -622,7 +630,7 @@ class EpisodeGroupMeta(_PluginBase):
"""
try:
url = f'[HOST]Users/[USER]/Items/{itemid}?Fields=ChannelMappingInfo&api_key=[APIKEY]'
res = Jellyfin().get_data(url=url)
res = self.jellyfin.get_data(url=url)
if res:
result = res.json()
if result:
@@ -638,7 +646,7 @@ class EpisodeGroupMeta(_PluginBase):
"""
iteminfo = {}
try:
plexitem = Plex().get_plex().library.fetchItem(ekey=itemid)
plexitem = self.plex.get_plex().library.fetchItem(ekey=itemid)
if 'movie' in plexitem.METADATA_TYPE:
iteminfo['Type'] = 'Movie'
iteminfo['IsFolder'] = False
@@ -667,11 +675,13 @@ class EpisodeGroupMeta(_PluginBase):
if plexitem.title.locked:
iteminfo['LockedFields'].append('Name')
except Exception as err:
logger.warn(f"获取Plex媒体项详情失败{str(err)}")
pass
try:
if plexitem.summary.locked:
iteminfo['LockedFields'].append('Overview')
except Exception as err:
logger.warn(f"获取Plex媒体项详情失败{str(err)}")
pass
return iteminfo
except Exception as err:
@@ -695,7 +705,7 @@ class EpisodeGroupMeta(_PluginBase):
更新Emby媒体项详情
"""
try:
res = Emby().post_data(
res = self.emby.post_data(
url=f'[HOST]emby/Items/{itemid}?api_key=[APIKEY]&reqformat=json',
data=json.dumps(iteminfo),
headers={
@@ -716,7 +726,7 @@ class EpisodeGroupMeta(_PluginBase):
更新Jellyfin媒体项详情
"""
try:
res = Jellyfin().post_data(
res = self.jellyfin.post_data(
url=f'[HOST]Items/{itemid}?api_key=[APIKEY]',
data=json.dumps(iteminfo),
headers={
@@ -737,7 +747,7 @@ class EpisodeGroupMeta(_PluginBase):
更新Plex媒体项详情
"""
try:
plexitem = Plex().get_plex().library.fetchItem(ekey=itemid)
plexitem = self.plex.get_plex().library.fetchItem(ekey=itemid)
if 'CommunityRating' in iteminfo and iteminfo['CommunityRating']:
edits = {
'audienceRating.value': iteminfo['CommunityRating'],
@@ -788,7 +798,7 @@ class EpisodeGroupMeta(_PluginBase):
"""
try:
url = f'[HOST]emby/Items/{itemid}/Images/Primary?api_key=[APIKEY]'
res = Emby().post_data(
res = self.emby.post_data(
url=url,
data=_base64,
headers={
@@ -812,7 +822,7 @@ class EpisodeGroupMeta(_PluginBase):
try:
url = f'[HOST]Items/{itemid}/RemoteImages/Download?' \
f'Type=Primary&ImageUrl={imageurl}&ProviderName=TheMovieDb&api_key=[APIKEY]'
res = Jellyfin().post_data(url=url)
res = self.jellyfin.post_data(url=url)
if res and res.status_code in [200, 204]:
return True
else:
@@ -828,7 +838,7 @@ class EpisodeGroupMeta(_PluginBase):
# FIXME 改为预下载图片
"""
try:
plexitem = Plex().get_plex().library.fetchItem(ekey=itemid)
plexitem = self.plex.get_plex().library.fetchItem(ekey=itemid)
plexitem.uploadPoster(url=imageurl)
return True
except Exception as err:

View File

@@ -22,7 +22,7 @@ class InvitesSignin(_PluginBase):
# 插件图标
plugin_icon = "invites.png"
# 插件版本
plugin_version = "1.2"
plugin_version = "1.3"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -56,33 +56,22 @@ class InvitesSignin(_PluginBase):
self._notify = config.get("notify")
self._onlyonce = config.get("onlyonce")
# 加载模块
if self._enabled:
if self._onlyonce:
# 定时服务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if self._cron:
try:
self._scheduler.add_job(func=self.__signin,
trigger=CronTrigger.from_crontab(self._cron),
name="药丸签到")
except Exception as err:
logger.error(f"定时任务配置错误:{str(err)}")
if self._onlyonce:
logger.info(f"药丸签到服务启动,立即运行一次")
self._scheduler.add_job(func=self.__signin, trigger='date',
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3),
name="药丸签到")
# 关闭一次性开关
self._onlyonce = False
self.update_config({
"onlyonce": False,
"cron": self._cron,
"enabled": self._enabled,
"cookie": self._cookie,
"notify": self._notify,
})
logger.info(f"药丸签到服务启动,立即运行一次")
self._scheduler.add_job(func=self.__signin, trigger='date',
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3),
name="药丸签到")
# 关闭一次性开关
self._onlyonce = False
self.update_config({
"onlyonce": False,
"cron": self._cron,
"enabled": self._enabled,
"cookie": self._cookie,
"notify": self._notify,
})
# 启动任务
if self._scheduler.get_jobs():
@@ -183,6 +172,27 @@ class InvitesSignin(_PluginBase):
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._cron:
return [{
"id": "InvitesSignin",
"name": "药丸签到服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.__signin,
"kwargs": {}
}]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构

View File

@@ -34,7 +34,7 @@ class IYUUAutoSeed(_PluginBase):
# 插件图标
plugin_icon = "IYUU.png"
# 插件版本
plugin_version = "1.2"
plugin_version = "1.3"
# 插件作者
plugin_author = "jxxghp"
# 作者主页
@@ -128,22 +128,21 @@ class IYUUAutoSeed(_PluginBase):
self.qb = Qbittorrent()
self.tr = Transmission()
if self._cron:
try:
self._scheduler.add_job(self.auto_seed,
CronTrigger.from_crontab(self._cron))
logger.info(f"辅种服务启动,周期:{self._cron}")
except Exception as err:
logger.error(f"辅种服务启动失败:{str(err)}")
self.systemmessage.put(f"辅种服务启动失败:{str(err)}")
if self._onlyonce:
logger.info(f"辅种服务启动,立即运行一次")
self._scheduler.add_job(self.auto_seed, 'date',
run_date=datetime.now(
tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3)
)
# 关闭一次性开关
self._onlyonce = False
if self._scheduler.get_jobs():
# 追加种子校验服务
self._scheduler.add_job(self.check_recheck, 'interval', minutes=3)
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
if self._clearcache:
# 关闭清除缓存开关
@@ -153,13 +152,6 @@ class IYUUAutoSeed(_PluginBase):
# 保存配置
self.__update_config()
if self._scheduler.get_jobs():
# 追加种子校验服务
self._scheduler.add_job(self.check_recheck, 'interval', minutes=3)
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
def get_state(self) -> bool:
return True if self._enabled and self._cron and self._token and self._downloaders else False
@@ -170,6 +162,27 @@ class IYUUAutoSeed(_PluginBase):
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self.get_state():
return [{
"id": "IYUUAutoSeed",
"name": "IYUU自动辅种服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.auto_seed,
"kwargs": {}
}]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构

View File

@@ -25,7 +25,7 @@ class LibraryScraper(_PluginBase):
# 插件图标
plugin_icon = "scraper.png"
# 插件版本
plugin_version = "1.2"
plugin_version = "1.3"
# 插件作者
plugin_author = "jxxghp"
# 作者主页
@@ -67,23 +67,10 @@ class LibraryScraper(_PluginBase):
# 启动定时任务 & 立即运行一次
if self._enabled or self._onlyonce:
self.transferhis = TransferHistoryOper()
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if self._cron:
logger.info(f"媒体库刮削服务启动,周期:{self._cron}")
try:
self._scheduler.add_job(func=self.__libraryscraper,
trigger=CronTrigger.from_crontab(self._cron),
name="媒体库刮削")
except Exception as e:
logger.error(f"媒体库刮削服务启动失败,原因:{str(e)}")
self.systemmessage.put(f"媒体库刮削服务启动失败,原因:{str(e)}")
else:
logger.info(f"媒体库刮削服务启动周期每7天")
self._scheduler.add_job(func=self.__libraryscraper,
trigger=CronTrigger.from_crontab("0 0 */7 * *"),
name="媒体库刮削")
if self._onlyonce:
logger.info(f"媒体库刮削服务,立即运行一次")
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
self._scheduler.add_job(func=self.__libraryscraper, trigger='date',
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3),
name="媒体库刮削")
@@ -97,10 +84,10 @@ class LibraryScraper(_PluginBase):
"scraper_paths": self._scraper_paths,
"exclude_paths": self._exclude_paths
})
if self._scheduler.get_jobs():
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
if self._scheduler.get_jobs():
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
def get_state(self) -> bool:
return self._enabled
@@ -112,6 +99,35 @@ class LibraryScraper(_PluginBase):
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._cron:
return [{
"id": "LibraryScraper",
"name": "媒体库刮削",
"trigger": "cron",
"func": self.__libraryscraper,
"kwargs": {}
}]
elif self._enabled:
return [{
"id": "LibraryScraper",
"name": "媒体库刮削",
"trigger": CronTrigger.from_crontab("0 0 */7 * *"),
"func": self.__libraryscraper,
"kwargs": {}
}]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
return [
{

View File

@@ -51,7 +51,7 @@ class LinkMonitor(_PluginBase):
# 插件图标
plugin_icon = "Linkace_C.png"
# 插件版本
plugin_version = "1.2"
plugin_version = "1.3"
# 插件作者
plugin_author = "jxxghp"
# 作者主页
@@ -104,8 +104,6 @@ class LinkMonitor(_PluginBase):
self.stop_service()
if self._enabled or self._onlyonce:
# 定时服务管理器
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
# 读取目录配置
monitor_dirs = self._monitor_dirs.split("\n")
@@ -176,6 +174,8 @@ class LinkMonitor(_PluginBase):
# 运行一次定时服务
if self._onlyonce:
# 定时服务管理器
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info("目录监控服务启动,立即运行一次")
self._scheduler.add_job(func=self.sync_all, trigger='date',
run_date=datetime.datetime.now(
@@ -186,21 +186,10 @@ class LinkMonitor(_PluginBase):
# 保存配置
self.__update_config()
# 全量同步定时
if self._enabled and self._cron:
try:
self._scheduler.add_job(func=self.sync_all,
trigger=CronTrigger.from_crontab(self._cron),
name="实时硬链接")
except Exception as err:
logger.error(f"定时任务配置错误:{str(err)}")
# 推送实时消息
self.systemmessage.put(f"执行周期配置错误:{str(err)}")
# 启动定时服务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
# 启动定时服务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
def __update_config(self):
"""
@@ -380,6 +369,26 @@ class LinkMonitor(_PluginBase):
"description": "实时硬链接",
}]
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._cron:
return [{
"id": "LinkMonitor",
"name": "全量硬链接定时服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.sync_all,
"kwargs": {}
}]
def sync(self) -> schemas.Response:
"""
API调用目录同步

View File

@@ -21,7 +21,7 @@ class MediaServerRefresh(_PluginBase):
# 插件图标
plugin_icon = "refresh2.png"
# 插件版本
plugin_version = "1.1"
plugin_version = "1.2"
# 插件作者
plugin_author = "jxxghp"
# 作者主页
@@ -36,8 +36,14 @@ class MediaServerRefresh(_PluginBase):
# 私有属性
_enabled = False
_delay = 0
_emby = None
_jellyfin = None
_plex = None
def init_plugin(self, config: dict = None):
self._emby = Emby()
self._jellyfin = Jellyfin()
self._plex = Plex()
if config:
self._enabled = config.get("enabled")
self._delay = config.get("delay") or 0
@@ -146,16 +152,16 @@ class MediaServerRefresh(_PluginBase):
]
# Emby
if "emby" in settings.MEDIASERVER:
Emby().refresh_library_by_items(items)
self._emby.refresh_library_by_items(items)
# Jeyllyfin
if "jellyfin" in settings.MEDIASERVER:
# FIXME Jellyfin未找到刷新单个项目的API
Jellyfin().refresh_root_library()
self._jellyfin.refresh_root_library()
# Plex
if "plex" in settings.MEDIASERVER:
Plex().refresh_library_by_items(items)
self._plex.refresh_library_by_items(items)
def stop_service(self):
"""

View File

@@ -31,7 +31,7 @@ class MediaSyncDel(_PluginBase):
# 插件图标
plugin_icon = "mediasyncdel.png"
# 插件版本
plugin_version = "1.2"
plugin_version = "1.3"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -96,27 +96,6 @@ class MediaSyncDel(_PluginBase):
"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="媒体库同步删除日志方式")
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="媒体库同步删除日志方式")
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
@staticmethod
def get_command() -> List[Dict[str, Any]]:
"""
@@ -128,6 +107,37 @@ class MediaSyncDel(_PluginBase):
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and str(self._sync_type) == "log":
# 媒体库同步删除日志方式
if self._cron:
return [{
"id": "MediaSyncDel",
"name": "媒体库同步删除服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.sync_del_by_log,
"kwargs": {}
}]
else:
return [{
"id": "MediaSyncDel",
"name": "媒体库同步删除服务",
"trigger": "interval",
"func": self.sync_del_by_log,
"kwargs": {"minutes": 30}
}]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构
@@ -1325,7 +1335,7 @@ class MediaSyncDel(_PluginBase):
获取emby日志列表、解析emby日志
"""
def __parse_log(file_name: str, del_list: list, last_time):
def __parse_log(file_name: str, del_list: list):
"""
解析emby日志
"""
@@ -1419,8 +1429,7 @@ class MediaSyncDel(_PluginBase):
log_files.reverse()
for log_file in log_files:
del_medias = __parse_log(file_name=log_file,
del_list=del_medias,
last_time=last_time)
del_list=del_medias)
return del_medias
@@ -1430,7 +1439,7 @@ class MediaSyncDel(_PluginBase):
获取jellyfin日志列表、解析jellyfin日志
"""
def __parse_log(file_name: str, del_list: list, last_time):
def __parse_log(file_name: str, del_list: list):
"""
解析jellyfin日志
"""
@@ -1524,8 +1533,7 @@ class MediaSyncDel(_PluginBase):
log_files.reverse()
for log_file in log_files:
del_medias = __parse_log(file_name=log_file,
del_list=del_medias,
last_time=last_time)
del_list=del_medias)
return del_medias

View File

@@ -22,7 +22,7 @@ class MoviePilotUpdateNotify(_PluginBase):
# 插件图标
plugin_icon = "Moviepilot_A.png"
# 插件版本
plugin_version = "1.0"
plugin_version = "1.1"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -54,24 +54,6 @@ class MoviePilotUpdateNotify(_PluginBase):
self._restart = config.get("restart")
self._notify = config.get("notify")
# 加载模块
if self._enabled:
# 定时服务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if self._cron:
try:
self._scheduler.add_job(func=self.__check_update,
trigger=CronTrigger.from_crontab(self._cron),
name="检查MoviePilot更新")
except Exception as err:
logger.error(f"定时任务配置错误:{str(err)}")
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
def __check_update(self):
"""
检查MoviePilot更新
@@ -136,6 +118,29 @@ class MoviePilotUpdateNotify(_PluginBase):
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._cron:
return [
{
"id": "MoviePilotUpdateNotify",
"name": "MoviePilot更新检查服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.__check_update,
"kwargs": {}
}
]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构

View File

@@ -39,7 +39,7 @@ class PersonMeta(_PluginBase):
# 插件图标
plugin_icon = "actor.png"
# 插件版本
plugin_version = "1.1"
plugin_version = "1.2"
# 插件作者
plugin_author = "jxxghp"
# 作者主页
@@ -80,31 +80,19 @@ class PersonMeta(_PluginBase):
self.stop_service()
# 启动服务
if self._enabled or self._onlyonce:
if self._onlyonce:
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if self._cron or self._onlyonce:
if self._cron:
try:
self._scheduler.add_job(func=self.scrap_library,
trigger=CronTrigger.from_crontab(self._cron),
name="演职人员刮削")
logger.info(f"演职人员刮削服务启动,周期:{self._cron}")
except Exception as e:
logger.error(f"演职人员刮削服务启动失败,错误信息:{str(e)}")
self.systemmessage.put(f"演职人员刮削服务启动失败,错误信息:{str(e)}")
if self._onlyonce:
self._scheduler.add_job(func=self.scrap_library, trigger='date',
run_date=datetime.datetime.now(
tz=pytz.timezone(settings.TZ)) + datetime.timedelta(seconds=3)
)
logger.info(f"演职人员刮削服务启动,立即运行一次")
# 关闭一次性开关
self._onlyonce = False
# 保存配置
self.__update_config()
self._scheduler.add_job(func=self.scrap_library, trigger='date',
run_date=datetime.datetime.now(
tz=pytz.timezone(settings.TZ)) + datetime.timedelta(seconds=3)
)
logger.info(f"演职人员刮削服务启动,立即运行一次")
# 关闭一次性开关
self._onlyonce = False
# 保存配置
self.__update_config()
# 启动服务
if self._scheduler.get_jobs():
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
@@ -131,6 +119,26 @@ class PersonMeta(_PluginBase):
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._cron:
return [{
"id": "PersonMeta",
"name": "演职人员刮削服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.scrap_library,
"kwargs": {}
}]
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构

View File

@@ -25,26 +25,26 @@ class FileMonitorHandler(FileSystemEventHandler):
def on_created(self, event):
logger.info("监测到新增文件:%s" % event.src_path)
if self.sync._exclude_keywords:
for keyword in self.sync._exclude_keywords.split("\n"):
if self.sync.exclude_keywords:
for keyword in self.sync.exclude_keywords.split("\n"):
if keyword and re.findall(keyword, event.src_path):
logger.info(f"{event.src_path} 命中过滤关键字 {keyword},不处理")
print(f"{event.src_path} 命中过滤关键字 {keyword},不处理")
return
new_file = Path(event.src_path)
try:
self.sync._state_set.add((Path(event.src_path), new_file.stat().st_ino))
self.sync.state_set.add((Path(event.src_path), new_file.stat().st_ino))
except Exception as e:
logger.error("文件丢失:%s" % event.src_path)
logger.error(f"文件丢失:%s - {e}" % event.src_path)
def on_deleted(self, event):
if Path(event.src_path) in self.sync._ignored_files:
self.sync._ignored_files.remove(Path(event.src_path))
if Path(event.src_path) in self.sync.ignored_files:
self.sync.ignored_files.remove(Path(event.src_path))
return
logger.info("监测到删除:%s" % event.src_path)
# 命中过滤关键字不处理
if self.sync._exclude_keywords:
for keyword in self.sync._exclude_keywords.split("\n"):
if self.sync.exclude_keywords:
for keyword in self.sync.exclude_keywords.split("\n"):
if keyword and re.findall(keyword, event.src_path):
logger.info(f"{event.src_path} 命中过滤关键字 {keyword},不处理")
print(f"{event.src_path} 命中过滤关键字 {keyword},不处理")
@@ -80,7 +80,7 @@ class RemoveLink(_PluginBase):
# 插件图标
plugin_icon = "Ombi_A.png"
# 插件版本
plugin_version = "1.1"
plugin_version = "1.2"
# 插件作者
plugin_author = "DzAvril"
# 作者主页
@@ -93,27 +93,27 @@ class RemoveLink(_PluginBase):
auth_level = 1
# preivate property
_monitor_dirs = ""
_exclude_keywords = ""
monitor_dirs = ""
exclude_keywords = ""
_enabled = False
_notify = False
_observer = []
_state_set = set()
_ignored_files = set()
state_set = set()
ignored_files = set()
def init_plugin(self, config: dict = None):
logger.info(f"Hello, RemoveLink! config {config}")
if config:
self._enabled = config.get("enabled")
self._notify = config.get("notify")
self._monitor_dirs = config.get("monitor_dirs")
self._exclude_keywords = config.get("exclude_keywords") or ""
self.monitor_dirs = config.get("monitor_dirs")
self.exclude_keywords = config.get("exclude_keywords") or ""
self.__update_config()
# 停止现有任务
self.stop_service()
if self._enabled:
# 读取目录配置
monitor_dirs = self._monitor_dirs.split("\n")
monitor_dirs = self.monitor_dirs.split("\n")
logger.info(f"监控目录:{monitor_dirs}")
if not monitor_dirs:
return
@@ -134,7 +134,7 @@ class RemoveLink(_PluginBase):
err_msg = str(e)
logger.error(f"{mon_path} 启动目录监控失败:{err_msg}")
self.systemmessage.put(f"{mon_path} 启动目录监控失败:{err_msg}")
self._state_set = updateState(monitor_dirs)
self.state_set = updateState(monitor_dirs)
def __update_config(self):
"""
@@ -144,8 +144,8 @@ class RemoveLink(_PluginBase):
{
"enabled": self._enabled,
"notify": self._notify,
"monitor_dirs": self._monitor_dirs,
"exclude_keywords": self._exclude_keywords,
"monitor_dirs": self.monitor_dirs,
"exclude_keywords": self.exclude_keywords,
}
)
@@ -289,15 +289,15 @@ class RemoveLink(_PluginBase):
"""
处理删除事件
"""
current_set = updateState(self._monitor_dirs.split("\n"))
deleted_set = self._state_set - current_set
current_set = updateState(self.monitor_dirs.split("\n"))
deleted_set = self.state_set - current_set
deleted_inode = [x[1] for x in deleted_set]
try:
# 在current_set中查找与deleted_inode有相同inode的文件并删除
for path, inode in current_set:
if inode in deleted_inode:
file = Path(path)
self._ignored_files.add(file)
self.ignored_files.add(file)
file.unlink()
logger.info(f"删除硬链接文件:{path}")
if self._notify:
@@ -316,4 +316,4 @@ class RemoveLink(_PluginBase):
except Exception as e:
logger.error("目录监控发生错误:%s - %s" % (str(e), traceback.format_exc()))
self._state_set = updateState(self._monitor_dirs.split("\n"))
self.state_set = updateState(self.monitor_dirs.split("\n"))

View File

@@ -30,7 +30,7 @@ class RssSubscribe(_PluginBase):
# 插件图标
plugin_icon = "rss.png"
# 插件版本
plugin_version = "1.0"
plugin_version = "1.1"
# 插件作者
plugin_author = "jxxghp"
# 作者主页
@@ -89,43 +89,29 @@ class RssSubscribe(_PluginBase):
self._action = config.get("action")
self._save_path = config.get("save_path")
if self._enabled or self._onlyonce:
if self._onlyonce:
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if self._cron:
try:
self._scheduler.add_job(func=self.check,
trigger=CronTrigger.from_crontab(self._cron),
name="RSS订阅")
except Exception as err:
logger.error(f"定时任务配置错误:{str(err)}")
# 推送实时消息
self.systemmessage.put(f"执行周期配置错误:{str(err)}")
else:
self._scheduler.add_job(self.check, "interval", minutes=30, name="RSS订阅")
if self._onlyonce:
logger.info(f"RSS订阅服务启动立即运行一次")
self._scheduler.add_job(func=self.check, trigger='date',
run_date=datetime.datetime.now(
tz=pytz.timezone(settings.TZ)) + datetime.timedelta(seconds=3)
)
if self._onlyonce or self._clear:
# 关闭一次性开关
self._onlyonce = False
# 记录清理缓存设置
self._clearflag = self._clear
# 关闭清理缓存开关
self._clear = False
# 保存设置
self.__update_config()
logger.info(f"自定义订阅服务启动,立即运行一次")
self._scheduler.add_job(func=self.check, trigger='date',
run_date=datetime.datetime.now(
tz=pytz.timezone(settings.TZ)) + datetime.timedelta(seconds=3)
)
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
if self._onlyonce or self._clear:
# 关闭一次性开关
self._onlyonce = False
# 记录清理缓存设置
self._clearflag = self._clear
# 关闭清理缓存开关
self._clear = False
# 保存设置
self.__update_config()
def get_state(self) -> bool:
return self._enabled
@@ -149,6 +135,35 @@ class RssSubscribe(_PluginBase):
"""
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._cron:
return [{
"id": "RssSubscribe",
"name": "自定义订阅服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.check,
"kwargs": {}
}]
elif self._enabled:
return [{
"id": "RssSubscribe",
"name": "自定义订阅服务",
"trigger": "interval",
"func": self.check,
"kwargs": {"minutes": 30}
}]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构

View File

@@ -17,7 +17,7 @@ class SiteRefresh(_PluginBase):
# 插件图标
plugin_icon = "Chrome_A.png"
# 插件版本
plugin_version = "1.1"
plugin_version = "1.2"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -77,7 +77,7 @@ class SiteRefresh(_PluginBase):
site_name = site.name
logger.info(f"开始尝试登录站点 {site_name}")
siteurl, siteuser, sitepwd = None, None, None
siteurl, siteuser, sitepwd, sitecode = None, None, None, None
# 判断site是否已配置用户名密码
for site_conf in self._siteconf:
if not site_conf:

View File

@@ -43,7 +43,7 @@ class SiteStatistic(_PluginBase):
# 插件图标
plugin_icon = "statistic.png"
# 插件版本
plugin_version = "1.6"
plugin_version = "1.7"
# 插件作者
plugin_author = "lightolly"
# 作者主页
@@ -101,9 +101,6 @@ class SiteStatistic(_PluginBase):
self._site_schema = ModuleHelper.load('app.plugins.sitestatistic.siteuserinfo',
filter_func=lambda _, obj: hasattr(obj, 'schema'))
# 定时服务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
self._site_schema.sort(key=lambda x: x.order)
# 站点上一次更新时间
self._last_update_time = None
@@ -112,6 +109,8 @@ class SiteStatistic(_PluginBase):
# 立即运行一次
if self._onlyonce:
# 定时服务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info(f"站点数据统计服务启动,立即运行一次")
self._scheduler.add_job(self.refresh_all_site_data, 'date',
run_date=datetime.now(
@@ -123,31 +122,10 @@ class SiteStatistic(_PluginBase):
# 保存配置
self.__update_config()
# 周期运行
if self._enabled and self._cron:
try:
self._scheduler.add_job(func=self.refresh_all_site_data,
trigger=CronTrigger.from_crontab(self._cron),
name="站点数据统计")
except Exception as err:
logger.error(f"定时任务配置错误:{str(err)}")
# 推送实时消息
self.systemmessage.put(f"执行周期配置错误:{str(err)}")
else:
triggers = TimerUtils.random_scheduler(num_executions=1,
begin_hour=0,
end_hour=1,
min_interval=1,
max_interval=60)
for trigger in triggers:
self._scheduler.add_job(self.refresh_all_site_data, "cron",
hour=trigger.hour, minute=trigger.minute,
name="站点数据统计")
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
def get_state(self) -> bool:
return self._enabled
@@ -186,6 +164,46 @@ class SiteStatistic(_PluginBase):
"description": "刷新对应域名的站点数据",
}]
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._cron:
return [{
"id": "SiteStatistic",
"name": "站点数据统计服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.refresh_all_site_data,
"kwargs": {}
}]
elif self._enabled:
triggers = TimerUtils.random_scheduler(num_executions=1,
begin_hour=0,
end_hour=1,
min_interval=1,
max_interval=60)
ret_jobs = []
for trigger in triggers:
ret_jobs.append({
"id": f"SiteStatistic|{trigger.hour}:{trigger.minute}",
"name": "站点数据统计服务",
"trigger": "cron",
"func": self.refresh_all_site_data,
"kwargs": {
"hour": trigger.hour,
"minute": trigger.minute
}
})
return ret_jobs
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构

View File

@@ -1,8 +1,6 @@
import ipaddress
from typing import List, Tuple, Dict, Any
from apscheduler.schedulers.background import BackgroundScheduler
from app.core.config import settings
from app.core.event import eventmanager, Event
from app.log import logger
@@ -25,7 +23,7 @@ class SpeedLimiter(_PluginBase):
# 插件图标
plugin_icon = "Librespeed_A.png"
# 插件版本
plugin_version = "1.0"
plugin_version = "1.1"
# 插件作者
plugin_author = "Shurelol"
# 作者主页
@@ -96,20 +94,6 @@ class SpeedLimiter(_PluginBase):
if 'transmission' in self._downloader:
self._tr = Transmission()
# 移出现有任务
self.stop_service()
# 启动限速任务
if self._enabled and self._limit_enabled:
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
self._scheduler.add_job(func=self.check_playing_sessions,
trigger='interval',
seconds=self._interval,
name="播放限速检查")
self._scheduler.print_jobs()
self._scheduler.start()
logger.info("播放限速检查服务启动")
def get_state(self) -> bool:
return self._enabled
@@ -120,6 +104,29 @@ class SpeedLimiter(_PluginBase):
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self._enabled and self._limit_enabled and self._interval:
return [
{
"id": "SpeedLimiter",
"name": "播放限速检查服务",
"trigger": "interval",
"func": self.check_playing_sessions,
"kwargs": {"seconds": self._interval}
}
]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
return [
{
@@ -614,14 +621,4 @@ class SpeedLimiter(_PluginBase):
return False
def stop_service(self):
"""
退出插件
"""
try:
if self._scheduler:
self._scheduler.remove_all_jobs()
if self._scheduler.running:
self._scheduler.shutdown()
self._scheduler = None
except Exception as e:
print(str(e))
pass

View File

@@ -22,7 +22,7 @@ class SyncDownloadFiles(_PluginBase):
# 插件图标
plugin_icon = "Youtube-dl_A.png"
# 插件版本
plugin_version = "1.0"
plugin_version = "1.1"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -88,27 +88,6 @@ class SyncDownloadFiles(_PluginBase):
self.sync()
if self._enabled:
# 定时服务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if self._time:
try:
self._scheduler.add_job(func=self.sync,
trigger="interval",
hours=float(str(self._time).strip()),
name="自动同步下载器文件记录")
logger.info(f"自动同步下载器文件记录服务启动,时间间隔 {self._time} 小时")
except Exception as err:
logger.error(f"定时任务配置错误:{str(err)}")
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
else:
self._enabled = False
self.__update_config()
def sync(self):
"""
同步所选下载器种子记录
@@ -378,7 +357,7 @@ class SyncDownloadFiles(_PluginBase):
return None
def get_state(self) -> bool:
return self._enabled
return True if self._enabled and self._time else False
@staticmethod
def get_command() -> List[Dict[str, Any]]:
@@ -387,6 +366,27 @@ class SyncDownloadFiles(_PluginBase):
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self.get_state():
return [{
"id": "SyncDownloadFiles",
"name": "同步下载器文件记录服务",
"trigger": "interval",
"func": self.sync,
"kwargs": {"seconds": float(str(self._time).strip()) * 3600}
}]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构
@@ -478,7 +478,7 @@ class SyncDownloadFiles(_PluginBase):
'component': 'VTextField',
'props': {
'model': 'time',
'label': '同步时间间隔'
'label': '同步时间间隔(小时)'
}
}
]

View File

@@ -27,7 +27,7 @@ class TorrentRemover(_PluginBase):
# 插件图标
plugin_icon = "delete.jpg"
# 插件版本
plugin_version = "1.1"
plugin_version = "1.2"
# 插件作者
plugin_author = "jxxghp"
# 作者主页
@@ -88,19 +88,10 @@ class TorrentRemover(_PluginBase):
self.stop_service()
if self.get_state() or self._onlyonce:
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
self.qb = Qbittorrent()
self.tr = Transmission()
if self._cron:
try:
self._scheduler.add_job(func=self.delete_torrents,
trigger=CronTrigger.from_crontab(self._cron),
name="自动删种服务")
logger.info(f"自动删种服务启动,周期:{self._cron}")
except Exception as err:
logger.error(f"自动删种服务启动失败:{str(err)}")
self.systemmessage.put(f"自动删种服务启动失败:{str(err)}")
if self._onlyonce:
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info(f"自动删种服务启动,立即运行一次")
self._scheduler.add_job(func=self.delete_torrents, trigger='date',
run_date=datetime.now(
@@ -130,10 +121,10 @@ class TorrentRemover(_PluginBase):
"torrentcategorys": self._torrentcategorys
})
if self._scheduler.get_jobs():
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
if self._scheduler.get_jobs():
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
def get_state(self) -> bool:
return True if self._enabled and self._cron and self._downloaders else False
@@ -145,6 +136,27 @@ class TorrentRemover(_PluginBase):
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self.get_state():
return [{
"id": "TorrentRemover",
"name": "自动删种服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.delete_torrents,
"kwargs": {}
}]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
return [
{

View File

@@ -27,7 +27,7 @@ class TorrentTransfer(_PluginBase):
# 插件图标
plugin_icon = "seed.png"
# 插件版本
plugin_version = "1.1"
plugin_version = "1.2"
# 插件作者
plugin_author = "jxxghp"
# 作者主页
@@ -102,16 +102,14 @@ class TorrentTransfer(_PluginBase):
logger.error(f"源下载器和目的下载器不能相同")
self.systemmessage.put(f"源下载器和目的下载器不能相同")
return
# 定时服务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
if self._cron:
logger.info(f"转移做种服务启动,周期:{self._cron}")
try:
self._scheduler.add_job(self.transfer,
CronTrigger.from_crontab(self._cron))
except Exception as e:
logger.error(f"转移做种服务启动失败:{str(e)}")
self.systemmessage.put(f"转移做种服务启动失败:{str(e)}")
return
if self._autostart:
# 追加种子校验服务
self._scheduler.add_job(self.check_recheck, 'interval', minutes=3)
if self._onlyonce:
logger.info(f"转移做种服务启动,立即运行一次")
self._scheduler.add_job(self.transfer, 'date',
@@ -135,11 +133,9 @@ class TorrentTransfer(_PluginBase):
"nopaths": self._nopaths,
"autostart": self._autostart
})
# 启动服务
if self._scheduler.get_jobs():
if self._autostart:
# 追加种子校验服务
self._scheduler.add_job(self.check_recheck, 'interval', minutes=3)
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
@@ -157,6 +153,29 @@ class TorrentTransfer(_PluginBase):
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_service(self) -> List[Dict[str, Any]]:
"""
注册插件公共服务
[{
"id": "服务ID",
"name": "服务名称",
"trigger": "触发器cron/interval/date/CronTrigger.from_crontab()",
"func": self.xxx,
"kwargs": {} # 定时器参数
}]
"""
if self.get_state():
return [
{
"id": "TorrentTransfer",
"name": "转移做种服务",
"trigger": CronTrigger.from_crontab(self._cron),
"func": self.transfer,
"kwargs": {}
}
]
return []
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构

View File

@@ -1,11 +1,10 @@
from urllib.parse import urlencode
from typing import Any, List, Dict, Tuple
from app.plugins import _PluginBase
from app.core.event import eventmanager, Event
from app.log import logger
from app.plugins import _PluginBase
from app.schemas.types import EventType, NotificationType
from app.utils.http import RequestUtils
from typing import Any, List, Dict, Tuple
from app.log import logger
class WorkWechatMsg(_PluginBase):
@@ -186,18 +185,18 @@ class WorkWechatMsg(_PluginBase):
payload = {
"msgtype": "news",
"news": {
"articles" : [
"articles": [
{
"title" : title,
"description" : text,
"url" : "moviepilot",
"picurl" : image
"title": title,
"description": text,
"url": "moviepilot",
"picurl": image
}
]
}
}
res = RequestUtils().post_res(url = self._webhookurl, json = payload)
res = RequestUtils().post_res(url=self._webhookurl, json=payload)
if res and res.status_code == 200:
ret_json = res.json()
errno = ret_json.get('errcode')