mirror of
https://github.com/d0zingcat/MoviePilot-Plugins.git
synced 2026-05-14 07:26:51 +00:00
Merge remote-tracking branch 'origin/main'
# Conflicts: # package.json
This commit is contained in:
30
package.json
30
package.json
@@ -27,11 +27,12 @@
|
||||
"name": "站点数据统计",
|
||||
"description": "自动统计和展示站点数据。",
|
||||
"labels": "站点",
|
||||
"version": "3.0",
|
||||
"version": "3.1",
|
||||
"icon": "statistic.png",
|
||||
"author": "lightolly",
|
||||
"level": 2,
|
||||
"history": {
|
||||
"v3.1": "修复观众无法统计做总数和做种体积的bug",
|
||||
"v3.0": "适配馒头数据统计,需要升级至v1.8.5+版本,且在站点信息中维护好API Key",
|
||||
"v2.9": "增强API安全性",
|
||||
"v2.8": "修复馒头未读消息统计",
|
||||
@@ -259,10 +260,13 @@
|
||||
"name": "整理VCB动漫压制组作品",
|
||||
"description": "提高部分VCB-Studio作品的识别准确率,将VCB-Studio的作品统一转移到指定目录同时进行刮削整理",
|
||||
"labels": "文件整理,识别",
|
||||
"version": "1.7",
|
||||
"version": "1.7.1",
|
||||
"icon": "vcbmonitor.png",
|
||||
"author": "pixel@qingwa",
|
||||
"level": 2
|
||||
"level": 2,
|
||||
"history": {
|
||||
"v1.7.1": "修复偶尔安装失败问题"
|
||||
}
|
||||
},
|
||||
"TorrentTransfer": {
|
||||
"name": "自动转移做种",
|
||||
@@ -298,11 +302,12 @@
|
||||
"name": "站点刷流",
|
||||
"description": "自动托管刷流,将会提高对应站点的访问频率。",
|
||||
"labels": "刷流",
|
||||
"version": "2.9",
|
||||
"version": "3.0",
|
||||
"icon": "brush.jpg",
|
||||
"author": "jxxghp,InfinityPacer",
|
||||
"level": 2,
|
||||
"history": {
|
||||
"v3.0": "优化不同站点刷流到相同种子的逻辑,修复数据页滚动闪烁,部分日志优化",
|
||||
"v2.9": "优化动态删除消息推送,优化配置页UI显示及部分配置项,支持配置种子分类以及开启自动分类管理,取消单独适配站点时区逻辑,可通过配置项「pubtime」自行适配",
|
||||
"v2.8": "优化UI显示以及提升性能",
|
||||
"v2.7": "动态删除种子规则调整(请注意查阅插件文档),站点独立配置样式优化、日志优化,修复部分配置项无法配置小数的问题,修复部分场景可能导致重复下载的问题",
|
||||
@@ -547,11 +552,12 @@
|
||||
"name": "QB远程操作",
|
||||
"description": "通过定时任务或交互命令远程操作QB暂停/开始/限速等。",
|
||||
"labels": "下载管理,Qbittorrent",
|
||||
"version": "1.4",
|
||||
"version": "1.5",
|
||||
"icon": "Qbittorrent_A.png",
|
||||
"author": "DzAvril",
|
||||
"level": 1,
|
||||
"history": {
|
||||
"v1.5": "可选特定路径下的做种不会被暂停",
|
||||
"v1.4": "可选某些站点不再做种(暂停做种后不会被恢复)"
|
||||
}
|
||||
},
|
||||
@@ -586,10 +592,13 @@
|
||||
"name": "契约检查",
|
||||
"description": "定时检查保种契约达成情况。",
|
||||
"labels": "做种",
|
||||
"version": "1.1",
|
||||
"version": "1.2",
|
||||
"icon": "contract.png",
|
||||
"author": "DzAvril",
|
||||
"level": 1
|
||||
"level": 1,
|
||||
"history": {
|
||||
"v1.2": "修复契约检查无数据返回的问题"
|
||||
}
|
||||
},
|
||||
"DownloaderHelper": {
|
||||
"name": "下载器助手",
|
||||
@@ -646,10 +655,13 @@
|
||||
"name": "聚合站点开关",
|
||||
"description": "统一管理所有与站点相关的开关。",
|
||||
"labels": "系统设置",
|
||||
"version": "1.0",
|
||||
"version": "1.1",
|
||||
"icon": "world.png",
|
||||
"author": "hotlcc",
|
||||
"level": 2
|
||||
"level": 2,
|
||||
"history": {
|
||||
"v1.1": "优化插件配置生效;支持青蛙辅种助手。"
|
||||
}
|
||||
},
|
||||
"TmdbWallpaper": {
|
||||
"name": "登录壁纸本地化",
|
||||
|
||||
@@ -10,6 +10,9 @@ from typing import Any, List, Dict, Tuple, Optional, Union, Set
|
||||
from urllib.parse import urlparse, parse_qs, unquote
|
||||
|
||||
import pytz
|
||||
from app.helper.sites import SitesHelper
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
|
||||
from app import schemas
|
||||
from app.chain.torrents import TorrentsChain
|
||||
from app.core.config import settings
|
||||
@@ -17,7 +20,6 @@ from app.core.context import MediaInfo
|
||||
from app.core.metainfo import MetaInfo
|
||||
from app.db.site_oper import SiteOper
|
||||
from app.db.subscribe_oper import SubscribeOper
|
||||
from app.helper.sites import SitesHelper
|
||||
from app.log import logger
|
||||
from app.modules.qbittorrent import Qbittorrent
|
||||
from app.modules.transmission import Transmission
|
||||
@@ -25,7 +27,6 @@ from app.plugins import _PluginBase
|
||||
from app.schemas import NotificationType, TorrentInfo, MediaType
|
||||
from app.utils.http import RequestUtils
|
||||
from app.utils.string import StringUtils
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
|
||||
lock = threading.Lock()
|
||||
|
||||
@@ -146,9 +147,10 @@ class BrushConfig:
|
||||
|
||||
@staticmethod
|
||||
def get_demo_site_config() -> str:
|
||||
desc = ("// 以下为配置示例,请参考:https://github.com/InfinityPacer/MoviePilot-Plugins/blob/main/README.md 进行配置\n"
|
||||
"// 如与全局保持一致的配置项,请勿在站点配置中配置\n"
|
||||
"// 注意无关内容需使用 // 注释\n")
|
||||
desc = (
|
||||
"// 以下为配置示例,请参考:https://github.com/InfinityPacer/MoviePilot-Plugins/blob/main/plugins/brushflowlowfreq/README.md 进行配置\n"
|
||||
"// 如与全局保持一致的配置项,请勿在站点配置中配置\n"
|
||||
"// 注意无关内容需使用 // 注释\n")
|
||||
config = """[{
|
||||
"sitename": "站点1",
|
||||
"seed_time": 96,
|
||||
@@ -249,7 +251,7 @@ class BrushFlow(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "brush.jpg"
|
||||
# 插件版本
|
||||
plugin_version = "2.9"
|
||||
plugin_version = "3.0"
|
||||
# 插件作者
|
||||
plugin_author = "jxxghp,InfinityPacer"
|
||||
# 作者主页
|
||||
@@ -1239,7 +1241,7 @@ class BrushFlow(_PluginBase):
|
||||
"component": "VSwitch",
|
||||
"props": {
|
||||
"model": "dialog_closed",
|
||||
"label": "设置站点"
|
||||
"label": "打开站点配置窗口"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1342,15 +1344,20 @@ class BrushFlow(_PluginBase):
|
||||
'content': [
|
||||
{
|
||||
'component': 'span',
|
||||
'text': '部分配置项以及细节请参考:'
|
||||
'text': '注意:详细配置说明以及刷流规则请参考:'
|
||||
},
|
||||
{
|
||||
'component': 'a',
|
||||
'props': {
|
||||
'href': 'https://github.com/InfinityPacer/MoviePilot-Plugins/blob/main/README.md',
|
||||
'href': 'https://github.com/InfinityPacer/MoviePilot-Plugins/blob/main/plugins/brushflowlowfreq/README.md',
|
||||
'target': '_blank'
|
||||
},
|
||||
'text': 'https://github.com/InfinityPacer/MoviePilot-Plugins/blob/main/README.md'
|
||||
'content': [
|
||||
{
|
||||
'component': 'u',
|
||||
'text': 'README'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1449,10 +1456,15 @@ class BrushFlow(_PluginBase):
|
||||
{
|
||||
'component': 'a',
|
||||
'props': {
|
||||
'href': 'https://github.com/InfinityPacer/MoviePilot-Plugins/blob/main/README.md',
|
||||
'href': 'https://github.com/InfinityPacer/MoviePilot-Plugins/blob/main/plugins/brushflowlowfreq/README.md',
|
||||
'target': '_blank'
|
||||
},
|
||||
'text': 'https://github.com/InfinityPacer/MoviePilot-Plugins/blob/main/README.md'
|
||||
'content': [
|
||||
{
|
||||
'component': 'u',
|
||||
'text': 'README'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1551,6 +1563,11 @@ class BrushFlow(_PluginBase):
|
||||
return [
|
||||
{
|
||||
'component': 'VRow',
|
||||
'props': {
|
||||
'style': {
|
||||
'overflow': 'hidden',
|
||||
}
|
||||
},
|
||||
'content': [
|
||||
# 总上传量
|
||||
{
|
||||
@@ -2149,6 +2166,12 @@ class BrushFlow(_PluginBase):
|
||||
torrent_tasks.values()):
|
||||
return False, "重复种子"
|
||||
|
||||
# 不同站点如果遇到相同种子,判断前一个种子是否已经在做种,否则排除处理
|
||||
if torrent.title:
|
||||
if any(torrent.site_name != f"{task.get('site_name')}" and torrent.title == f"{task.get('title')}"
|
||||
and not task.get("seed_time") for task in torrent_tasks.values()):
|
||||
return False, "其他站点存在尚未下载完成的相同种子"
|
||||
|
||||
# 促销条件
|
||||
if brush_config.freeleech and torrent.downloadvolumefactor != 0:
|
||||
return False, "非免费种子"
|
||||
@@ -2286,15 +2309,15 @@ class BrushFlow(_PluginBase):
|
||||
# 如果配置了动态删除以及删种阈值,则根据动态删种进行分组处理
|
||||
if brush_config.proxy_delete and brush_config.delete_size_range:
|
||||
logger.info("已开启动态删种,按系统默认动态删种条件开始检查任务")
|
||||
proxy_delete_hashs = self.__delete_torrent_for_proxy(torrents=check_torrents,
|
||||
torrent_tasks=torrent_tasks) or []
|
||||
need_delete_hashes.extend(proxy_delete_hashs)
|
||||
proxy_delete_hashes = self.__delete_torrent_for_proxy(torrents=check_torrents,
|
||||
torrent_tasks=torrent_tasks) or []
|
||||
need_delete_hashes.extend(proxy_delete_hashes)
|
||||
# 否则均认为是没有开启动态删种
|
||||
else:
|
||||
logger.info("没有开启动态删种,按用户设置删种条件开始检查任务")
|
||||
not_proxy_delete_hashs = self.__delete_torrent_for_evaluate_conditions(torrents=check_torrents,
|
||||
torrent_tasks=torrent_tasks) or []
|
||||
need_delete_hashes.extend(not_proxy_delete_hashs)
|
||||
not_proxy_delete_hashes = self.__delete_torrent_for_evaluate_conditions(torrents=check_torrents,
|
||||
torrent_tasks=torrent_tasks) or []
|
||||
need_delete_hashes.extend(not_proxy_delete_hashes)
|
||||
|
||||
if need_delete_hashes:
|
||||
if downloader.delete_torrents(ids=need_delete_hashes, delete_file=True):
|
||||
@@ -2493,7 +2516,7 @@ class BrushFlow(_PluginBase):
|
||||
根据条件删除种子并获取已删除列表
|
||||
"""
|
||||
brush_config = self.__get_brush_config()
|
||||
delete_hashs = []
|
||||
delete_hashes = []
|
||||
|
||||
for torrent in torrents:
|
||||
torrent_hash = self.__get_hash(torrent)
|
||||
@@ -2512,8 +2535,8 @@ class BrushFlow(_PluginBase):
|
||||
torrent_info=torrent_info,
|
||||
torrent_task=torrent_task)
|
||||
if should_delete:
|
||||
delete_hashs.append(torrent_hash)
|
||||
reason = "触发动态删除," + reason if proxy_delete else reason
|
||||
delete_hashes.append(torrent_hash)
|
||||
reason = "触发动态删除阈值," + reason if proxy_delete else reason
|
||||
self.__send_delete_message(site_name=site_name, torrent_title=torrent_title, torrent_desc=torrent_desc,
|
||||
reason=reason)
|
||||
logger.info(f"站点:{site_name},{reason},删除种子:{torrent_title}|{torrent_desc}")
|
||||
@@ -2521,7 +2544,7 @@ class BrushFlow(_PluginBase):
|
||||
if brush_config.log_more:
|
||||
logger.info(f"站点:{site_name},{reason},不删除种子:{torrent_title}|{torrent_desc}")
|
||||
|
||||
return delete_hashs
|
||||
return delete_hashes
|
||||
|
||||
def __delete_torrent_for_evaluate_proxy_pre_conditions(self, torrents: List[Any],
|
||||
torrent_tasks: Dict[str, dict]) -> List:
|
||||
@@ -2529,7 +2552,7 @@ class BrushFlow(_PluginBase):
|
||||
根据动态删除前置条件排除H&R种子后删除种子并获取已删除列表
|
||||
"""
|
||||
brush_config = self.__get_brush_config()
|
||||
delete_hashs = []
|
||||
delete_hashes = []
|
||||
|
||||
for torrent in torrents:
|
||||
torrent_hash = self.__get_hash(torrent)
|
||||
@@ -2552,7 +2575,7 @@ class BrushFlow(_PluginBase):
|
||||
should_delete, reason = self.__evaluate_proxy_pre_conditions_for_delete(site_name=site_name,
|
||||
torrent_info=torrent_info)
|
||||
if should_delete:
|
||||
delete_hashs.append(torrent_hash)
|
||||
delete_hashes.append(torrent_hash)
|
||||
self.__send_delete_message(site_name=site_name, torrent_title=torrent_title, torrent_desc=torrent_desc,
|
||||
reason=reason)
|
||||
logger.info(f"站点:{site_name},{reason},删除种子:{torrent_title}|{torrent_desc}")
|
||||
@@ -2560,7 +2583,7 @@ class BrushFlow(_PluginBase):
|
||||
if brush_config.log_more:
|
||||
logger.info(f"站点:{site_name},{reason},不删除种子:{torrent_title}|{torrent_desc}")
|
||||
|
||||
return delete_hashs
|
||||
return delete_hashes
|
||||
|
||||
def __delete_torrent_for_proxy(self, torrents: List[Any], torrent_tasks: Dict[str, dict]) -> List:
|
||||
"""
|
||||
@@ -3557,7 +3580,7 @@ class BrushFlow(_PluginBase):
|
||||
now = datetime.now()
|
||||
return (now - pubdate).total_seconds() // 60
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
logger.error(f"发布时间 {pubdate} 获取分钟失败,错误详情: {e}")
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -39,7 +39,7 @@ class ContractCheck(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "contract.png"
|
||||
# 插件版本
|
||||
plugin_version = "1.1"
|
||||
plugin_version = "1.2"
|
||||
# 插件作者
|
||||
plugin_author = "DzAvril"
|
||||
# 作者主页
|
||||
@@ -713,7 +713,7 @@ class ContractCheck(_PluginBase):
|
||||
else:
|
||||
num_gap = contract_info.num - current_seeding_size[0]
|
||||
is_satisfied = is_size_satisfied and is_num_satisfied
|
||||
duration = (datetime.now().date() - contract_info.date).days
|
||||
duration = (datetime.now() - contract_info.date).days
|
||||
if duration < contract_info.duration:
|
||||
duration_gap = contract_info.duration - duration
|
||||
return is_satisfied, size_gap, num_gap, duration_gap
|
||||
|
||||
@@ -69,6 +69,7 @@ class ISiteUserInfo(metaclass=ABCMeta):
|
||||
"UBits": ["UBits"],
|
||||
"听听歌": ["TTG", "WiKi", "DoA", "NGB", "ARiN"],
|
||||
"馒头": ["MTeam", "MTeamTV"],
|
||||
"朋友": ["FRDS"],
|
||||
}
|
||||
|
||||
# 错误信息
|
||||
|
||||
@@ -3,11 +3,12 @@ import os
|
||||
from typing import Any, List, Dict, Tuple
|
||||
|
||||
from app.core.event import eventmanager, Event
|
||||
from app.db.models.site import Site
|
||||
from app.core.plugin import PluginManager
|
||||
from app.db.site_oper import SiteOper
|
||||
from app.db.systemconfig_oper import SystemConfigOper
|
||||
from app.log import logger
|
||||
from app.plugins import _PluginBase
|
||||
from app.scheduler import Scheduler
|
||||
from app.schemas.types import SystemConfigKey, EventType
|
||||
|
||||
|
||||
@@ -19,7 +20,7 @@ class MergeSiteSwitch(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "world.png"
|
||||
# 插件版本
|
||||
plugin_version = "1.0"
|
||||
plugin_version = "1.1"
|
||||
# 插件作者
|
||||
plugin_author = "hotlcc"
|
||||
# 作者主页
|
||||
@@ -46,6 +47,8 @@ class MergeSiteSwitch(_PluginBase):
|
||||
__plugin_id_iyuu_auto_seed: str = 'IYUUAutoSeed'
|
||||
# 站点刷流
|
||||
__plugin_id_brush_flow: str = 'BrushFlow'
|
||||
# 青蛙辅种助手
|
||||
__plugin_id_cross_seed: str = 'CrossSeed'
|
||||
|
||||
# 配置相关
|
||||
# 插件缺省配置
|
||||
@@ -369,6 +372,43 @@ class MergeSiteSwitch(_PluginBase):
|
||||
}]
|
||||
}]
|
||||
})
|
||||
# 青蛙辅种助手
|
||||
if self.__plugin_id_cross_seed in installed_plugin_ids:
|
||||
form_content.append({
|
||||
'component': 'VRow',
|
||||
'content': [{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 9, 'xl': 9, 'lg': 9, 'md': 9, 'sm': 8, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSelect',
|
||||
'props': {
|
||||
'model': 'cross_seed_sites',
|
||||
'label': '插件 / 青蛙辅种助手 / 辅种站点',
|
||||
'multiple': True,
|
||||
'chips': True,
|
||||
'items': site_options,
|
||||
'hint': '只有选中的站点才会在辅种中使用。'
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 4, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'cross_seed_follow_enable_sites',
|
||||
'label': '跟随启用的站点',
|
||||
'hint': '与站点的启用状态保持一致,保存时会立即生效,并在后台监听站点状态变化实时生效。'
|
||||
}
|
||||
}]
|
||||
}]
|
||||
})
|
||||
# 提示信息
|
||||
form_content.append({
|
||||
'component': 'VRow',
|
||||
@@ -500,6 +540,12 @@ class MergeSiteSwitch(_PluginBase):
|
||||
"""
|
||||
return self.__check_follow_enable_sites(config_key='brush_flow_follow_enable_sites', plugin_id=self.__plugin_id_brush_flow, installed_plugin_ids=installed_plugin_ids)
|
||||
|
||||
def __check_cross_seed_follow_enable_sites(self, installed_plugin_ids: List[str] = None) -> bool:
|
||||
"""
|
||||
判断青蛙辅种站点的跟随按钮是否打开
|
||||
"""
|
||||
return self.__check_follow_enable_sites(config_key='cross_seed_follow_enable_sites', plugin_id=self.__plugin_id_cross_seed, installed_plugin_ids=installed_plugin_ids)
|
||||
|
||||
def __check_any_follow_enable_sites(self) -> bool:
|
||||
"""
|
||||
判断是否开启任意跟随按钮
|
||||
@@ -564,6 +610,10 @@ class MergeSiteSwitch(_PluginBase):
|
||||
config.update({
|
||||
'brush_flow_sites': self.__get_brush_flow_site_ids(),
|
||||
})
|
||||
if self.__plugin_id_cross_seed in installed_plugin_ids:
|
||||
config.update({
|
||||
'cross_seed_sites': self.__get_cross_seed_site_ids(),
|
||||
})
|
||||
self.update_config(config=config)
|
||||
return config
|
||||
|
||||
@@ -588,6 +638,8 @@ class MergeSiteSwitch(_PluginBase):
|
||||
config.update({"iyuu_seed_sites": enable_sites.copy()})
|
||||
if config.get('brush_flow_follow_enable_sites'):
|
||||
config.update({"brush_flow_sites": enable_sites.copy()})
|
||||
if config.get('cross_seed_follow_enable_sites'):
|
||||
config.update({"cross_seed_sites": enable_sites.copy()})
|
||||
return config
|
||||
|
||||
def __pre_config(self, config: dict) -> dict:
|
||||
@@ -621,6 +673,8 @@ class MergeSiteSwitch(_PluginBase):
|
||||
self.__set_iyuu_seed_site_ids(config.get('iyuu_seed_sites'))
|
||||
if self.__plugin_id_brush_flow in installed_plugin_ids:
|
||||
self.__set_brush_flow_site_ids(config.get('brush_flow_sites'))
|
||||
if self.__plugin_id_cross_seed in installed_plugin_ids:
|
||||
self.__set_cross_seed_site_ids(config.get('cross_seed_sites'))
|
||||
return config
|
||||
|
||||
def __get_enable_site_ids(self) -> List[int]:
|
||||
@@ -687,6 +741,19 @@ class MergeSiteSwitch(_PluginBase):
|
||||
return None
|
||||
return config.get(config_key)
|
||||
|
||||
def __reload_plugin_config(self, plugin_id: str, config: dict = None):
|
||||
"""
|
||||
重载插件配置
|
||||
"""
|
||||
if not plugin_id:
|
||||
return
|
||||
if not config:
|
||||
config = self.get_config(plugin_id)
|
||||
# 重新生效插件
|
||||
PluginManager().init_plugin(plugin_id, config)
|
||||
# 注册插件服务
|
||||
Scheduler().update_plugin_job(plugin_id)
|
||||
|
||||
def __set_plugin_config_value(self, plugin_id: str, config_key: str, config_value: Any) -> Any:
|
||||
"""
|
||||
设置插件配置值
|
||||
@@ -698,6 +765,7 @@ class MergeSiteSwitch(_PluginBase):
|
||||
config = {}
|
||||
config.update({config_key: config_value})
|
||||
self.update_config(plugin_id=plugin_id, config=config)
|
||||
self.__reload_plugin_config(plugin_id=plugin_id, config=config)
|
||||
|
||||
def __get_signin_site_ids(self) -> List[int]:
|
||||
"""
|
||||
@@ -769,6 +837,20 @@ class MergeSiteSwitch(_PluginBase):
|
||||
self.__set_plugin_config_value(self.__plugin_id_brush_flow, 'brushsites', site_ids)
|
||||
logger.info("刷流站点配置完成")
|
||||
|
||||
def __get_cross_seed_site_ids(self) -> List[int]:
|
||||
"""
|
||||
获取青蛙辅种站点IDs
|
||||
"""
|
||||
sites = self.__get_plugin_config_value(self.__plugin_id_cross_seed, 'sites')
|
||||
return sites if sites else []
|
||||
|
||||
def __set_cross_seed_site_ids(self, site_ids: List[int]):
|
||||
"""
|
||||
设置青蛙辅种站点IDs
|
||||
"""
|
||||
self.__set_plugin_config_value(self.__plugin_id_cross_seed, 'sites', site_ids)
|
||||
logger.info("青蛙辅种站点配置完成")
|
||||
|
||||
def __update_search_site_ids_by_site(self, site_id: int, site_status: bool):
|
||||
if site_id == None:
|
||||
return
|
||||
@@ -846,6 +928,17 @@ class MergeSiteSwitch(_PluginBase):
|
||||
site_ids.remove(site_id)
|
||||
self.__set_brush_flow_site_ids(site_ids=site_ids)
|
||||
|
||||
def __update_cross_seed_site_ids_by_site(self, site_id: int, site_status: bool):
|
||||
if site_id == None:
|
||||
return
|
||||
site_ids = self.__get_cross_seed_site_ids() or []
|
||||
if site_id not in site_ids and site_status:
|
||||
site_ids.append(site_id)
|
||||
self.__set_cross_seed_site_ids(site_ids=site_ids)
|
||||
elif site_id in site_ids and not site_status:
|
||||
site_ids.remove(site_id)
|
||||
self.__set_cross_seed_site_ids(site_ids=site_ids)
|
||||
|
||||
def __update_site_ids_for_site_event(self, site_id: int, site_status: bool):
|
||||
"""
|
||||
针对站点事件更新各项配置
|
||||
@@ -868,6 +961,8 @@ class MergeSiteSwitch(_PluginBase):
|
||||
self.__update_iyuu_seed_site_ids_by_site(site_id=site_id, site_status=site_status)
|
||||
if self.__check_brush_flow_follow_enable_sites(installed_plugin_ids=installed_plugin_ids):
|
||||
self.__update_brush_flow_site_ids_by_site(site_id=site_id, site_status=site_status)
|
||||
if self.__check_cross_seed_follow_enable_sites(installed_plugin_ids=installed_plugin_ids):
|
||||
self.__update_cross_seed_site_ids_by_site(site_id=site_id, site_status=site_status)
|
||||
|
||||
@eventmanager.register(EventType.SiteUpdated)
|
||||
def listen_site_updated_event(self, event: Event = None):
|
||||
|
||||
@@ -27,7 +27,7 @@ class QbCommand(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "Qbittorrent_A.png"
|
||||
# 插件版本
|
||||
plugin_version = "1.4"
|
||||
plugin_version = "1.5"
|
||||
# 插件作者
|
||||
plugin_author = "DzAvril"
|
||||
# 作者主页
|
||||
@@ -60,7 +60,7 @@ class QbCommand(_PluginBase):
|
||||
_op_sites = []
|
||||
_multi_level_root_domain = ["edu.cn", "com.cn", "net.cn", "org.cn"]
|
||||
_scheduler = None
|
||||
|
||||
_exclude_dirs = ""
|
||||
def init_plugin(self, config: dict = None):
|
||||
self._sites = SitesHelper()
|
||||
self._siteoper = SiteOper()
|
||||
@@ -87,6 +87,7 @@ class QbCommand(_PluginBase):
|
||||
all_sites = [site for site in self._sites.get_indexers() if not site.get("public")] + self.__custom_sites()
|
||||
# 过滤掉没有选中的站点
|
||||
self._op_sites = [site for site in all_sites if site.get("id") in self._op_site_ids]
|
||||
self._exclude_dirs = config.get("exclude_dirs") or ""
|
||||
|
||||
if self._only_pause_once or self._only_resume_once:
|
||||
if self._only_pause_once and self._only_resume_once:
|
||||
@@ -121,6 +122,7 @@ class QbCommand(_PluginBase):
|
||||
"pause_cron": self._pause_cron,
|
||||
"resume_cron": self._resume_cron,
|
||||
"op_site_ids": self._op_site_ids,
|
||||
"exclude_dirs": self._exclude_dirs,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -430,14 +432,6 @@ class QbCommand(_PluginBase):
|
||||
hash_downloading, hash_uploading, hash_paused, hash_checking, hash_error = (
|
||||
self.get_torrents_status(all_torrents)
|
||||
)
|
||||
if type == self.TorrentType.DOWNLOADING:
|
||||
to_be_paused = hash_downloading
|
||||
elif type == self.TorrentType.UPLOADING:
|
||||
to_be_paused = hash_uploading
|
||||
elif type == self.TorrentType.CHECKING:
|
||||
to_be_paused = hash_checking
|
||||
else:
|
||||
to_be_paused = hash_downloading + hash_uploading + hash_checking
|
||||
|
||||
logger.info(
|
||||
f"暂定任务启动 \n"
|
||||
@@ -461,6 +455,19 @@ class QbCommand(_PluginBase):
|
||||
f"错误数量: {len(hash_error)}\n"
|
||||
f"暂停操作中请稍等...\n",
|
||||
)
|
||||
pause_torrents = self.filter_pause_torrents(all_torrents)
|
||||
hash_downloading, hash_uploading, hash_paused, hash_checking, hash_error = (
|
||||
self.get_torrents_status(pause_torrents)
|
||||
)
|
||||
if type == self.TorrentType.DOWNLOADING:
|
||||
to_be_paused = hash_downloading
|
||||
elif type == self.TorrentType.UPLOADING:
|
||||
to_be_paused = hash_uploading
|
||||
elif type == self.TorrentType.CHECKING:
|
||||
to_be_paused = hash_checking
|
||||
else:
|
||||
to_be_paused = hash_downloading + hash_uploading + hash_checking
|
||||
|
||||
if len(to_be_paused) > 0:
|
||||
if self._qb.stop_torrents(ids=to_be_paused):
|
||||
logger.info(f"暂停了{len(to_be_paused)}个种子")
|
||||
@@ -501,6 +508,22 @@ class QbCommand(_PluginBase):
|
||||
f"错误数量: {len(hash_error)}\n",
|
||||
)
|
||||
|
||||
def __is_excluded(self, file_path) -> bool:
|
||||
"""
|
||||
是否排除目录
|
||||
"""
|
||||
for exclude_dir in self._exclude_dirs.split("\n"):
|
||||
if exclude_dir and exclude_dir in str(file_path):
|
||||
return True
|
||||
return False
|
||||
def filter_pause_torrents(self, all_torrents):
|
||||
torrents = []
|
||||
for torrent in all_torrents:
|
||||
if self.__is_excluded(torrent.get("content_path")):
|
||||
continue
|
||||
torrents.append(torrent)
|
||||
return torrents
|
||||
|
||||
@eventmanager.register(EventType.PluginAction)
|
||||
def handle_resume_torrent(self, event: Event):
|
||||
if not self._enabled:
|
||||
@@ -516,7 +539,6 @@ class QbCommand(_PluginBase):
|
||||
return
|
||||
|
||||
all_torrents = self.get_all_torrents()
|
||||
all_torrents = self.filter_torrents(all_torrents)
|
||||
hash_downloading, hash_uploading, hash_paused, hash_checking, hash_error = (
|
||||
self.get_torrents_status(all_torrents)
|
||||
)
|
||||
@@ -542,6 +564,11 @@ class QbCommand(_PluginBase):
|
||||
f"错误数量: {len(hash_error)}\n"
|
||||
f"开始操作中请稍等...\n",
|
||||
)
|
||||
|
||||
resume_torrents = self.filter_resume_torrents(all_torrents)
|
||||
hash_downloading, hash_uploading, hash_paused, hash_checking, hash_error = (
|
||||
self.get_torrents_status(resume_torrents)
|
||||
)
|
||||
if not self._qb.start_torrents(ids=hash_paused):
|
||||
logger.error(f"开始种子失败")
|
||||
if self._notify:
|
||||
@@ -579,7 +606,7 @@ class QbCommand(_PluginBase):
|
||||
f"错误数量: {len(hash_error)}\n",
|
||||
)
|
||||
|
||||
def filter_torrents(self, all_torrents):
|
||||
def filter_resume_torrents(self, all_torrents):
|
||||
"""
|
||||
过滤掉不参与保种的种子
|
||||
"""
|
||||
@@ -644,14 +671,13 @@ class QbCommand(_PluginBase):
|
||||
if self._notify:
|
||||
self.post_message(
|
||||
mtype=NotificationType.SiteMessage,
|
||||
title=f"【QB开始任务启动】",
|
||||
title=f"【QB任务状态】",
|
||||
text=f"种子总数: {len(all_torrents)} \n"
|
||||
f"做种数量: {len(hash_uploading)}\n"
|
||||
f"下载数量: {len(hash_downloading)}\n"
|
||||
f"检查数量: {len(hash_checking)}\n"
|
||||
f"暂停数量: {len(hash_paused)}\n"
|
||||
f"错误数量: {len(hash_error)}\n"
|
||||
f"开始操作中请稍等...\n",
|
||||
)
|
||||
|
||||
@eventmanager.register(EventType.PluginAction)
|
||||
@@ -747,7 +773,7 @@ class QbCommand(_PluginBase):
|
||||
elif flag is None and self._enabled and self._enable_upload_limit:
|
||||
flag = self.set_upload_limit(upload_limit)
|
||||
|
||||
if flag:
|
||||
if flag == True:
|
||||
logger.info(f"设置QB限速成功")
|
||||
if self._notify:
|
||||
if upload_limit == 0:
|
||||
@@ -763,7 +789,7 @@ class QbCommand(_PluginBase):
|
||||
title=f"【QB远程操作】",
|
||||
text=text,
|
||||
)
|
||||
elif not flag:
|
||||
elif flag == False:
|
||||
logger.error(f"QB设置限速失败")
|
||||
if self._notify:
|
||||
self.post_message(
|
||||
@@ -1054,6 +1080,26 @@ class QbCommand(_PluginBase):
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"component": "VRow",
|
||||
"content": [
|
||||
{
|
||||
"component": "VCol",
|
||||
"props": {"cols": 12},
|
||||
"content": [
|
||||
{
|
||||
"component": "VTextarea",
|
||||
"props": {
|
||||
"model": "exclude_dirs",
|
||||
"label": "不暂停保种目录",
|
||||
"rows": 5,
|
||||
"placeholder": "该目录下的做种不会暂停,一行一个目录",
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"component": "VRow",
|
||||
"content": [
|
||||
|
||||
@@ -43,7 +43,7 @@ class SiteStatistic(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "statistic.png"
|
||||
# 插件版本
|
||||
plugin_version = "3.0"
|
||||
plugin_version = "3.1"
|
||||
# 插件作者
|
||||
plugin_author = "lightolly"
|
||||
# 作者主页
|
||||
|
||||
@@ -26,6 +26,7 @@ class SiteSchema(Enum):
|
||||
NexusProject = "NexusProject"
|
||||
NexusRabbit = "NexusRabbit"
|
||||
NexusHhanclub = "NexusHhanclub"
|
||||
NexusAudiences = "NexusAudiences"
|
||||
SmallHorse = "Small Horse"
|
||||
Unit3d = "Unit3d"
|
||||
TorrentLeech = "TorrentLeech"
|
||||
|
||||
18
plugins/sitestatistic/siteuserinfo/nexus_audiences.py
Normal file
18
plugins/sitestatistic/siteuserinfo/nexus_audiences.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from app.plugins.sitestatistic.siteuserinfo import SITE_BASE_ORDER, SiteSchema
|
||||
from app.plugins.sitestatistic.siteuserinfo.nexus_php import NexusPhpSiteUserInfo
|
||||
|
||||
|
||||
class NexusAudiencesSiteUserInfo(NexusPhpSiteUserInfo):
|
||||
schema = SiteSchema.NexusAudiences
|
||||
order = SITE_BASE_ORDER + 5
|
||||
|
||||
@classmethod
|
||||
def match(cls, html_text: str) -> bool:
|
||||
return 'audiences.me' in html_text
|
||||
|
||||
def _parse_seeding_pages(self):
|
||||
self._torrent_seeding_headers = {"Referer": urljoin(self._base_url, self._user_detail_page)}
|
||||
super()._parse_seeding_pages()
|
||||
@@ -5,8 +5,8 @@ import threading
|
||||
import time
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from time import sleep
|
||||
from typing import List, Tuple, Dict, Any, Optional
|
||||
|
||||
import pytz
|
||||
import qbittorrentapi
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
@@ -14,12 +14,13 @@ from apscheduler.triggers.cron import CronTrigger
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.observers.polling import PollingObserver
|
||||
|
||||
from app import schemas
|
||||
from app.chain.tmdb import TmdbChain
|
||||
from app.chain.transfer import TransferChain
|
||||
from app.core.config import settings
|
||||
from app.core.context import MediaInfo
|
||||
from app.core.event import eventmanager, Event
|
||||
from app.core.metainfo import MetaInfoPath
|
||||
from app.db.downloadhistory_oper import DownloadHistoryOper
|
||||
from app.db.transferhistory_oper import TransferHistoryOper
|
||||
from app.log import logger
|
||||
@@ -76,7 +77,7 @@ class VCBAnimeMonitor(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "vcbmonitor.png"
|
||||
# 插件版本
|
||||
plugin_version = "1.7"
|
||||
plugin_version = "1.7.1"
|
||||
# 插件作者
|
||||
plugin_author = "pixel@qingwa"
|
||||
# 作者主页
|
||||
@@ -155,15 +156,20 @@ class VCBAnimeMonitor(_PluginBase):
|
||||
self._scheduler.add_job(self.send_msg, trigger='interval', seconds=15)
|
||||
self.qb = Qbittorrent()
|
||||
|
||||
# 读取目录配置
|
||||
monitor_dirs = self._monitor_dirs.split("\n")
|
||||
if not monitor_dirs:
|
||||
return
|
||||
|
||||
# 启用种子目录监控
|
||||
if self._torrents_path is not None and Path(self._torrents_path).exists() and self._enabled:
|
||||
# 只取第一个目录作为新的保存
|
||||
first_path = monitor_dirs[0]
|
||||
if SystemUtils.is_windows():
|
||||
self.new_save_path = self._monitor_dirs.split(':')[0] + ":" + self._monitor_dirs.split(':')[1]
|
||||
self.new_save_path = first_path.split(':')[0] + ":" + first_path.split(':')[1]
|
||||
else:
|
||||
self.new_save_path = self._monitor_dirs.split(':')[0]
|
||||
print(self.new_save_path)
|
||||
|
||||
self.new_save_path = first_path.split(':')[0]
|
||||
# print(self.new_save_path)
|
||||
try:
|
||||
observer = Observer()
|
||||
self._observer.append(observer)
|
||||
@@ -177,10 +183,6 @@ class VCBAnimeMonitor(_PluginBase):
|
||||
else:
|
||||
logger.info("种子目录为空,不转移qb中正在下载的VCB-Studio文件")
|
||||
|
||||
# 读取目录配置
|
||||
monitor_dirs = self._monitor_dirs.split("\n")
|
||||
if not monitor_dirs:
|
||||
return
|
||||
for mon_path in monitor_dirs:
|
||||
# 格式源目录:目的目录
|
||||
if not mon_path:
|
||||
@@ -594,6 +596,7 @@ class VCBAnimeMonitor(_PluginBase):
|
||||
def torrent_event(self, event, mon_path: str, text: str):
|
||||
"""
|
||||
处理种子文件
|
||||
:param mon_path: 种子目录
|
||||
"""
|
||||
evc_path = Path(event.src_path)
|
||||
if not event.is_directory and (evc_path.suffix == ".torrent" or str(evc_path).split('.')[1] == "torrent"):
|
||||
@@ -1009,8 +1012,7 @@ class VCBAnimeMonitor(_PluginBase):
|
||||
'props': {
|
||||
'type': 'info',
|
||||
'variant': 'tonal',
|
||||
'text': '核心用法与目录同步插件相同,不同点在于只识别处理VCB-Studio资源,'
|
||||
'避免与目录同步插件的监控目录相同(否则会同时进行识别)'
|
||||
'text': '核心用法与目录同步插件相同,不同点在于只识别处理VCB-Studio资源,\n'
|
||||
'不处理SPs目录下的文件,OVA/OAD集数根据入库顺序累加命名,不保证与TMDB集数匹配'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@ import concurrent
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
import roman
|
||||
|
||||
from app.chain.media import MediaChain
|
||||
from app.chain.tmdb import TmdbChain
|
||||
from app.core.metainfo import MetaInfoPath
|
||||
@@ -12,6 +9,26 @@ from app.log import logger
|
||||
from app.schemas import MediaType
|
||||
|
||||
|
||||
def roman_to_int(s) -> int:
|
||||
"""
|
||||
:param s: 罗马数字字符串
|
||||
罗马数字转整数
|
||||
"""
|
||||
roman_dict = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
|
||||
total = 0
|
||||
prev_value = 0
|
||||
|
||||
for char in reversed(s): # 反向遍历罗马数字字符串
|
||||
current_value = roman_dict[char]
|
||||
if current_value >= prev_value:
|
||||
total += current_value # 如果当前值大于等于前一个值,加上当前值
|
||||
else:
|
||||
total -= current_value # 如果当前值小于前一个值,减去当前值
|
||||
prev_value = current_value
|
||||
|
||||
return total
|
||||
|
||||
|
||||
class ReMeta:
|
||||
# 解析之后的标题:
|
||||
title: str = None
|
||||
@@ -41,7 +58,7 @@ class ReMeta:
|
||||
]
|
||||
_ova_patterns = [re.compile(r"\[.*?(OVA|OAD).*?]"),
|
||||
re.compile(r"\[\d+\.5]"),
|
||||
re.compile(r"\[00]")]
|
||||
re.compile(r"\[00\]")]
|
||||
|
||||
final_season_patterns = [re.compile('final season', re.IGNORECASE),
|
||||
re.compile('The Final', re.IGNORECASE),
|
||||
@@ -57,13 +74,14 @@ class ReMeta:
|
||||
def handel_file(self, file_path: Path):
|
||||
meta = MetaInfoPath(file_path)
|
||||
self.title = meta.title
|
||||
self.title = Path(self.title).stem.strip()
|
||||
if 'VCB-Studio' not in meta.title:
|
||||
logger.warn("不属于VCB的作品,不处理!")
|
||||
return None
|
||||
if meta.title.count("[") != 4 and meta.title.count("]") != 4:
|
||||
# 可能是电影,电影只有三组[],因此去除所有[]后只剩下电影名
|
||||
logger.warn("不符合VCB-Studio的剧集命名规范,跳过剧集模块处理!交给默认处理逻辑")
|
||||
meta.title = re.sub(r'\[.*?]', '', meta.title).strip()
|
||||
meta.title = re.sub(r'\[.*?\]', '', meta.title).strip()
|
||||
meta.en_name = meta.title
|
||||
return meta
|
||||
split_title: List[str] | None = self.split_season_ep(self.title)
|
||||
@@ -84,13 +102,12 @@ class ReMeta:
|
||||
return meta
|
||||
|
||||
# 分离季度部分和集数部分
|
||||
@staticmethod
|
||||
def split_season_ep(pre_title: str):
|
||||
def split_season_ep(self, pre_title: str):
|
||||
split_ep = re.findall(r"(\[.*?])", pre_title)[1]
|
||||
if not split_ep:
|
||||
logger.warn("未识别出集数位置信息,结束识别!")
|
||||
return None
|
||||
split_title = re.sub(r"\[.*?]", "", pre_title).strip()
|
||||
split_title = re.sub(r"\[.*?\]", "", pre_title).strip()
|
||||
logger.info(f"分离出包含季度的部分:{split_title} \n 分离出包含集数的部分: {split_ep}")
|
||||
return [split_title, split_ep]
|
||||
|
||||
@@ -126,7 +143,7 @@ class ReMeta:
|
||||
match = pattern.search(pre_title)
|
||||
if match:
|
||||
if type(group) == str:
|
||||
title_season["season"] = int(roman.fromRoman(match.group(int(group))))
|
||||
title_season["season"] = roman_to_int(match.group(int(group)))
|
||||
title_season["title"] = re.sub(pattern, "", pre_title).strip()
|
||||
else:
|
||||
title_season["season"] = int(match.group(group))
|
||||
@@ -142,8 +159,7 @@ class ReMeta:
|
||||
return title_season
|
||||
|
||||
# 处理存在“Final”字样命名的季度
|
||||
@staticmethod
|
||||
def handle_final_season(title: str) -> int | None:
|
||||
def handle_final_season(self, title: str) -> int | None:
|
||||
medias = MediaChain().search(title=title)[1]
|
||||
if not medias:
|
||||
logger.warn("没有找到对应的媒体信息!")
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
roman~=4.1
|
||||
Reference in New Issue
Block a user