Compare commits

...

15 Commits

Author SHA1 Message Date
jxxghp
cb4981adb3 v2.0.7
- 修复了手动整理强制目录的问题
- 修复了AList无法整理文件的问题
- 修复了下载种子不使用全局UA的问题
- 修复了幼儿园的索引
- 修复了一处资源类型识别错误
- 用户认证现在也可以通过UI完成了
2024-11-19 20:42:25 +08:00
jxxghp
6880b42a84 fix #3161 2024-11-19 20:38:06 +08:00
jxxghp
97054adc61 fix 手动整理时强制目录 2024-11-19 20:22:31 +08:00
jxxghp
de94e5d595 fix #3166 2024-11-19 20:12:27 +08:00
jxxghp
a5a734d091 fix u115 transtype 2024-11-19 18:04:48 +08:00
jxxghp
efb607d22f Merge remote-tracking branch 'origin/v2' into v2 2024-11-19 13:31:52 +08:00
jxxghp
d0b2787a7c fix #1832 2024-11-19 13:11:54 +08:00
jxxghp
d5988ff443 Merge pull request #3165 from InfinityPacer/feature/module 2024-11-19 12:24:37 +08:00
InfinityPacer
96b4f1b575 feat(site): set default site timeout to 15 seconds 2024-11-19 11:10:01 +08:00
jxxghp
bb6b8439c7 fix siteauth scheduler 2024-11-19 08:39:39 +08:00
jxxghp
9cdce4509d fix siteauth schema 2024-11-19 08:25:12 +08:00
jxxghp
3956ab1fe8 add siteauth api 2024-11-19 08:18:26 +08:00
jxxghp
14686fdb03 合并拉取请求 #3159
fix: 去除资源搜索中多余的`订阅附加参数`过滤
2024-11-18 23:25:03 +08:00
Attente
32892ab747 fix: 去除资源搜索中多余的订阅附加参数过滤 2024-11-18 17:03:49 +08:00
jxxghp
79c637e003 fix #3154 相同事件避免并发处理 2024-11-18 08:01:43 +08:00
14 changed files with 107 additions and 46 deletions

View File

@@ -331,6 +331,29 @@ def read_rss_sites(db: Session = Depends(get_db),
return rss_sites
@router.get("/auth", summary="查询认证站点", response_model=dict)
def read_auth_sites(_: schemas.TokenPayload = Depends(verify_token)) -> dict:
"""
获取可认证站点列表
"""
return SitesHelper().get_authsites()
@router.post("/auth", summary="用户站点认证", response_model=schemas.Response)
def auth_site(
auth_info: schemas.SiteAuth,
_: User = Depends(get_current_active_superuser)
) -> Any:
"""
用户站点认证
"""
if not auth_info or not auth_info.site or not auth_info.params:
return schemas.Response(success=False, message="请输入认证站点和认证参数")
status, msg = SitesHelper().check_user(auth_info.site, auth_info.params)
SystemConfigOper().set(SystemConfigKey.UserSiteAuthParams, auth_info.dict())
return schemas.Response(success=status, message=msg)
@router.get("/{site_id}", summary="站点详情", response_model=schemas.Site)
def read_site(
site_id: int,

View File

@@ -180,7 +180,7 @@ class DownloadChain(ChainBase):
torrent_file, content, download_folder, files, error_msg = self.torrent.download_torrent(
url=torrent_url,
cookie=site_cookie,
ua=torrent.site_ua,
ua=torrent.site_ua or settings.USER_AGENT,
proxy=torrent.site_proxy)
if isinstance(content, str):

View File

@@ -365,10 +365,6 @@ class SubscribeChain(ChainBase):
torrent_info = context.torrent_info
torrent_mediainfo = context.media_info
# 匹配订阅附加参数
if not self.torrenthelper.filter_torrent(torrent_info=torrent_info,
filter_params=self.get_params(subscribe)):
continue
# 洗版
if subscribe.best_version:
# 洗版时,非整季不要

View File

@@ -389,14 +389,13 @@ class TransferChain(ChainBase):
download_hash = download_file.download_hash
# 查询整理目标目录
if not target_directory:
if target_path:
target_directory = self.directoryhelper.get_dir(file_mediainfo,
storage=target_storage, dest_path=target_path)
elif src_match:
if not target_directory and not target_path:
if src_match:
# 按源目录匹配,以便找到更合适的目录配置
target_directory = self.directoryhelper.get_dir(file_mediainfo,
storage=file_item.storage, src_path=file_path)
else:
# 未指定目标路径,根据媒体信息获取目标目录
target_directory = self.directoryhelper.get_dir(file_mediainfo)
# 执行整理

View File

@@ -84,6 +84,7 @@ class EventManager(metaclass=Singleton):
self.__disabled_handlers = set() # 禁用的事件处理器集合
self.__disabled_classes = set() # 禁用的事件处理器类集合
self.__lock = threading.Lock() # 线程锁
self.__processing_events = {} # 用于记录当前正在处理的事件 {event_hash: event}
def start(self):
"""
@@ -129,6 +130,14 @@ class EventManager(metaclass=Singleton):
for handler in handlers.values()
)
@staticmethod
def __get_event_hash(event: Event) -> str:
"""
计算事件的唯一标识符hash
"""
data_string = str(event.event_type.value) + str(event.event_data)
return str(uuid.uuid5(uuid.NAMESPACE_DNS, data_string))
def send_event(self, etype: Union[EventType, ChainEventType], data: Optional[Union[Dict, ChainEventData]] = None,
priority: int = DEFAULT_EVENT_PRIORITY) -> Optional[Event]:
"""
@@ -139,6 +148,12 @@ class EventManager(metaclass=Singleton):
:return: 如果是链式事件,返回处理后的事件数据;否则返回 None
"""
event = Event(etype, data, priority)
event_hash = self.__get_event_hash(event)
with self.__lock:
if event_hash in self.__processing_events:
logger.debug(f"Duplicate event ignored: {event}")
return None
self.__processing_events[event_hash] = event
if isinstance(etype, EventType):
self.__trigger_broadcast_event(event)
elif isinstance(etype, ChainEventType):
@@ -320,9 +335,14 @@ class EventManager(metaclass=Singleton):
"""
触发链式事件,按顺序调用订阅的处理器,并记录处理耗时
"""
logger.debug(f"Triggering synchronous chain event: {event}")
dispatch = self.__dispatch_chain_event(event)
return event if dispatch else None
try:
logger.debug(f"Triggering synchronous chain event: {event}")
dispatch = self.__dispatch_chain_event(event)
return event if dispatch else None
finally:
event_hash = self.__get_event_hash(event)
with self.__lock:
self.__processing_events.pop(event_hash, None)
def __trigger_broadcast_event(self, event: Event):
"""
@@ -363,6 +383,9 @@ class EventManager(metaclass=Singleton):
return
for handler_id, handler in handlers.items():
self.__executor.submit(self.__safe_invoke_handler, handler, event)
event_hash = self.__get_event_hash(event)
with self.__lock:
self.__processing_events.pop(event_hash, None)
def __safe_invoke_handler(self, handler: Callable, event: Event):
"""

View File

@@ -524,16 +524,7 @@ class MetaVideo(MetaBase):
"""
if not self.name:
return
source_res = re.search(r"(%s)" % self._source_re, token, re.IGNORECASE)
if source_res:
self._last_token_type = "source"
self._continue_flag = False
self._stop_name_flag = True
if not self._source:
self._source = source_res.group(1)
self._last_token = self._source.upper()
return
elif token.upper() == "DL" \
if token.upper() == "DL" \
and self._last_token_type == "source" \
and self._last_token == "WEB":
self._source = "WEB-DL"
@@ -553,7 +544,7 @@ class MetaVideo(MetaBase):
self._source = "WEB-DL"
self._continue_flag = False
return
# UHD REMUX组合
# UHD REMUX组合
if token.upper() == "REMUX" \
and self._source == "BluRay":
self._source = "BluRay REMUX"
@@ -562,7 +553,17 @@ class MetaVideo(MetaBase):
elif token.upper() == "BLURAY" \
and self._source == "UHD":
self._source = "UHD BluRay"
self._continue_flag = False
return
source_res = re.search(r"(%s)" % self._source_re, token, re.IGNORECASE)
if source_res:
self._last_token_type = "source"
self._continue_flag = False
self._stop_name_flag = True
if not self._source:
self._source = source_res.group(1)
self._last_token = self._source.upper()
return
effect_res = re.search(r"(%s)" % self._effect_re, token, re.IGNORECASE)
if effect_res:
self._last_token_type = "effect"

View File

@@ -46,7 +46,7 @@ class Site(Base):
# 流控间隔
limit_seconds = Column(Integer, default=0)
# 超时时间
timeout = Column(Integer, default=0)
timeout = Column(Integer, default=15)
# 是否启用
is_active = Column(Boolean(), default=True)
# 创建时间

View File

@@ -213,7 +213,7 @@ class Alist(StorageBase):
path=(Path(fileitem.path) / item["name"]).as_posix() + ("/" if item["is_dir"] else ""),
name=item["name"],
basename=Path(item["name"]).stem,
extension=Path(item["name"]).suffix if not item["is_dir"] else None,
extension=Path(item["name"]).suffix[1:] if not item["is_dir"] else None,
size=item["size"] if not item["is_dir"] else None,
modify_time=self.__parse_timestamp(item["modified"]),
thumbnail=item["thumb"],
@@ -351,7 +351,7 @@ class Alist(StorageBase):
path=path.as_posix() + ("/" if result["data"]["is_dir"] else ""),
name=result["data"]["name"],
basename=Path(result["data"]["name"]).stem,
extension=Path(result["data"]["name"]).suffix,
extension=Path(result["data"]["name"]).suffix[1:],
size=result["data"]["size"],
modify_time=self.__parse_timestamp(result["data"]["modified"]),
thumbnail=result["data"]["thumb"],
@@ -521,13 +521,15 @@ class Alist(StorageBase):
).get_res(download_url)
if not path:
path = settings.TEMP_PATH / fileitem.name
new_path = settings.TEMP_PATH / fileitem.name
else:
new_path = path / fileitem.name
with open(path, "wb") as f:
with open(new_path, "wb") as f:
f.write(resp.content)
if path.exists():
return path
if new_path.exists():
return new_path
return None
def upload(

View File

@@ -21,7 +21,8 @@ class U115Pan(StorageBase, metaclass=Singleton):
# 支持的整理方式
transtype = {
"move": "移动"
"move": "移动",
"copy": "复制"
}
client: P115Client = None
@@ -34,7 +35,8 @@ class U115Pan(StorageBase, metaclass=Singleton):
"""
try:
if not self.client or not self.client.cookies or force:
self.client = P115Client(self.__credential)
self.client = P115Client(self.__credential,
check_for_relogin=True, app="alipaymini", console_qrcode=False)
self.fs = P115FileSystem(self.client)
except Exception as err:
logger.error(f"115连接失败请重新扫码登录{str(err)}")

View File

@@ -19,10 +19,11 @@ from app.chain.transfer import TransferChain
from app.core.config import settings
from app.core.event import EventManager
from app.core.plugin import PluginManager
from app.db.systemconfig_oper import SystemConfigOper
from app.helper.sites import SitesHelper
from app.log import logger
from app.schemas import Notification, NotificationType
from app.schemas.types import EventType
from app.schemas.types import EventType, SystemConfigKey
from app.utils.singleton import Singleton
from app.utils.timer import TimerUtils
@@ -74,8 +75,12 @@ class Scheduler(metaclass=Singleton):
message="用户认证失败次数过多,将不再尝试认证!",
role="system")
return
logger.info("用户未认证,正在尝试重新认证...")
status, msg = SitesHelper().check_user()
logger.info("用户未认证,正在尝试认证...")
auth_conf = SystemConfigOper().get(SystemConfigKey.UserSiteAuthParams)
if auth_conf:
status, msg = SitesHelper().check_user(**auth_conf)
else:
status, msg = SitesHelper().check_user()
if status:
self._auth_count = 0
logger.info(f"{msg} 用户认证成功")
@@ -169,6 +174,9 @@ class Scheduler(metaclass=Singleton):
# 停止定时服务
self.stop()
# 用户认证立即执行一次
user_auth()
# 调试模式不启动定时服务
if settings.DEV:
return

View File

@@ -1,4 +1,4 @@
from typing import Optional, Any, Union
from typing import Optional, Any, Union, Dict
from pydantic import BaseModel
@@ -35,7 +35,7 @@ class Site(BaseModel):
# 备注
note: Optional[Any] = None
# 超时时间
timeout: Optional[int] = 0
timeout: Optional[int] = 15
# 流控单位周期
limit_interval: Optional[int] = None
# 流控次数
@@ -110,3 +110,8 @@ class SiteUserData(BaseModel):
updated_day: Optional[str] = None
# 更新时间
updated_time: Optional[str] = None
class SiteAuth(BaseModel):
site: Optional[str] = None
params: Optional[Dict[str, Union[int, str]]] = {}

View File

@@ -122,6 +122,8 @@ class SystemConfigKey(Enum):
DefaultMovieSubscribeConfig = "DefaultMovieSubscribeConfig"
# 默认电视剧订阅规则
DefaultTvSubscribeConfig = "DefaultTvSubscribeConfig"
# 用户站点认证参数
UserSiteAuthParams = "UserSiteAuthParams"
# 处理进度Key字典

View File

@@ -345,13 +345,13 @@ meta_cases = [{
"part": "",
"season": "",
"episode": "",
"restype": "BluRay Remux",
"restype": "BluRay REMUX",
"pix": "1080p",
"video_codec": "AVC",
"audio_codec": "LPCM 7³"
}
}, {
"title": "30.Rock.S02E01.1080p.BluRay.X264-BORDURE.mkv",
"title": "30.Rock.S02E01.1080p.UHD.BluRay.X264-BORDURE.mkv",
"subtitle": "",
"target": {
"type": "电视剧",
@@ -361,7 +361,7 @@ meta_cases = [{
"part": "",
"season": "S02",
"episode": "E01",
"restype": "BluRay",
"restype": "UHD BluRay",
"pix": "1080p",
"video_codec": "X264",
"audio_codec": ""
@@ -611,7 +611,7 @@ meta_cases = [{
"subtitle": "",
"target": {
"type": "电视剧",
"cn_name": "刑少女的生存之道",
"cn_name": "刑少女的生存之道",
"en_name": "",
"year": "",
"part": "",
@@ -665,7 +665,7 @@ meta_cases = [{
"part": "",
"season": "",
"episode": "",
"restype": "BluRay DoVi UHD",
"restype": "UHD BluRay DoVi",
"pix": "1080p",
"video_codec": "X265",
"audio_codec": "DD 7.1"

View File

@@ -1,2 +1,2 @@
APP_VERSION = 'v2.0.6'
FRONTEND_VERSION = 'v2.0.6'
APP_VERSION = 'v2.0.7'
FRONTEND_VERSION = 'v2.0.7'