mirror of
https://github.com/sudoxnym/fin-assistant.git
synced 2026-04-14 19:46:26 +00:00
[contd] media source
This commit is contained in:
parent
a9e7a24af2
commit
adb48cdc23
2 changed files with 93 additions and 33 deletions
|
|
@ -689,6 +689,9 @@ class JellyfinClientManager(object):
|
|||
def get_server_url(self) -> str:
|
||||
return self.jf_client.config.data["auth.server"]
|
||||
|
||||
def get_auth_token(self) -> str:
|
||||
return self.jf_client.config.data["auth.token"]
|
||||
|
||||
async def get_item(self, id):
|
||||
return await self.hass.async_add_executor_job(self.jf_client.jellyfin.get_item, id)
|
||||
|
||||
|
|
|
|||
123
media_source.py
123
media_source.py
|
|
@ -90,6 +90,15 @@ async def async_get_media_source(hass: HomeAssistant):
|
|||
class JellyfinSource(MediaSource):
|
||||
"""Media source for Jellyfin"""
|
||||
|
||||
@staticmethod
|
||||
def parse_mediasource_identifier(identifier: str):
|
||||
prefix = f"{URI_SCHEME}{DOMAIN}/"
|
||||
text = identifier
|
||||
if identifier.startswith(prefix):
|
||||
text = identifier[len(prefix):]
|
||||
|
||||
return text.split(IDENTIFIER_SPLIT, 2)
|
||||
|
||||
def __init__(self, hass: HomeAssistant, manager: JellyfinClientManager):
|
||||
"""Initialize Netatmo source."""
|
||||
super().__init__(DOMAIN)
|
||||
|
|
@ -103,18 +112,24 @@ class JellyfinSource(MediaSource):
|
|||
if not item or not item.identifier:
|
||||
return None
|
||||
|
||||
_, media_content_id = parse_mediasource_identifier(item.identifier)
|
||||
media_content_type, media_content_id = self.parse_mediasource_identifier(item.identifier)
|
||||
|
||||
profile = {
|
||||
"Name": USER_APP_NAME,
|
||||
"MaxStreamingBitrate": 25000 * 1000,
|
||||
"MusicStreamingTranscodingBitrate": 1280000,
|
||||
"MusicStreamingTranscodingBitrate": 1920000,
|
||||
"TimelineOffsetSeconds": 5,
|
||||
"TranscodingProfiles": [
|
||||
{"Type": "Audio"},
|
||||
{
|
||||
"Container": "mp4",
|
||||
"Type": "Audio",
|
||||
"Container": "mp3",
|
||||
"Protocol": "http",
|
||||
"AudioCodec": "mp3",
|
||||
"MaxAudioChannels": "2",
|
||||
},
|
||||
{
|
||||
"Type": "Video",
|
||||
"Container": "mp4",
|
||||
"Protocol": "http",
|
||||
"AudioCodec": "aac,mp3,opus,flac,vorbis",
|
||||
"VideoCodec": "h264,mpeg4,mpeg2video",
|
||||
|
|
@ -122,7 +137,25 @@ class JellyfinSource(MediaSource):
|
|||
},
|
||||
{"Container": "jpeg", "Type": "Photo"},
|
||||
],
|
||||
"DirectPlayProfiles": [],
|
||||
"DirectPlayProfiles": [
|
||||
{
|
||||
"Type": "Audio",
|
||||
"Container": "mp3",
|
||||
"AudioCodec": "mp3"
|
||||
},
|
||||
{
|
||||
"Type": "Audio",
|
||||
"Container": "m4a,m4b",
|
||||
"AudioCodec": "aac"
|
||||
},
|
||||
{
|
||||
"Type": "Video",
|
||||
"Container": "mp4,m4v",
|
||||
"AudioCodec": "aac,mp3,opus,flac,vorbis",
|
||||
"VideoCodec": "h264,mpeg4,mpeg2video",
|
||||
"MaxAudioChannels": "6",
|
||||
},
|
||||
],
|
||||
"ResponseProfiles": [],
|
||||
"ContainerProfiles": [],
|
||||
"CodecProfiles": [],
|
||||
|
|
@ -158,21 +191,48 @@ class JellyfinSource(MediaSource):
|
|||
|
||||
playback_info = await self.jelly_cm.get_play_info(media_content_id, profile)
|
||||
_LOGGER.debug("playbackinfo: %s", str(playback_info))
|
||||
if playback_info is None or "MediaSources" not in playback_info:
|
||||
_LOGGER.error(f"No playback info for item id {media_content_id}")
|
||||
return None
|
||||
|
||||
selected = None
|
||||
weight_selected = 0
|
||||
for media_source in playback_info["MediaSources"]:
|
||||
weight = (media_source.get("SupportsDirectPlay") or 0) * 50000 + (
|
||||
weight = (media_source.get("SupportsDirectStream") or 0) * 50000 + (
|
||||
media_source.get("Bitrate") or 0
|
||||
) / 1000
|
||||
if weight > weight_selected:
|
||||
weight_selected = weight
|
||||
selected = media_source
|
||||
|
||||
if selected is None:
|
||||
return None
|
||||
|
||||
if selected["SupportsTranscoding"]:
|
||||
if selected["SupportsDirectStream"]:
|
||||
if media_content_type == MEDIA_TYPE_TRACK:
|
||||
mimetype = "audio/" + selected["Container"]
|
||||
url = self.jelly_cm.get_server_url() + "/Audio/%s/stream?static=true&MediaSourceId=%s&api_key=%s" % (
|
||||
media_content_id,
|
||||
selected["Id"],
|
||||
self.jelly_cm.get_auth_token()
|
||||
)
|
||||
else:
|
||||
mimetype = "video/" + selected["Container"]
|
||||
url = self.jelly_cm.get_server_url() + "/Videos/%s/stream?static=true&MediaSourceId=%s&api_key=%s" % (
|
||||
media_content_id,
|
||||
selected["Id"],
|
||||
self.jelly_cm.get_auth_token()
|
||||
)
|
||||
elif selected["SupportsTranscoding"]:
|
||||
url = self.jelly_cm.get_server_url() + selected.get("TranscodingUrl")
|
||||
_LOGGER.debug("cast url: %s", url)
|
||||
return PlayMedia(url, "video/mp4")
|
||||
container = selected["TranscodingContainer"] if "TranscodingContainer" in selected else selected["Container"]
|
||||
if media_content_type == MEDIA_TYPE_TRACK:
|
||||
mimetype = "audio/" + container
|
||||
else:
|
||||
mimetype = "video/" + container
|
||||
|
||||
_LOGGER.debug("cast url: %s", url)
|
||||
return PlayMedia(url, mimetype)
|
||||
|
||||
return None
|
||||
|
||||
|
|
@ -183,7 +243,7 @@ class JellyfinSource(MediaSource):
|
|||
autolog("<<<")
|
||||
|
||||
media_contant_type, media_content_id = async_parse_identifier(item)
|
||||
return await async_library_items(self.jelly_cm, media_contant_type, media_content_id)
|
||||
return await async_library_items(self.jelly_cm, media_contant_type, media_content_id, canPlayList=False)
|
||||
|
||||
@callback
|
||||
def async_parse_identifier(
|
||||
|
|
@ -210,7 +270,6 @@ def Type2Mediatype(type):
|
|||
"Playlist": MEDIA_CLASS_DIRECTORY,
|
||||
"MusicArtist": MEDIA_TYPE_ARTIST,
|
||||
"MusicAlbum": MEDIA_TYPE_ALBUM,
|
||||
"Audio": MEDIA_TYPE_TRACK,
|
||||
}
|
||||
return switcher[type]
|
||||
|
||||
|
|
@ -231,32 +290,28 @@ def Type2Mediaclass(type):
|
|||
}
|
||||
return switcher[type]
|
||||
|
||||
def IsPlayable(type):
|
||||
def IsPlayable(type, canPlayList):
|
||||
switcher = {
|
||||
"Movie": True,
|
||||
"Series": True,
|
||||
"Season": True,
|
||||
"Series": canPlayList,
|
||||
"Season": canPlayList,
|
||||
"Episode": True,
|
||||
"Music": False,
|
||||
"BoxSet": True,
|
||||
"BoxSet": canPlayList,
|
||||
"Folder": False,
|
||||
"CollectionFolder": False,
|
||||
"Playlist": True,
|
||||
"MusicArtist": True,
|
||||
"MusicAlbum": True,
|
||||
"Playlist": canPlayList,
|
||||
"MusicArtist": canPlayList,
|
||||
"MusicAlbum": canPlayList,
|
||||
"Audio": True,
|
||||
}
|
||||
return switcher[type]
|
||||
|
||||
def parse_mediasource_identifier(identifier: str):
|
||||
prefix = f"{URI_SCHEME}{DOMAIN}/"
|
||||
text = identifier
|
||||
if identifier.startswith(prefix):
|
||||
text = identifier[len(prefix):]
|
||||
|
||||
return text.split(IDENTIFIER_SPLIT, 2)
|
||||
|
||||
async def async_library_items(jelly_cm: JellyfinClientManager, media_content_type_in=None, media_content_id_in=None) -> BrowseMediaSource:
|
||||
async def async_library_items(jelly_cm: JellyfinClientManager,
|
||||
media_content_type_in=None,
|
||||
media_content_id_in=None,
|
||||
canPlayList=True
|
||||
) -> BrowseMediaSource:
|
||||
"""
|
||||
Create response payload to describe contents of a specific library.
|
||||
|
||||
|
|
@ -271,7 +326,7 @@ async def async_library_items(jelly_cm: JellyfinClientManager, media_content_typ
|
|||
media_content_type = None
|
||||
media_content_id = None
|
||||
else:
|
||||
media_content_type, media_content_id = parse_mediasource_identifier(media_content_id_in)
|
||||
media_content_type, media_content_id = JellyfinSource.parse_mediasource_identifier(media_content_id_in)
|
||||
_LOGGER.debug(f'>> {media_content_type} / {media_content_id}')
|
||||
|
||||
if media_content_type in [None, "library"]:
|
||||
|
|
@ -287,7 +342,9 @@ async def async_library_items(jelly_cm: JellyfinClientManager, media_content_typ
|
|||
)
|
||||
elif media_content_type in [MEDIA_CLASS_DIRECTORY, MEDIA_TYPE_ARTIST, MEDIA_TYPE_ALBUM, MEDIA_TYPE_PLAYLIST, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_SEASON]:
|
||||
query = {
|
||||
"ParentId": media_content_id
|
||||
"ParentId": media_content_id,
|
||||
"sortBy": "SortName",
|
||||
"sortOrder": "Ascending"
|
||||
}
|
||||
|
||||
parent_item = await jelly_cm.get_item(media_content_id)
|
||||
|
|
@ -297,7 +354,7 @@ async def async_library_items(jelly_cm: JellyfinClientManager, media_content_typ
|
|||
media_class=media_content_type,
|
||||
media_content_type=media_content_type,
|
||||
title=parent_item["Name"],
|
||||
can_play=IsPlayable(parent_item["Type"]),
|
||||
can_play=IsPlayable(parent_item["Type"], canPlayList),
|
||||
can_expand=True,
|
||||
thumbnail=await jelly_cm.get_artwork_url(media_content_id),
|
||||
children=[],
|
||||
|
|
@ -328,7 +385,7 @@ async def async_library_items(jelly_cm: JellyfinClientManager, media_content_typ
|
|||
media_class=Type2Mediaclass(item["Type"]),
|
||||
media_content_type=Type2Mediatype(item["Type"]),
|
||||
title=item["Name"],
|
||||
can_play=IsPlayable(item["Type"]),
|
||||
can_play=IsPlayable(item["Type"], canPlayList),
|
||||
can_expand=True,
|
||||
children=[],
|
||||
thumbnail=await jelly_cm.get_artwork_url(item["Id"])
|
||||
|
|
@ -340,7 +397,7 @@ async def async_library_items(jelly_cm: JellyfinClientManager, media_content_typ
|
|||
media_class=Type2Mediaclass(item["Type"]),
|
||||
media_content_type=Type2Mediatype(item["Type"]),
|
||||
title=item["Name"],
|
||||
can_play=IsPlayable(item["Type"]),
|
||||
can_play=IsPlayable(item["Type"], canPlayList),
|
||||
can_expand=False,
|
||||
children=[],
|
||||
thumbnail=await jelly_cm.get_artwork_url(item["Id"])
|
||||
|
|
@ -352,7 +409,7 @@ async def async_library_items(jelly_cm: JellyfinClientManager, media_content_typ
|
|||
library_info.media_content_type = Type2Mediatype(item["Type"])
|
||||
library_info.media_class = Type2Mediaclass(item["Type"])
|
||||
library_info.can_expand = False
|
||||
library_info.can_play=IsPlayable(item["Type"]),
|
||||
library_info.can_play=IsPlayable(item["Type"], canPlayList),
|
||||
break
|
||||
|
||||
return library_info
|
||||
|
|
|
|||
Loading…
Reference in a new issue