From 0776a0b235e78c86380b1fb58ff3393f47046e4c Mon Sep 17 00:00:00 2001 From: jxxghp Date: Tue, 20 Jun 2023 13:09:11 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E6=94=AF=E6=8C=81Emby/Jellyfin=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E8=AE=A4=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/endpoints/douban.py | 20 +++++++++----------- app/api/endpoints/history.py | 7 +++---- app/api/endpoints/login.py | 12 +++++++++++- app/api/endpoints/media.py | 7 +++---- app/api/endpoints/plugin.py | 15 +++++++-------- app/api/endpoints/search.py | 7 +++---- app/api/endpoints/site.py | 17 ++++++++--------- app/api/endpoints/subscribe.py | 15 +++++++-------- app/api/endpoints/system.py | 6 ++++-- app/api/endpoints/tmdb.py | 9 ++++----- app/chain/user.py | 4 ++-- app/core/security.py | 16 ++++++++++++++++ app/db/userauth.py | 20 ++++---------------- app/modules/emby/__init__.py | 5 +++-- app/modules/emby/emby.py | 22 ++++++++++++++++------ app/modules/jellyfin/__init__.py | 5 +++-- app/modules/jellyfin/jellyfin.py | 17 ++++++++++++++--- 17 files changed, 117 insertions(+), 87 deletions(-) diff --git a/app/api/endpoints/douban.py b/app/api/endpoints/douban.py index 65ab4cc1..f4f32cc2 100644 --- a/app/api/endpoints/douban.py +++ b/app/api/endpoints/douban.py @@ -6,9 +6,7 @@ from fastapi import BackgroundTasks from app import schemas from app.chain.douban import DoubanChain from app.core.context import MediaInfo -from app.db.models.user import User -from app.db.userauth import get_current_active_superuser -from app.db.userauth import get_current_active_user +from app.core.security import verify_token from app.schemas import MediaType router = APIRouter() @@ -24,7 +22,7 @@ def start_douban_chain(): @router.get("/sync", summary="同步豆瓣想看", response_model=schemas.Response) async def sync_douban( background_tasks: BackgroundTasks, - _: User = Depends(get_current_active_superuser)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 同步豆瓣想看 """ @@ -34,7 +32,7 @@ async def sync_douban( @router.get("/id", summary="豆瓣ID识别", response_model=schemas.Context) async def recognize_doubanid(doubanid: str, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 根据豆瓣ID识别媒体信息 """ @@ -44,7 +42,7 @@ async def recognize_doubanid(doubanid: str, @router.get("/info", summary="查询豆瓣详情", response_model=schemas.MediaInfo) -async def douban_info(doubanid: str) -> Any: +async def douban_info(doubanid: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 根据豆瓣ID查询豆瓣媒体信息 """ @@ -60,7 +58,7 @@ async def douban_movies(sort: str = "R", tags: str = "", start: int = 0, count: int = 30, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 浏览豆瓣电影信息 """ @@ -76,7 +74,7 @@ async def douban_tvs(sort: str = "R", tags: str = "", start: int = 0, count: int = 30, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 浏览豆瓣剧集信息 """ @@ -90,7 +88,7 @@ async def douban_tvs(sort: str = "R", @router.get("/movie_top250", summary="豆瓣电影TOP250", response_model=List[schemas.MediaInfo]) async def movie_top250(page: int = 1, count: int = 30, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 浏览豆瓣剧集信息 """ @@ -101,7 +99,7 @@ async def movie_top250(page: int = 1, @router.get("/tv_weekly_chinese", summary="豆瓣国产剧集周榜", response_model=List[schemas.MediaInfo]) async def tv_weekly_chinese(page: int = 1, count: int = 30, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 中国每周剧集口碑榜 """ @@ -112,7 +110,7 @@ async def tv_weekly_chinese(page: int = 1, @router.get("/tv_weekly_global", summary="豆瓣全球剧集周榜", response_model=List[schemas.MediaInfo]) async def tv_weekly_global(page: int = 1, count: int = 30, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 全球每周剧集口碑榜 """ diff --git a/app/api/endpoints/history.py b/app/api/endpoints/history.py index 1b004734..55d0f414 100644 --- a/app/api/endpoints/history.py +++ b/app/api/endpoints/history.py @@ -4,11 +4,10 @@ from fastapi import APIRouter, Depends from sqlalchemy.orm import Session from app import schemas +from app.core.security import verify_token from app.db import get_db from app.db.models.downloadhistory import DownloadHistory from app.db.models.transferhistory import TransferHistory -from app.db.models.user import User -from app.db.userauth import get_current_active_user router = APIRouter() @@ -17,7 +16,7 @@ router = APIRouter() async def download_history(page: int = 1, count: int = 30, db: Session = Depends(get_db), - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 查询下载历史记录 """ @@ -29,7 +28,7 @@ async def transfer_history(title: str = None, page: int = 1, count: int = 30, db: Session = Depends(get_db), - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 查询转移历史记录 """ diff --git a/app/api/endpoints/login.py b/app/api/endpoints/login.py index cd5188f0..3168358e 100644 --- a/app/api/endpoints/login.py +++ b/app/api/endpoints/login.py @@ -6,10 +6,12 @@ from fastapi.security import OAuth2PasswordRequestForm from sqlalchemy.orm import Session from app import schemas +from app.chain.user import UserChain from app.core import security from app.core.config import settings from app.db import get_db from app.db.models.user import User +from app.log import logger router = APIRouter() @@ -21,13 +23,21 @@ async def login_access_token( """ 获取认证Token """ + # 检查数据库 user = User.authenticate( db=db, name=form_data.username, password=form_data.password ) if not user: - raise HTTPException(status_code=400, detail="用户名或密码不正确") + # 请求协助认证 + logger.warn("登录用户本地不匹配,尝试辅助认证 ...") + token = UserChain().user_authenticate(form_data.username, form_data.password) + if not token: + raise HTTPException(status_code=400, detail="用户名或密码不正确") + else: + logger.info(f"辅助认证成功,用户信息: {token}") + user = schemas.User(id=-1, name=form_data.username, is_active=True, is_superuser=False) elif not user.is_active: raise HTTPException(status_code=400, detail="用户未启用") access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) diff --git a/app/api/endpoints/media.py b/app/api/endpoints/media.py index afdd3e1f..81127700 100644 --- a/app/api/endpoints/media.py +++ b/app/api/endpoints/media.py @@ -4,8 +4,7 @@ from fastapi import APIRouter, Depends from app import schemas from app.chain.media import MediaChain -from app.db.models.user import User -from app.db.userauth import get_current_active_user +from app.core.security import verify_token router = APIRouter() @@ -13,7 +12,7 @@ router = APIRouter() @router.get("/recognize", summary="识别媒体信息", response_model=schemas.Context) async def recognize(title: str, subtitle: str = None, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 根据标题、副标题识别媒体信息 """ @@ -24,7 +23,7 @@ async def recognize(title: str, @router.get("/search", summary="搜索媒体信息", response_model=List[schemas.MediaInfo]) async def search_by_title(title: str, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 模糊搜索媒体信息列表 """ diff --git a/app/api/endpoints/plugin.py b/app/api/endpoints/plugin.py index da1e1412..718b6e3f 100644 --- a/app/api/endpoints/plugin.py +++ b/app/api/endpoints/plugin.py @@ -4,16 +4,15 @@ from fastapi import APIRouter, Depends from app import schemas from app.core.plugin import PluginManager -from app.db.models.user import User +from app.core.security import verify_token from app.db.systemconfig_oper import SystemConfigOper -from app.db.userauth import get_current_active_user from app.schemas.types import SystemConfigKey router = APIRouter() @router.get("/", summary="所有插件", response_model=List[schemas.Plugin]) -async def all_plugins(_: User = Depends(get_current_active_user)) -> Any: +async def all_plugins(_: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 查询所有插件清单 """ @@ -21,7 +20,7 @@ async def all_plugins(_: User = Depends(get_current_active_user)) -> Any: @router.get("/installed", summary="已安装插件", response_model=List[str]) -async def installed_plugins(_: User = Depends(get_current_active_user)) -> Any: +async def installed_plugins(_: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 查询用户已安装插件清单 """ @@ -29,7 +28,7 @@ async def installed_plugins(_: User = Depends(get_current_active_user)) -> Any: @router.get("/{plugin_id}", summary="获取插件配置") -async def plugin_config(plugin_id: str, _: User = Depends(get_current_active_user)) -> dict: +async def plugin_config(plugin_id: str, _: schemas.TokenPayload = Depends(verify_token)) -> dict: """ 根据插件ID获取插件配置信息 """ @@ -38,7 +37,7 @@ async def plugin_config(plugin_id: str, _: User = Depends(get_current_active_use @router.put("/{plugin_id}", summary="更新插件配置", response_model=schemas.Response) async def set_plugin_config(plugin_id: str, conf: dict, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 根据插件ID获取插件配置信息 """ @@ -48,7 +47,7 @@ async def set_plugin_config(plugin_id: str, conf: dict, @router.post("/{plugin_id}/install", summary="安装插件", response_model=schemas.Response) async def install_plugin(plugin_id: str, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 安装插件 """ @@ -64,7 +63,7 @@ async def install_plugin(plugin_id: str, @router.delete("/{plugin_id}", summary="卸载插件", response_model=schemas.Response) -async def uninstall_plugin(plugin_id: str, _: User = Depends(get_current_active_user)) -> Any: +async def uninstall_plugin(plugin_id: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 卸载插件 """ diff --git a/app/api/endpoints/search.py b/app/api/endpoints/search.py index 0a13c65c..2930ee64 100644 --- a/app/api/endpoints/search.py +++ b/app/api/endpoints/search.py @@ -4,8 +4,7 @@ from fastapi import APIRouter, Depends from app import schemas from app.chain.search import SearchChain -from app.db.models.user import User -from app.db.userauth import get_current_active_user +from app.core.security import verify_token from app.schemas.types import MediaType router = APIRouter() @@ -14,7 +13,7 @@ router = APIRouter() @router.get("/tmdbid", summary="精确搜索资源", response_model=List[schemas.Context]) async def search_by_tmdbid(tmdbid: int, mtype: str = None, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 根据TMDBID精确搜索站点资源 """ @@ -26,7 +25,7 @@ async def search_by_tmdbid(tmdbid: int, @router.get("/title", summary="模糊搜索资源", response_model=List[schemas.TorrentInfo]) async def search_by_title(title: str, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 根据名称模糊搜索站点资源 """ diff --git a/app/api/endpoints/site.py b/app/api/endpoints/site.py index 7e95f971..305612e9 100644 --- a/app/api/endpoints/site.py +++ b/app/api/endpoints/site.py @@ -6,18 +6,17 @@ from sqlalchemy.orm import Session from app import schemas from app.chain.cookiecloud import CookieCloudChain from app.chain.site import SiteChain +from app.core.security import verify_token from app.db import get_db from app.db.models.site import Site -from app.db.models.user import User from app.db.siteicon_oper import SiteIconOper -from app.db.userauth import get_current_active_user, get_current_active_superuser router = APIRouter() @router.get("/", summary="所有站点", response_model=List[schemas.Site]) async def read_sites(db: Session = Depends(get_db), - _: User = Depends(get_current_active_user)) -> List[dict]: + _: schemas.TokenPayload = Depends(verify_token)) -> List[dict]: """ 获取站点列表 """ @@ -29,7 +28,7 @@ async def update_site( *, db: Session = Depends(get_db), site_in: schemas.Site, - _: User = Depends(get_current_active_superuser), + _: schemas.TokenPayload = Depends(verify_token) ) -> Any: """ 更新站点信息 @@ -48,7 +47,7 @@ async def update_site( async def read_site( site_id: int, db: Session = Depends(get_db), - _: User = Depends(get_current_active_user), + _: schemas.TokenPayload = Depends(verify_token) ) -> Any: """ 获取站点信息 @@ -63,7 +62,7 @@ async def read_site( @router.get("/cookiecloud", summary="CookieCloud同步", response_model=schemas.Response) -async def cookie_cloud_sync(_: User = Depends(get_current_active_user)) -> Any: +async def cookie_cloud_sync(_: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 运行CookieCloud同步站点信息 """ @@ -79,7 +78,7 @@ async def update_cookie( username: str, password: str, db: Session = Depends(get_db), - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 使用用户密码更新站点Cookie """ @@ -101,7 +100,7 @@ async def update_cookie( @router.get("/test", summary="连接测试", response_model=schemas.Response) -async def test_site(domain: str, _: User = Depends(get_current_active_user)) -> Any: +async def test_site(domain: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 测试站点是否可用 """ @@ -110,7 +109,7 @@ async def test_site(domain: str, _: User = Depends(get_current_active_user)) -> @router.get("/icon", summary="站点图标", response_model=schemas.Response) -async def site_icon(domain: str, _: User = Depends(get_current_active_user)) -> Any: +async def site_icon(domain: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 获取站点图标:base64或者url """ diff --git a/app/api/endpoints/subscribe.py b/app/api/endpoints/subscribe.py index ee9214bc..0e3e8d14 100644 --- a/app/api/endpoints/subscribe.py +++ b/app/api/endpoints/subscribe.py @@ -6,10 +6,9 @@ from sqlalchemy.orm import Session from app import schemas from app.chain.subscribe import SubscribeChain from app.core.config import settings +from app.core.security import verify_token from app.db import get_db from app.db.models.subscribe import Subscribe -from app.db.models.user import User -from app.db.userauth import get_current_active_superuser from app.schemas.types import MediaType router = APIRouter() @@ -27,7 +26,7 @@ def start_subscribe_chain(title: str, year: str, @router.get("/", summary="所有订阅", response_model=List[schemas.Subscribe]) async def read_subscribes( db: Session = Depends(get_db), - _: User = Depends(get_current_active_superuser)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 查询所有订阅 """ @@ -38,7 +37,7 @@ async def read_subscribes( async def create_subscribe( *, subscribe_in: schemas.Subscribe, - _: User = Depends(get_current_active_superuser), + _: schemas.TokenPayload = Depends(verify_token) ) -> Any: """ 新增订阅 @@ -52,7 +51,7 @@ async def update_subscribe( *, db: Session = Depends(get_db), subscribe_in: schemas.Subscribe, - _: User = Depends(get_current_active_superuser), + _: schemas.TokenPayload = Depends(verify_token) ) -> Any: """ 更新订阅信息 @@ -72,7 +71,7 @@ async def delete_subscribe( *, db: Session = Depends(get_db), subscribe_in: schemas.Subscribe, - _: User = Depends(get_current_active_superuser), + _: schemas.TokenPayload = Depends(verify_token) ) -> Any: """ 删除订阅信息 @@ -136,7 +135,7 @@ async def seerr_subscribe(request: Request, background_tasks: BackgroundTasks, @router.get("/refresh", summary="刷新订阅", response_model=schemas.Response) async def refresh_subscribes( - _: User = Depends(get_current_active_superuser)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 刷新所有订阅 """ @@ -146,7 +145,7 @@ async def refresh_subscribes( @router.get("/search", summary="搜索订阅", response_model=schemas.Response) async def search_subscribes( - _: User = Depends(get_current_active_superuser)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 搜索所有订阅 """ diff --git a/app/api/endpoints/system.py b/app/api/endpoints/system.py index 5e45ea0a..c1682783 100644 --- a/app/api/endpoints/system.py +++ b/app/api/endpoints/system.py @@ -1,16 +1,18 @@ import asyncio import json -from fastapi import APIRouter +from fastapi import APIRouter, Depends from fastapi.responses import StreamingResponse +from app import schemas +from app.core.security import verify_token from app.helper.progress import ProgressHelper router = APIRouter() @router.get("/progress/{process_type}", summary="实时进度") -async def get_progress(process_type: str): +async def get_progress(process_type: str, _: schemas.TokenPayload = Depends(verify_token)): """ 实时获取处理进度,返回格式为SSE """ diff --git a/app/api/endpoints/tmdb.py b/app/api/endpoints/tmdb.py index bda89b38..72e69589 100644 --- a/app/api/endpoints/tmdb.py +++ b/app/api/endpoints/tmdb.py @@ -5,8 +5,7 @@ from fastapi import APIRouter, Depends from app import schemas from app.chain.tmdb import TmdbChain from app.core.context import MediaInfo -from app.db.models.user import User -from app.db.userauth import get_current_active_user +from app.core.security import verify_token from app.schemas.types import MediaType router = APIRouter() @@ -30,7 +29,7 @@ async def tmdb_movies(sort_by: str = "popularity.desc", with_genres: str = "", with_original_language: str = "", page: int = 1, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 浏览TMDB电影信息 """ @@ -49,7 +48,7 @@ async def tmdb_tvs(sort_by: str = "popularity.desc", with_genres: str = "", with_original_language: str = "", page: int = 1, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 浏览TMDB剧集信息 """ @@ -65,7 +64,7 @@ async def tmdb_tvs(sort_by: str = "popularity.desc", @router.get("/trending", summary="TMDB流行趋势", response_model=List[schemas.MediaInfo]) async def tmdb_trending(page: int = 1, - _: User = Depends(get_current_active_user)) -> Any: + _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 浏览TMDB剧集信息 """ diff --git a/app/chain/user.py b/app/chain/user.py index 1eb9a361..6ba155f5 100644 --- a/app/chain/user.py +++ b/app/chain/user.py @@ -5,11 +5,11 @@ from app.chain import ChainBase class UserChain(ChainBase): - def user_authenticate(self, name, password) -> Optional[bool]: + def user_authenticate(self, name, password) -> Optional[str]: """ 辅助完成用户认证 :param name: 用户名 :param password: 密码 - :return: bool + :return: token """ return self.run_module("user_authenticate", name=name, password=password) diff --git a/app/core/security.py b/app/core/security.py index 56a7f7a4..b1ff9183 100644 --- a/app/core/security.py +++ b/app/core/security.py @@ -8,8 +8,11 @@ from typing import Any, Union, Optional import jwt from Crypto.Cipher import AES from Crypto.Util.Padding import pad +from fastapi import HTTPException, status, Depends from fastapi.security import OAuth2PasswordBearer from passlib.context import CryptContext + +from app import schemas from app.core.config import settings from cryptography.fernet import Fernet @@ -36,6 +39,19 @@ def create_access_token( return encoded_jwt +def verify_token(token: str = Depends(reusable_oauth2)) -> schemas.TokenPayload: + try: + payload = jwt.decode( + token, settings.SECRET_KEY, algorithms=[ALGORITHM] + ) + return schemas.TokenPayload(**payload) + except (jwt.DecodeError, jwt.InvalidTokenError, jwt.ImmatureSignatureError): + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="token校验不通过", + ) + + def verify_password(plain_password: str, hashed_password: str) -> bool: return pwd_context.verify(plain_password, hashed_password) diff --git a/app/db/userauth.py b/app/db/userauth.py index fff4fd73..afd53fda 100644 --- a/app/db/userauth.py +++ b/app/db/userauth.py @@ -1,28 +1,16 @@ -import jwt -from fastapi import Depends, HTTPException, status +from fastapi import Depends, HTTPException from sqlalchemy.orm import Session from app import schemas -from app.core.config import settings -from app.core import security -from app.core.security import reusable_oauth2 +from app.core.security import verify_token from app.db import get_db from app.db.models.user import User def get_current_user( - db: Session = Depends(get_db), token: str = Depends(reusable_oauth2) + db: Session = Depends(get_db), + token_data: schemas.TokenPayload = Depends(verify_token) ) -> User: - try: - payload = jwt.decode( - token, settings.SECRET_KEY, algorithms=[security.ALGORITHM] - ) - token_data = schemas.TokenPayload(**payload) - except (jwt.DecodeError, jwt.InvalidTokenError, jwt.ImmatureSignatureError): - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail="token校验不通过", - ) user = User.get(db, rid=token_data.sub) if not user: raise HTTPException(status_code=404, detail="用户不存在") diff --git a/app/modules/emby/__init__.py b/app/modules/emby/__init__.py index 3acc2d23..41fc8660 100644 --- a/app/modules/emby/__init__.py +++ b/app/modules/emby/__init__.py @@ -21,13 +21,14 @@ class EmbyModule(_ModuleBase): def init_setting(self) -> Tuple[str, Union[str, bool]]: return "MEDIASERVER", "emby" - def user_authenticate(self, name, password) -> Optional[bool]: + def user_authenticate(self, name: str, password: str) -> Optional[str]: """ 使用Emby用户辅助完成用户认证 :param name: 用户名 :param password: 密码 - :return: bool + :return: token or None """ + # Emby认证 return self.emby.authenticate(name, password) def webhook_parser(self, body: Any, form: Any, args: Any) -> Optional[dict]: diff --git a/app/modules/emby/emby.py b/app/modules/emby/emby.py index 3a4388d6..3c66486e 100644 --- a/app/modules/emby/emby.py +++ b/app/modules/emby/emby.py @@ -6,10 +6,10 @@ from typing import List, Optional, Union, Dict from app.core.config import settings from app.log import logger from app.schemas import RefreshMediaItem +from app.schemas.types import MediaType from app.utils.http import RequestUtils from app.utils.singleton import Singleton from app.utils.string import StringUtils -from app.schemas.types import MediaType class Emby(metaclass=Singleton): @@ -87,27 +87,37 @@ class Emby(metaclass=Singleton): logger.error(f"连接Users出错:" + str(e)) return None - def authenticate(self, username: str, password: str) -> Optional[bool]: + def authenticate(self, username: str, password: str) -> Optional[str]: """ 用户认证 + :param username: 用户名 + :param password: 密码 + :return: 认证token """ if not self._host or not self._apikey: return None req_url = "%semby/Users/AuthenticateByName" % self._host try: - res = RequestUtils(content_type="application/json").post_res( + res = RequestUtils(headers={ + 'X-Emby-Authorization': f'MediaBrowser Client="MoviePilot", ' + f'Device="Axios", ' + f'DeviceId="1", ' + f'Version="10.8.0", ' + f'Token="{self._apikey}"', + 'Content-Type': 'application/json', + "Accept": "application/json" + }).post_res( url=req_url, data=json.dumps({ "Username": username, - "Pw": password, - "KeepMeLoggedIn": False + "Pw": password }) ) if res: auth_token = res.json().get("AccessToken") if auth_token: logger.info(f"用户 {username} Emby认证成功") - return True + return auth_token else: logger.error(f"Users/AuthenticateByName 未获取到返回数据") except Exception as e: diff --git a/app/modules/jellyfin/__init__.py b/app/modules/jellyfin/__init__.py index 51d2de41..c1883e99 100644 --- a/app/modules/jellyfin/__init__.py +++ b/app/modules/jellyfin/__init__.py @@ -22,13 +22,14 @@ class JellyfinModule(_ModuleBase): def init_setting(self) -> Tuple[str, Union[str, bool]]: return "MEDIASERVER", "jellyfin" - def user_authenticate(self, name, password) -> Optional[bool]: + def user_authenticate(self, name: str, password: str) -> Optional[str]: """ 使用Emby用户辅助完成用户认证 :param name: 用户名 :param password: 密码 - :return: bool + :return: Token or None """ + # Jellyfin认证 return self.jellyfin.authenticate(name, password) def webhook_parser(self, body: Any, form: Any, args: Any) -> Optional[dict]: diff --git a/app/modules/jellyfin/jellyfin.py b/app/modules/jellyfin/jellyfin.py index 27202e2b..670ad306 100644 --- a/app/modules/jellyfin/jellyfin.py +++ b/app/modules/jellyfin/jellyfin.py @@ -84,15 +84,26 @@ class Jellyfin(metaclass=Singleton): logger.error(f"连接Users出错:" + str(e)) return None - def authenticate(self, username: str, password: str) -> Optional[bool]: + def authenticate(self, username: str, password: str) -> Optional[str]: """ 用户认证 + :param username: 用户名 + :param password: 密码 + :return: 认证成功返回token,否则返回None """ if not self._host or not self._apikey: return None req_url = "%sUsers/authenticatebyname" % self._host try: - res = RequestUtils(content_type="application/json").post_res( + res = RequestUtils(headers={ + 'X-Emby-Authorization': f'MediaBrowser Client="MoviePilot", ' + f'Device="Axios", ' + f'DeviceId="1", ' + f'Version="10.8.0", ' + f'Token="{self._apikey}"', + 'Content-Type': 'application/json', + "Accept": "application/json" + }).post_res( url=req_url, data=json.dumps({ "Username": username, @@ -103,7 +114,7 @@ class Jellyfin(metaclass=Singleton): auth_token = res.json().get("AccessToken") if auth_token: logger.info(f"用户 {username} Jellyfin认证成功") - return True + return auth_token else: logger.error(f"Users/AuthenticateByName 未获取到返回数据") except Exception as e: