mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-04 23:17:20 +08:00
feat: refactor ZSpace media server request handling and improve authorization headers
This commit is contained in:
@@ -45,6 +45,46 @@ class ZSpace:
|
|||||||
if not self.reconnect():
|
if not self.reconnect():
|
||||||
logger.error(f"请检查极影视服务端地址 {host}")
|
logger.error(f"请检查极影视服务端地址 {host}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __get_client_authorization() -> str:
|
||||||
|
"""
|
||||||
|
构造客户端标识头。
|
||||||
|
|
||||||
|
极影视兼容 Emby 登录接口时,需要携带客户端、设备和版本信息,
|
||||||
|
这里统一复用一份固定头,避免各接口散落重复字符串。
|
||||||
|
"""
|
||||||
|
return 'MediaBrowser Client="MoviePilot", Device="requests", DeviceId="1", Version="1.0.0"'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __get_user_authorization(user_id: Union[str, int]) -> str:
|
||||||
|
"""
|
||||||
|
构造用户态授权头。
|
||||||
|
|
||||||
|
保留这个组装函数,便于后续需要显式传递用户态 Authorization 头时复用。
|
||||||
|
当前极影视兼容 Emby 实测主要依赖 `X-Emby-Token`,这里不默认附带该头,
|
||||||
|
避免部分实现把非 GUID 的用户 ID 校验为非法格式。
|
||||||
|
"""
|
||||||
|
return f'Emby UserId="{user_id}", Client="MoviePilot", Device="requests", DeviceId="1", Version="1.0.0"'
|
||||||
|
|
||||||
|
def __request_utils(self,
|
||||||
|
timeout: Optional[int] = None,
|
||||||
|
include_token: bool = True,
|
||||||
|
headers: Optional[dict] = None) -> RequestUtils:
|
||||||
|
"""
|
||||||
|
统一构造极影视请求客户端。
|
||||||
|
|
||||||
|
极影视这里使用用户名密码登录获取 AccessToken,后续 API 请求优先通过
|
||||||
|
`X-Emby-Token` 请求头传递 token,而不是把登录 token 当成静态 API Key。
|
||||||
|
"""
|
||||||
|
request_headers = {
|
||||||
|
"X-Emby-Authorization": self.__get_client_authorization()
|
||||||
|
}
|
||||||
|
if include_token and self._apikey:
|
||||||
|
request_headers["X-Emby-Token"] = self._apikey
|
||||||
|
if headers:
|
||||||
|
request_headers.update(headers)
|
||||||
|
return RequestUtils(headers=request_headers, timeout=timeout)
|
||||||
|
|
||||||
def is_inactive(self) -> bool:
|
def is_inactive(self) -> bool:
|
||||||
"""
|
"""
|
||||||
判断是否需要重连
|
判断是否需要重连
|
||||||
@@ -88,11 +128,8 @@ class ZSpace:
|
|||||||
if not self._host or not self._apikey:
|
if not self._host or not self._apikey:
|
||||||
return []
|
return []
|
||||||
url = f"{self._host}emby/Library/SelectableMediaFolders"
|
url = f"{self._host}emby/Library/SelectableMediaFolders"
|
||||||
params = {
|
|
||||||
'api_key': self._apikey
|
|
||||||
}
|
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().get_res(url, params)
|
res = self.__request_utils().get_res(url)
|
||||||
if res:
|
if res:
|
||||||
return res.json()
|
return res.json()
|
||||||
else:
|
else:
|
||||||
@@ -109,11 +146,8 @@ class ZSpace:
|
|||||||
if not self._host or not self._apikey:
|
if not self._host or not self._apikey:
|
||||||
return []
|
return []
|
||||||
url = f"{self._host}emby/Library/VirtualFolders/Query"
|
url = f"{self._host}emby/Library/VirtualFolders/Query"
|
||||||
params = {
|
|
||||||
'api_key': self._apikey
|
|
||||||
}
|
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().get_res(url, params)
|
res = self.__request_utils().get_res(url)
|
||||||
if res:
|
if res:
|
||||||
library_items = res.json().get("Items")
|
library_items = res.json().get("Items")
|
||||||
libraries = []
|
libraries = []
|
||||||
@@ -155,9 +189,8 @@ class ZSpace:
|
|||||||
if not user:
|
if not user:
|
||||||
return []
|
return []
|
||||||
url = f"{self._host}emby/Users/{user}/Views"
|
url = f"{self._host}emby/Users/{user}/Views"
|
||||||
params = {"api_key": self._apikey}
|
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().get_res(url, params)
|
res = self.__request_utils().get_res(url)
|
||||||
if res:
|
if res:
|
||||||
return res.json().get("Items")
|
return res.json().get("Items")
|
||||||
else:
|
else:
|
||||||
@@ -207,12 +240,9 @@ class ZSpace:
|
|||||||
"""
|
"""
|
||||||
if not self._host or not self._apikey:
|
if not self._host or not self._apikey:
|
||||||
return None
|
return None
|
||||||
url = f"{self._host}Users"
|
url = f"{self._host}emby/Users"
|
||||||
params = {
|
|
||||||
"api_key": self._apikey
|
|
||||||
}
|
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().get_res(url, params)
|
res = self.__request_utils().get_res(url)
|
||||||
if res:
|
if res:
|
||||||
users = res.json()
|
users = res.json()
|
||||||
if isinstance(users, list):
|
if isinstance(users, list):
|
||||||
@@ -252,12 +282,9 @@ class ZSpace:
|
|||||||
"""
|
"""
|
||||||
if not self._host or not self._apikey:
|
if not self._host or not self._apikey:
|
||||||
return None
|
return None
|
||||||
url = f"{self._host}System/Info"
|
url = f"{self._host}emby/System/Info"
|
||||||
params = {
|
|
||||||
'api_key': self._apikey
|
|
||||||
}
|
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().get_res(url, params)
|
res = self.__request_utils().get_res(url)
|
||||||
if res:
|
if res:
|
||||||
return res.json().get("Id")
|
return res.json().get("Id")
|
||||||
else:
|
else:
|
||||||
@@ -273,11 +300,8 @@ class ZSpace:
|
|||||||
if not self._host or not self._apikey:
|
if not self._host or not self._apikey:
|
||||||
return 0
|
return 0
|
||||||
url = f"{self._host}emby/Users/Query"
|
url = f"{self._host}emby/Users/Query"
|
||||||
params = {
|
|
||||||
'api_key': self._apikey
|
|
||||||
}
|
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().get_res(url, params)
|
res = self.__request_utils().get_res(url)
|
||||||
if res:
|
if res:
|
||||||
count = res.json().get("TotalRecordCount")
|
count = res.json().get("TotalRecordCount")
|
||||||
if count:
|
if count:
|
||||||
@@ -296,11 +320,8 @@ class ZSpace:
|
|||||||
if not self._host or not self._apikey:
|
if not self._host or not self._apikey:
|
||||||
return schemas.Statistic()
|
return schemas.Statistic()
|
||||||
url = f"{self._host}emby/Items/Counts"
|
url = f"{self._host}emby/Items/Counts"
|
||||||
params = {
|
|
||||||
'api_key': self._apikey
|
|
||||||
}
|
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().get_res(url, params)
|
res = self.__request_utils().get_res(url)
|
||||||
if res:
|
if res:
|
||||||
result = res.json()
|
result = res.json()
|
||||||
return schemas.Statistic(
|
return schemas.Statistic(
|
||||||
@@ -332,11 +353,10 @@ class ZSpace:
|
|||||||
"Recursive": "true",
|
"Recursive": "true",
|
||||||
"SearchTerm": name,
|
"SearchTerm": name,
|
||||||
"Limit": 10,
|
"Limit": 10,
|
||||||
"IncludeSearchTypes": "false",
|
"IncludeSearchTypes": "false"
|
||||||
"api_key": self._apikey
|
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().get_res(url, params)
|
res = self.__request_utils().get_res(url, params=params)
|
||||||
if res:
|
if res:
|
||||||
res_items = res.json().get("Items")
|
res_items = res.json().get("Items")
|
||||||
if res_items:
|
if res_items:
|
||||||
@@ -370,11 +390,10 @@ class ZSpace:
|
|||||||
"Recursive": "true",
|
"Recursive": "true",
|
||||||
"SearchTerm": title,
|
"SearchTerm": title,
|
||||||
"Limit": 10,
|
"Limit": 10,
|
||||||
"IncludeSearchTypes": "false",
|
"IncludeSearchTypes": "false"
|
||||||
"api_key": self._apikey
|
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().get_res(url, params)
|
res = self.__request_utils().get_res(url, params=params)
|
||||||
if res:
|
if res:
|
||||||
res_items = res.json().get("Items")
|
res_items = res.json().get("Items")
|
||||||
if res_items:
|
if res_items:
|
||||||
@@ -439,10 +458,9 @@ class ZSpace:
|
|||||||
url = f"{self._host}emby/Shows/{item_id}/Episodes"
|
url = f"{self._host}emby/Shows/{item_id}/Episodes"
|
||||||
params = {
|
params = {
|
||||||
"Season": season,
|
"Season": season,
|
||||||
"IsMissing": "false",
|
"IsMissing": "false"
|
||||||
"api_key": self._apikey
|
|
||||||
}
|
}
|
||||||
res_json = RequestUtils().get_res(url, params)
|
res_json = self.__request_utils().get_res(url, params=params)
|
||||||
if res_json:
|
if res_json:
|
||||||
tv_item = res_json.json()
|
tv_item = res_json.json()
|
||||||
res_items = tv_item.get("Items")
|
res_items = tv_item.get("Items")
|
||||||
@@ -475,11 +493,8 @@ class ZSpace:
|
|||||||
if not self._host or not self._apikey:
|
if not self._host or not self._apikey:
|
||||||
return None
|
return None
|
||||||
url = f"{self._host}emby/Items/{item_id}/RemoteImages"
|
url = f"{self._host}emby/Items/{item_id}/RemoteImages"
|
||||||
params = {
|
|
||||||
"api_key": self._apikey
|
|
||||||
}
|
|
||||||
try:
|
try:
|
||||||
res = RequestUtils(timeout=10).get_res(url, params)
|
res = self.__request_utils(timeout=10).get_res(url)
|
||||||
if res:
|
if res:
|
||||||
images = res.json().get("Images")
|
images = res.json().get("Images")
|
||||||
if images:
|
if images:
|
||||||
@@ -503,9 +518,9 @@ class ZSpace:
|
|||||||
logger.error("极影视外网播放地址未能获取或为空")
|
logger.error("极影视外网播放地址未能获取或为空")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
url = f"{self._playhost}Items/{item_id}/Images/{image_type}"
|
url = f"{self._playhost}emby/Items/{item_id}/Images/{image_type}"
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().get_res(url)
|
res = self.__request_utils().get_res(url)
|
||||||
if res and res.status_code != 404:
|
if res and res.status_code != 404:
|
||||||
logger.info(f"影片图片链接:{res.url}")
|
logger.info(f"影片图片链接:{res.url}")
|
||||||
return res.url
|
return res.url
|
||||||
@@ -524,11 +539,10 @@ class ZSpace:
|
|||||||
return False
|
return False
|
||||||
url = f"{self._host}emby/Items/{item_id}/Refresh"
|
url = f"{self._host}emby/Items/{item_id}/Refresh"
|
||||||
params = {
|
params = {
|
||||||
"Recursive": "true",
|
"Recursive": "true"
|
||||||
"api_key": self._apikey
|
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().post_res(url, params=params)
|
res = self.__request_utils().post_res(url, params=params)
|
||||||
if res:
|
if res:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@@ -545,11 +559,8 @@ class ZSpace:
|
|||||||
if not self._host or not self._apikey:
|
if not self._host or not self._apikey:
|
||||||
return False
|
return False
|
||||||
url = f"{self._host}emby/Library/Refresh"
|
url = f"{self._host}emby/Library/Refresh"
|
||||||
params = {
|
|
||||||
"api_key": self._apikey
|
|
||||||
}
|
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().post_res(url, params=params)
|
res = self.__request_utils().post_res(url)
|
||||||
if res:
|
if res:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@@ -662,11 +673,8 @@ class ZSpace:
|
|||||||
if not self._host or not self._apikey or not self.user:
|
if not self._host or not self._apikey or not self.user:
|
||||||
return None
|
return None
|
||||||
url = f"{self._host}emby/Users/{self.user}/Items/{itemid}"
|
url = f"{self._host}emby/Users/{self.user}/Items/{itemid}"
|
||||||
params = {
|
|
||||||
"api_key": self._apikey
|
|
||||||
}
|
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().get_res(url, params)
|
res = self.__request_utils().get_res(url)
|
||||||
if res and res.status_code == 200:
|
if res and res.status_code == 200:
|
||||||
iteminfo = self.__format_item_info(res.json())
|
iteminfo = self.__format_item_info(res.json())
|
||||||
return iteminfo
|
return iteminfo
|
||||||
@@ -690,7 +698,6 @@ class ZSpace:
|
|||||||
url = f"{self._host}emby/Users/{self.user}/Items"
|
url = f"{self._host}emby/Users/{self.user}/Items"
|
||||||
params = {
|
params = {
|
||||||
"ParentId": parent,
|
"ParentId": parent,
|
||||||
"api_key": self._apikey,
|
|
||||||
"Fields": "ProviderIds,OriginalTitle,ProductionYear,Path,UserDataPlayCount,UserDataLastPlayedDate,ParentId"
|
"Fields": "ProviderIds,OriginalTitle,ProductionYear,Path,UserDataPlayCount,UserDataLastPlayedDate,ParentId"
|
||||||
}
|
}
|
||||||
if limit is not None and limit != -1:
|
if limit is not None and limit != -1:
|
||||||
@@ -699,7 +706,7 @@ class ZSpace:
|
|||||||
"Limit": limit
|
"Limit": limit
|
||||||
})
|
})
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().get_res(url, params)
|
res = self.__request_utils().get_res(url, params=params)
|
||||||
if not res or res.status_code != 200:
|
if not res or res.status_code != 200:
|
||||||
return None
|
return None
|
||||||
items = res.json().get("Items") or []
|
items = res.json().get("Items") or []
|
||||||
@@ -807,7 +814,8 @@ class ZSpace:
|
|||||||
|
|
||||||
def get_data(self, url: str) -> Optional[Response]:
|
def get_data(self, url: str) -> Optional[Response]:
|
||||||
"""
|
"""
|
||||||
自定义URL从媒体服务器获取数据,其中[HOST]、[APIKEY]、[USER]会被替换成实际的值
|
自定义URL从媒体服务器获取数据,其中[HOST]、[APIKEY]、[USER]会被替换成实际的值。
|
||||||
|
极影视这里的 [APIKEY] 实际替换为登录返回的 AccessToken,以兼容现有占位符。
|
||||||
:param url: 请求地址
|
:param url: 请求地址
|
||||||
"""
|
"""
|
||||||
if not self._host or not self._apikey:
|
if not self._host or not self._apikey:
|
||||||
@@ -816,14 +824,15 @@ class ZSpace:
|
|||||||
.replace("[APIKEY]", self._apikey or '') \
|
.replace("[APIKEY]", self._apikey or '') \
|
||||||
.replace("[USER]", self.user or '')
|
.replace("[USER]", self.user or '')
|
||||||
try:
|
try:
|
||||||
return RequestUtils(content_type="application/json").get_res(url=url)
|
return self.__request_utils(headers={"Content-Type": "application/json"}).get_res(url=url)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"连接极影视出错:{e}")
|
logger.error(f"连接极影视出错:{e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def post_data(self, url: str, data: Optional[str] = None, headers: dict = None) -> Optional[Response]:
|
def post_data(self, url: str, data: Optional[str] = None, headers: dict = None) -> Optional[Response]:
|
||||||
"""
|
"""
|
||||||
自定义URL从媒体服务器获取数据,其中[HOST]、[APIKEY]、[USER]会被替换成实际的值
|
自定义URL从媒体服务器获取数据,其中[HOST]、[APIKEY]、[USER]会被替换成实际的值。
|
||||||
|
极影视这里的 [APIKEY] 实际替换为登录返回的 AccessToken,以兼容现有占位符。
|
||||||
:param url: 请求地址
|
:param url: 请求地址
|
||||||
:param data: 请求数据
|
:param data: 请求数据
|
||||||
:param headers: 请求头
|
:param headers: 请求头
|
||||||
@@ -834,9 +843,7 @@ class ZSpace:
|
|||||||
.replace("[APIKEY]", self._apikey or '') \
|
.replace("[APIKEY]", self._apikey or '') \
|
||||||
.replace("[USER]", self.user or '')
|
.replace("[USER]", self.user or '')
|
||||||
try:
|
try:
|
||||||
return RequestUtils(
|
return self.__request_utils(headers=headers).post_res(url=url, data=data)
|
||||||
headers=headers,
|
|
||||||
).post_res(url=url, data=data)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"连接极影视出错:{e}")
|
logger.error(f"连接极影视出错:{e}")
|
||||||
return None
|
return None
|
||||||
@@ -864,7 +871,7 @@ class ZSpace:
|
|||||||
host_url = self._playhost or self._host
|
host_url = self._playhost or self._host
|
||||||
else:
|
else:
|
||||||
host_url = self._host
|
host_url = self._host
|
||||||
return f"{host_url}Items/{item_id}/" \
|
return f"{host_url}emby/Items/{item_id}/" \
|
||||||
f"Images/Backdrop?tag={image_tag}&api_key={self._apikey}"
|
f"Images/Backdrop?tag={image_tag}&api_key={self._apikey}"
|
||||||
|
|
||||||
def __get_local_image_by_id(self, item_id: str) -> str:
|
def __get_local_image_by_id(self, item_id: str) -> str:
|
||||||
@@ -874,7 +881,7 @@ class ZSpace:
|
|||||||
"""
|
"""
|
||||||
if not self._host or not self._apikey:
|
if not self._host or not self._apikey:
|
||||||
return ""
|
return ""
|
||||||
return f"{self._host}Items/{item_id}/Images/Primary"
|
return f"{self._host}emby/Items/{item_id}/Images/Primary?api_key={self._apikey}"
|
||||||
|
|
||||||
def get_resume(self, num: Optional[int] = 12, username: Optional[str] = None) -> Optional[
|
def get_resume(self, num: Optional[int] = 12, username: Optional[str] = None) -> Optional[
|
||||||
List[schemas.MediaServerPlayItem]]:
|
List[schemas.MediaServerPlayItem]]:
|
||||||
@@ -889,15 +896,14 @@ class ZSpace:
|
|||||||
user = self.user
|
user = self.user
|
||||||
if not user:
|
if not user:
|
||||||
return []
|
return []
|
||||||
url = f"{self._host}Users/{user}/Items/Resume"
|
url = f"{self._host}emby/Users/{user}/Items/Resume"
|
||||||
params = {
|
params = {
|
||||||
"Limit": 100,
|
"Limit": 100,
|
||||||
"MediaTypes": "Video",
|
"MediaTypes": "Video",
|
||||||
"Fields": "ProductionYear,Path",
|
"Fields": "ProductionYear,Path"
|
||||||
"api_key": self._apikey,
|
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().get_res(url, params)
|
res = self.__request_utils().get_res(url, params=params)
|
||||||
if res:
|
if res:
|
||||||
result = res.json().get("Items") or []
|
result = res.json().get("Items") or []
|
||||||
ret_resume = []
|
ret_resume = []
|
||||||
@@ -960,15 +966,14 @@ class ZSpace:
|
|||||||
user = self.user
|
user = self.user
|
||||||
if not user:
|
if not user:
|
||||||
return []
|
return []
|
||||||
url = f"{self._host}Users/{user}/Items/Latest"
|
url = f"{self._host}emby/Users/{user}/Items/Latest"
|
||||||
params = {
|
params = {
|
||||||
"Limit": 100,
|
"Limit": 100,
|
||||||
"MediaTypes": "Video",
|
"MediaTypes": "Video",
|
||||||
"Fields": "ProductionYear,Path,BackdropImageTags",
|
"Fields": "ProductionYear,Path,BackdropImageTags"
|
||||||
"api_key": self._apikey
|
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
res = RequestUtils().get_res(url, params)
|
res = self.__request_utils().get_res(url, params=params)
|
||||||
if res:
|
if res:
|
||||||
result = res.json() or []
|
result = res.json() or []
|
||||||
ret_latest = []
|
ret_latest = []
|
||||||
@@ -1023,14 +1028,13 @@ class ZSpace:
|
|||||||
return None, None
|
return None, None
|
||||||
url = f"{self._host}emby/Users/AuthenticateByName"
|
url = f"{self._host}emby/Users/AuthenticateByName"
|
||||||
try:
|
try:
|
||||||
res = RequestUtils(headers={
|
res = self.__request_utils(
|
||||||
'X-Emby-Authorization': 'MediaBrowser Client="MoviePilot", '
|
include_token=False,
|
||||||
'Device="requests", '
|
headers={
|
||||||
'DeviceId="1", '
|
'Content-Type': 'application/json',
|
||||||
'Version="1.0.0"',
|
'Accept': 'application/json'
|
||||||
'Content-Type': 'application/json',
|
}
|
||||||
'Accept': 'application/json'
|
).post_res(
|
||||||
}).post_res(
|
|
||||||
url=url,
|
url=url,
|
||||||
data=json.dumps({
|
data=json.dumps({
|
||||||
"Username": username,
|
"Username": username,
|
||||||
@@ -1055,20 +1059,19 @@ class ZSpace:
|
|||||||
"""
|
"""
|
||||||
if not self._host or not self._apikey:
|
if not self._host or not self._apikey:
|
||||||
return None
|
return None
|
||||||
url = f"{self._host}emby/Users/Me"
|
urls = []
|
||||||
params = {
|
if self.user:
|
||||||
"api_key": self._apikey
|
urls.append(f"{self._host}emby/Users/{self.user}")
|
||||||
}
|
urls.append(f"{self._host}emby/Users/Me")
|
||||||
try:
|
for url in urls:
|
||||||
res = RequestUtils().get_res(url, params)
|
try:
|
||||||
if res:
|
res = self.__request_utils().get_res(url)
|
||||||
result = res.json()
|
if res:
|
||||||
if isinstance(result, dict):
|
result = res.json()
|
||||||
return result
|
if isinstance(result, dict):
|
||||||
else:
|
return result
|
||||||
logger.error("Users/Me 未获取到返回数据")
|
except Exception as e:
|
||||||
except Exception as e:
|
logger.error(f"连接 {url} 出错:{e}")
|
||||||
logger.error(f"连接Users/Me出错:{e}")
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __get_current_user_id(self) -> Optional[str]:
|
def __get_current_user_id(self) -> Optional[str]:
|
||||||
|
|||||||
@@ -14,17 +14,17 @@ class _FakeResponse:
|
|||||||
|
|
||||||
class ZSpaceMediaServerTest(unittest.TestCase):
|
class ZSpaceMediaServerTest(unittest.TestCase):
|
||||||
def test_reconnect_uses_username_password_login(self):
|
def test_reconnect_uses_username_password_login(self):
|
||||||
request_utils = Mock()
|
with patch("app.modules.zspace.zspace.RequestUtils") as request_utils_cls:
|
||||||
request_utils.post_res.return_value = _FakeResponse({
|
request_utils = request_utils_cls.return_value
|
||||||
"AccessToken": "zspace-token",
|
request_utils.post_res.return_value = _FakeResponse({
|
||||||
"User": {"Id": "user-id"},
|
"AccessToken": "zspace-token",
|
||||||
})
|
"User": {"Id": "user-id"},
|
||||||
request_utils.get_res.side_effect = [
|
})
|
||||||
_FakeResponse([]),
|
request_utils.get_res.side_effect = [
|
||||||
_FakeResponse({"Id": "server-id"}),
|
_FakeResponse([]),
|
||||||
]
|
_FakeResponse({"Id": "server-id"}),
|
||||||
|
]
|
||||||
|
|
||||||
with patch("app.modules.zspace.zspace.RequestUtils", return_value=request_utils):
|
|
||||||
client = ZSpace(
|
client = ZSpace(
|
||||||
host="http://zspace.local",
|
host="http://zspace.local",
|
||||||
username="admin",
|
username="admin",
|
||||||
@@ -34,6 +34,21 @@ class ZSpaceMediaServerTest(unittest.TestCase):
|
|||||||
self.assertEqual(client._apikey, "zspace-token")
|
self.assertEqual(client._apikey, "zspace-token")
|
||||||
self.assertEqual(client.user, "user-id")
|
self.assertEqual(client.user, "user-id")
|
||||||
self.assertEqual(client.serverid, "server-id")
|
self.assertEqual(client.serverid, "server-id")
|
||||||
|
self.assertEqual(
|
||||||
|
request_utils_cls.call_args_list[0].kwargs["headers"]["X-Emby-Authorization"],
|
||||||
|
'MediaBrowser Client="MoviePilot", Device="requests", DeviceId="1", Version="1.0.0"',
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
request_utils_cls.call_args_list[1].kwargs["headers"]["X-Emby-Token"],
|
||||||
|
"zspace-token",
|
||||||
|
)
|
||||||
|
self.assertIsNone(
|
||||||
|
request_utils_cls.call_args_list[1].kwargs["headers"].get("Authorization")
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
request_utils.get_res.call_args_list[1].args[0],
|
||||||
|
"http://zspace.local/emby/System/Info",
|
||||||
|
)
|
||||||
|
|
||||||
def test_get_user_falls_back_to_current_login_user(self):
|
def test_get_user_falls_back_to_current_login_user(self):
|
||||||
client = ZSpace.__new__(ZSpace)
|
client = ZSpace.__new__(ZSpace)
|
||||||
@@ -48,6 +63,14 @@ class ZSpaceMediaServerTest(unittest.TestCase):
|
|||||||
user_id = client.get_user("admin")
|
user_id = client.get_user("admin")
|
||||||
|
|
||||||
self.assertEqual(user_id, "current-user-id")
|
self.assertEqual(user_id, "current-user-id")
|
||||||
|
self.assertEqual(
|
||||||
|
request_utils_cls.return_value.get_res.call_args.args[0],
|
||||||
|
"http://zspace.local/emby/Users",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
request_utils_cls.call_args.kwargs["headers"]["X-Emby-Token"],
|
||||||
|
"zspace-token",
|
||||||
|
)
|
||||||
|
|
||||||
def test_authenticate_does_not_require_existing_api_key(self):
|
def test_authenticate_does_not_require_existing_api_key(self):
|
||||||
with patch("app.modules.zspace.zspace.RequestUtils") as request_utils_cls:
|
with patch("app.modules.zspace.zspace.RequestUtils") as request_utils_cls:
|
||||||
@@ -68,6 +91,57 @@ class ZSpaceMediaServerTest(unittest.TestCase):
|
|||||||
headers.get("X-Emby-Authorization"),
|
headers.get("X-Emby-Authorization"),
|
||||||
'MediaBrowser Client="MoviePilot", Device="requests", DeviceId="1", Version="1.0.0"',
|
'MediaBrowser Client="MoviePilot", Device="requests", DeviceId="1", Version="1.0.0"',
|
||||||
)
|
)
|
||||||
|
self.assertIsNone(headers.get("X-Emby-Token"))
|
||||||
|
|
||||||
|
def test_get_resume_uses_emby_path_and_login_token_headers(self):
|
||||||
|
client = ZSpace.__new__(ZSpace)
|
||||||
|
client._host = "http://zspace.local/"
|
||||||
|
client._apikey = "zspace-token"
|
||||||
|
client.user = "user-id"
|
||||||
|
client._sync_libraries = []
|
||||||
|
client.get_user_library_folders = Mock(return_value=[])
|
||||||
|
|
||||||
|
with patch("app.modules.zspace.zspace.RequestUtils") as request_utils_cls:
|
||||||
|
request_utils_cls.return_value.get_res.return_value = _FakeResponse({"Items": []})
|
||||||
|
|
||||||
|
items = client.get_resume()
|
||||||
|
|
||||||
|
self.assertEqual(items, [])
|
||||||
|
self.assertEqual(
|
||||||
|
request_utils_cls.return_value.get_res.call_args.args[0],
|
||||||
|
"http://zspace.local/emby/Users/user-id/Items/Resume",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
request_utils_cls.return_value.get_res.call_args.kwargs["params"],
|
||||||
|
{
|
||||||
|
"Limit": 100,
|
||||||
|
"MediaTypes": "Video",
|
||||||
|
"Fields": "ProductionYear,Path",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
request_utils_cls.call_args.kwargs["headers"]["X-Emby-Token"],
|
||||||
|
"zspace-token",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_image_urls_use_emby_compatible_paths(self):
|
||||||
|
client = ZSpace.__new__(ZSpace)
|
||||||
|
client._host = "http://zspace.local/"
|
||||||
|
client._playhost = "http://play.zspace.local/"
|
||||||
|
client._apikey = "zspace-token"
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
client.get_backdrop_url("item-id", "tag-id"),
|
||||||
|
"http://zspace.local/emby/Items/item-id/Images/Backdrop?tag=tag-id&api_key=zspace-token",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
client.get_backdrop_url("item-id", "tag-id", remote=True),
|
||||||
|
"http://play.zspace.local/emby/Items/item-id/Images/Backdrop?tag=tag-id&api_key=zspace-token",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
client._ZSpace__get_local_image_by_id("item-id"),
|
||||||
|
"http://zspace.local/emby/Items/item-id/Images/Primary?api_key=zspace-token",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user