mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-04-05 11:47:50 +08:00
Merge pull request #2918 from InfinityPacer/feature/event
This commit is contained in:
@@ -202,9 +202,9 @@ class UserChain(ChainBase, metaclass=Singleton):
|
||||
# 触发认证通过的拦截事件
|
||||
intercept_event = self.eventmanager.send_event(
|
||||
etype=ChainEventType.AuthIntercept,
|
||||
data=AuthInterceptCredentials(username=username, channel=channel, service=service, token=token)
|
||||
data=AuthInterceptCredentials(username=username, channel=channel, service=service,
|
||||
token=token, status="completed")
|
||||
)
|
||||
|
||||
if intercept_event and intercept_event.event_data:
|
||||
intercept_data: AuthInterceptCredentials = intercept_event.event_data
|
||||
if intercept_data.cancel:
|
||||
|
||||
@@ -2,11 +2,12 @@ from typing import Any, Generator, List, Optional, Tuple, Union
|
||||
|
||||
from app import schemas
|
||||
from app.core.context import MediaInfo
|
||||
from app.core.event import eventmanager
|
||||
from app.log import logger
|
||||
from app.modules import _MediaServerBase, _ModuleBase
|
||||
from app.modules.emby.emby import Emby
|
||||
from app.schemas.event import AuthCredentials
|
||||
from app.schemas.types import MediaType, ModuleType
|
||||
from app.schemas.event import AuthCredentials, AuthInterceptCredentials
|
||||
from app.schemas.types import MediaType, ModuleType, ChainEventType
|
||||
|
||||
|
||||
class EmbyModule(_ModuleBase, _MediaServerBase[Emby]):
|
||||
@@ -75,6 +76,16 @@ class EmbyModule(_ModuleBase, _MediaServerBase[Emby]):
|
||||
if not credentials or credentials.grant_type != "password":
|
||||
return None
|
||||
for name, server in self.get_instances().items():
|
||||
# 触发认证拦截事件
|
||||
intercept_event = eventmanager.send_event(
|
||||
etype=ChainEventType.AuthIntercept,
|
||||
data=AuthInterceptCredentials(username=credentials.username, channel=self.get_name(),
|
||||
service=name, status="triggered")
|
||||
)
|
||||
if intercept_event and intercept_event.event_data:
|
||||
intercept_data: AuthInterceptCredentials = intercept_event.event_data
|
||||
if intercept_data.cancel:
|
||||
continue
|
||||
token = server.authenticate(credentials.username, credentials.password)
|
||||
if token:
|
||||
credentials.channel = self.get_name()
|
||||
|
||||
@@ -2,11 +2,12 @@ from typing import Any, Generator, List, Optional, Tuple, Union
|
||||
|
||||
from app import schemas
|
||||
from app.core.context import MediaInfo
|
||||
from app.core.event import eventmanager
|
||||
from app.log import logger
|
||||
from app.modules import _MediaServerBase, _ModuleBase
|
||||
from app.modules.jellyfin.jellyfin import Jellyfin
|
||||
from app.schemas.event import AuthCredentials
|
||||
from app.schemas.types import MediaType, ModuleType
|
||||
from app.schemas.event import AuthCredentials, AuthInterceptCredentials
|
||||
from app.schemas.types import MediaType, ModuleType, ChainEventType
|
||||
|
||||
|
||||
class JellyfinModule(_ModuleBase, _MediaServerBase[Jellyfin]):
|
||||
@@ -75,6 +76,16 @@ class JellyfinModule(_ModuleBase, _MediaServerBase[Jellyfin]):
|
||||
if not credentials or credentials.grant_type != "password":
|
||||
return None
|
||||
for name, server in self.get_instances().items():
|
||||
# 触发认证拦截事件
|
||||
intercept_event = eventmanager.send_event(
|
||||
etype=ChainEventType.AuthIntercept,
|
||||
data=AuthInterceptCredentials(username=credentials.username, channel=self.get_name(),
|
||||
service=name, status="triggered")
|
||||
)
|
||||
if intercept_event and intercept_event.event_data:
|
||||
intercept_data: AuthInterceptCredentials = intercept_event.event_data
|
||||
if intercept_data.cancel:
|
||||
continue
|
||||
token = server.authenticate(credentials.username, credentials.password)
|
||||
if token:
|
||||
credentials.channel = self.get_name()
|
||||
|
||||
@@ -2,10 +2,12 @@ from typing import Optional, Tuple, Union, Any, List, Generator
|
||||
|
||||
from app import schemas
|
||||
from app.core.context import MediaInfo
|
||||
from app.core.event import eventmanager
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase, _MediaServerBase
|
||||
from app.modules.plex.plex import Plex
|
||||
from app.schemas.types import MediaType, ModuleType
|
||||
from app.schemas.event import AuthCredentials, AuthInterceptCredentials
|
||||
from app.schemas.types import MediaType, ModuleType, ChainEventType
|
||||
|
||||
|
||||
class PlexModule(_ModuleBase, _MediaServerBase[Plex]):
|
||||
@@ -64,6 +66,37 @@ class PlexModule(_ModuleBase, _MediaServerBase[Plex]):
|
||||
logger.info(f"Plex {name} 服务器连接断开,尝试重连 ...")
|
||||
server.reconnect()
|
||||
|
||||
def user_authenticate(self, credentials: AuthCredentials) -> Optional[AuthCredentials]:
|
||||
"""
|
||||
使用Plex用户辅助完成用户认证
|
||||
:param credentials: 认证数据
|
||||
:return: 认证数据
|
||||
"""
|
||||
# Plex认证
|
||||
if not credentials or credentials.grant_type != "password":
|
||||
return None
|
||||
for name, server in self.get_instances().items():
|
||||
# 触发认证拦截事件
|
||||
intercept_event = eventmanager.send_event(
|
||||
etype=ChainEventType.AuthIntercept,
|
||||
data=AuthInterceptCredentials(username=credentials.username, channel=self.get_name(),
|
||||
service=name, status="triggered")
|
||||
)
|
||||
if intercept_event and intercept_event.event_data:
|
||||
intercept_data: AuthInterceptCredentials = intercept_event.event_data
|
||||
if intercept_data.cancel:
|
||||
continue
|
||||
auth_result = server.authenticate(credentials.username, credentials.password)
|
||||
if auth_result:
|
||||
token, username = auth_result
|
||||
credentials.channel = self.get_name()
|
||||
credentials.service = name
|
||||
credentials.token = token
|
||||
# Plex 传入可能为邮箱,这里调整为用户名返回
|
||||
credentials.username = username
|
||||
return credentials
|
||||
return None
|
||||
|
||||
def webhook_parser(self, body: Any, form: Any, args: Any) -> Optional[schemas.WebhookEventInfo]:
|
||||
"""
|
||||
解析Webhook报文体
|
||||
|
||||
@@ -5,6 +5,7 @@ from urllib.parse import quote_plus
|
||||
|
||||
from cachetools import TTLCache, cached
|
||||
from plexapi import media
|
||||
from plexapi.myplex import MyPlexAccount
|
||||
from plexapi.server import PlexServer
|
||||
from requests import Response, Session
|
||||
|
||||
@@ -61,6 +62,27 @@ class Plex:
|
||||
self._plex = None
|
||||
logger.error(f"Plex服务器连接失败:{str(e)}")
|
||||
|
||||
def authenticate(self, username: str, password: str) -> Optional[Tuple[str, str]]:
|
||||
"""
|
||||
用户认证
|
||||
:param username: 用户名
|
||||
:param password: 密码
|
||||
:return: 认证成功返回 (token, 用户名),否则返回 None
|
||||
"""
|
||||
if not username or not password:
|
||||
return None
|
||||
try:
|
||||
account = MyPlexAccount(username=username, password=password, remember=False)
|
||||
if account:
|
||||
plex = PlexServer(self._host, account.authToken)
|
||||
if not plex:
|
||||
return None
|
||||
return account.authToken, account.username
|
||||
except Exception as e:
|
||||
# 处理认证失败或网络错误等情况
|
||||
logger.error(f"Authentication failed: {e}")
|
||||
return None
|
||||
|
||||
@cached(cache=TTLCache(maxsize=100, ttl=86400))
|
||||
def __get_library_images(self, library_key: str, mtype: int) -> Optional[List[str]]:
|
||||
"""
|
||||
|
||||
@@ -74,15 +74,17 @@ class AuthInterceptCredentials(ChainEventData):
|
||||
channel (str): 认证渠道
|
||||
service (str): 服务名称
|
||||
token (str): 认证令牌
|
||||
status (str): 认证状态,"triggered" 和 "completed" 两个状态
|
||||
|
||||
# 输出参数
|
||||
source (str): 拦截源,默认值为 "未知拦截源"
|
||||
cancel (bool): 是否取消认证,默认值为 False
|
||||
"""
|
||||
# 输入参数
|
||||
username: str = Field(..., description="用户名")
|
||||
username: Optional[str] = Field(..., description="用户名")
|
||||
channel: str = Field(..., description="认证渠道")
|
||||
service: str = Field(..., description="服务名称")
|
||||
status: str = Field(..., description="认证状态, 包含 'triggered' 表示认证触发,'completed' 表示认证成功")
|
||||
token: Optional[str] = Field(None, description="认证令牌")
|
||||
|
||||
# 输出参数
|
||||
|
||||
Reference in New Issue
Block a user