feat:播放跳转api

This commit is contained in:
jxxghp
2024-01-03 12:02:08 +08:00
parent 322c72ab54
commit 0095e0f4dd
15 changed files with 260 additions and 80 deletions

View File

@@ -150,6 +150,14 @@ class EmbyModule(_ModuleBase):
return []
return self.emby.get_resume(count)
def mediaserver_play_url(self, server: str, item_id: Union[str, int]) -> Optional[str]:
"""
获取媒体库播放地址
"""
if server != "emby":
return None
return self.emby.get_play_url(item_id)
def mediaserver_latest(self, server: str, count: int = 20) -> List[schemas.MediaServerPlayItem]:
"""
获取媒体服务器最新入库条目

View File

@@ -22,6 +22,12 @@ class Emby(metaclass=Singleton):
self._host += "/"
if not self._host.startswith("http"):
self._host = "http://" + self._host
self._playhost = settings.EMBY_PLAY_HOST
if self._playhost:
if not self._playhost.endswith("/"):
self._playhost += "/"
if not self._playhost.startswith("http"):
self._playhost = "http://" + self._playhost
self._apikey = settings.EMBY_API_KEY
self.user = self.get_user(settings.SUPERUSER)
self.folders = self.get_emby_folders()
@@ -93,13 +99,17 @@ class Emby(metaclass=Singleton):
library_type = MediaType.TV.value
case _:
continue
image = self.__get_local_image_by_id(library.get("Id"))
libraries.append(
schemas.MediaServerLibrary(
server="emby",
id=library.get("Id"),
name=library.get("Name"),
path=library.get("Path"),
type=library_type
type=library_type,
image=image,
link=f'{self._playhost or self._host}web/index.html'
f'#!/videos?serverId={self.serverid}&parentId={library.get("Id")}'
)
)
return libraries
@@ -909,12 +919,13 @@ class Emby(metaclass=Singleton):
logger.error(f"连接Emby出错" + str(e))
return None
def __get_play_url(self, item_id: str) -> str:
def get_play_url(self, item_id: str) -> str:
"""
拼装媒体播放链接
:param item_id: 媒体的的ID
"""
return f"{self._host}web/index.html#!/item?id={item_id}&context=home&serverId={self.serverid}"
return f"{self._playhost or self._host}web/index.html#!" \
f"/item?id={item_id}&context=home&serverId={self.serverid}"
def __get_backdrop_url(self, item_id: str, image_tag: str) -> str:
"""
@@ -958,7 +969,7 @@ class Emby(metaclass=Singleton):
if item.get("Type") not in ["Movie", "Episode"]:
continue
item_type = MediaType.MOVIE.value if item.get("Type") == "Movie" else MediaType.TV.value
link = self.__get_play_url(item.get("Id"))
link = self.get_play_url(item.get("Id"))
if item_type == MediaType.MOVIE.value:
title = item.get("Name")
else:
@@ -1008,7 +1019,7 @@ class Emby(metaclass=Singleton):
if item.get("Type") not in ["Movie", "Series"]:
continue
item_type = MediaType.MOVIE.value if item.get("Type") == "Movie" else MediaType.TV.value
link = self.__get_play_url(item.get("Id"))
link = self.get_play_url(item.get("Id"))
image = self.__get_local_image_by_id(item_id=item.get("Id"))
ret_latest.append(schemas.MediaServerPlayItem(
id=item.get("Id"),

View File

@@ -148,6 +148,14 @@ class JellyfinModule(_ModuleBase):
return []
return self.jellyfin.get_resume(count)
def mediaserver_play_url(self, server: str, item_id: Union[str, int]) -> Optional[str]:
"""
获取媒体库播放地址
"""
if server != "jellyfin":
return None
return self.jellyfin.get_play_url(item_id)
def mediaserver_latest(self, server: str, count: int = 20) -> List[schemas.MediaServerPlayItem]:
"""
获取媒体服务器最新入库条目

View File

@@ -20,6 +20,12 @@ class Jellyfin(metaclass=Singleton):
self._host += "/"
if not self._host.startswith("http"):
self._host = "http://" + self._host
self._playhost = settings.JELLYFIN_PLAY_HOST
if self._playhost:
if not self._playhost.endswith("/"):
self._playhost += "/"
if not self._playhost.startswith("http"):
self._playhost = "http://" + self._playhost
self._apikey = settings.JELLYFIN_API_KEY
self.user = self.get_user(settings.SUPERUSER)
self.serverid = self.get_server_id()
@@ -72,13 +78,21 @@ class Jellyfin(metaclass=Singleton):
library_type = MediaType.TV.value
case _:
continue
image = self.__get_local_image_by_id(library.get("Id"))
link = f"{self._playhost or self._host}web/index.html#!" \
f"/movies.html?topParentId={library.get('Id')}" \
if library_type == MediaType.MOVIE.value \
else f"{self._playhost or self._host}web/index.html#!" \
f"/tv.html?topParentId={library.get('Id')}"
libraries.append(
schemas.MediaServerLibrary(
server="jellyfin",
id=library.get("Id"),
name=library.get("Name"),
path=library.get("Path"),
type=library_type
type=library_type,
image=image,
link=link
))
return libraries
@@ -588,12 +602,13 @@ class Jellyfin(metaclass=Singleton):
logger.error(f"连接Jellyfin出错" + str(e))
return None
def __get_play_url(self, item_id: str) -> str:
def get_play_url(self, item_id: str) -> str:
"""
拼装媒体播放链接
:param item_id: 媒体的的ID
"""
return f"{self._host}web/index.html#!/details?id={item_id}&serverId={self.serverid}"
return f"{self._playhost or self._host}web/index.html#!" \
f"/details?id={item_id}&serverId={self.serverid}"
def __get_local_image_by_id(self, item_id: str) -> str:
"""
@@ -637,7 +652,7 @@ class Jellyfin(metaclass=Singleton):
if item.get("Type") not in ["Movie", "Episode"]:
continue
item_type = MediaType.MOVIE.value if item.get("Type") == "Movie" else MediaType.TV.value
link = self.__get_play_url(item.get("Id"))
link = self.get_play_url(item.get("Id"))
if item.get("BackdropImageTags"):
image = self.__get_backdrop_url(item_id=item.get("Id"),
image_tag=item.get("BackdropImageTags")[0])
@@ -681,7 +696,7 @@ class Jellyfin(metaclass=Singleton):
if item.get("Type") not in ["Movie", "Series"]:
continue
item_type = MediaType.MOVIE.value if item.get("Type") == "Movie" else MediaType.TV.value
link = self.__get_play_url(item.get("Id"))
link = self.get_play_url(item.get("Id"))
image = self.__get_local_image_by_id(item_id=item.get("Id"))
ret_latest.append(schemas.MediaServerPlayItem(
id=item.get("Id"),

View File

@@ -149,3 +149,11 @@ class PlexModule(_ModuleBase):
if server != "plex":
return []
return self.plex.get_latest(count)
def mediaserver_play_url(self, server: str, item_id: Union[str, int]) -> Optional[str]:
"""
获取媒体库播放地址
"""
if server != "plex":
return None
return self.plex.get_play_url(item_id)

View File

@@ -1,4 +1,5 @@
import json
from functools import lru_cache
from pathlib import Path
from typing import List, Optional, Dict, Tuple, Generator, Any
from urllib.parse import quote_plus
@@ -22,6 +23,12 @@ class Plex(metaclass=Singleton):
self._host += "/"
if not self._host.startswith("http"):
self._host = "http://" + self._host
self._playhost = settings.PLEX_PLAY_HOST
if self._playhost:
if not self._playhost.endswith("/"):
self._playhost += "/"
if not self._playhost.startswith("http"):
self._playhost = "http://" + self._playhost
self._token = settings.PLEX_TOKEN
if self._host and self._token:
try:
@@ -50,6 +57,43 @@ class Plex(metaclass=Singleton):
self._plex = None
logger.error(f"Plex服务器连接失败{str(e)}")
@lru_cache(maxsize=10)
def __get_library_images(self, library_key: str) -> Optional[List[str]]:
"""
获取媒体服务器最近添加的媒体的图片列表
param: library_key
param: type type的含义: 1 电影 2 剧集 详见 plexapi/utils.py中SEARCHTYPES的定义
"""
if not self._plex:
return None
# 返回结果
poster_urls = {}
# 页码计数
container_start = 0
# 需要的总条数/每页的条数
total_size = 4
# 如果总数不足,接续获取下一页
while len(poster_urls) < total_size:
items = self._plex.fetchItems(f"/hubs/home/recentlyAdded?type={type}&sectionID={library_key}",
container_size=total_size,
container_start=container_start)
for item in items:
if item.type == 'episode':
# 如果是剧集的单集,则去找上级的图片
if item.parentThumb is not None:
poster_urls[item.parentThumb] = None
else:
# 否则就用自己的图片
if item.thumb is not None:
poster_urls[item.thumb] = None
if len(poster_urls) == total_size:
break
if len(items) < total_size:
break
container_start += total_size
return [f"{self._host.rstrip('/') + url}?X-Plex-Token={self._token}" for url in
list(poster_urls.keys())[:total_size]]
def get_librarys(self) -> List[schemas.MediaServerLibrary]:
"""
获取媒体服务器所有媒体库列表
@@ -70,12 +114,16 @@ class Plex(metaclass=Singleton):
library_type = MediaType.TV.value
case _:
continue
image_list = self.__get_library_images(library.key)
libraries.append(
schemas.MediaServerLibrary(
id=library.key,
name=library.title,
path=library.locations,
type=library_type
type=library_type,
image_list=image_list,
link=f"{self._playhost or self._host}#!/media/{self._plex.machineIdentifier}"
f"/com.plexapp.plugins.library?source={library.key}"
)
)
return libraries
@@ -544,12 +592,12 @@ class Plex(metaclass=Singleton):
"""
return self._plex
def __get_play_url(self, item_id: str) -> str:
def get_play_url(self, item_id: str) -> str:
"""
拼装媒体播放链接
:param item_id: 媒体的的ID
"""
return f'{self._host}#!/server/{self._plex.machineIdentifier}/details?key={item_id}'
return f'{self._playhost or self._host}#!/server/{self._plex.machineIdentifier}/details?key={item_id}'
def get_resume(self, num: int = 12) -> Optional[List[schemas.MediaServerPlayItem]]:
"""
@@ -568,7 +616,7 @@ class Plex(metaclass=Singleton):
name = "%s%s" % (item.grandparentTitle, item.index)
else:
name = "%s%s季第%s" % (item.grandparentTitle, item.parentIndex, item.index)
link = self.__get_play_url(item.key)
link = self.get_play_url(item.key)
image = item.artUrl
ret_resume.append(schemas.MediaServerPlayItem(
id=item.key,
@@ -590,7 +638,7 @@ class Plex(metaclass=Singleton):
ret_resume = []
for item in items:
item_type = MediaType.MOVIE.value if item.TYPE == "movie" else MediaType.TV.value
link = self.__get_play_url(item.key)
link = self.get_play_url(item.key)
title = item.title if item_type == MediaType.MOVIE.value else \
"%s%s" % (item.parentTitle, item.index)
image = item.posterUrl