1 """Support for the Jellyfin media player."""
3 from __future__
import annotations
10 MediaPlayerEntityFeature,
18 from .
import JellyfinConfigEntry
19 from .browse_media
import build_item_response, build_root_response
20 from .client_wrapper
import get_artwork_url
21 from .const
import CONTENT_TYPE_MAP, LOGGER
22 from .coordinator
import JellyfinDataUpdateCoordinator
23 from .entity
import JellyfinClientEntity
28 entry: JellyfinConfigEntry,
29 async_add_entities: AddEntitiesCallback,
31 """Set up Jellyfin media_player from a config entry."""
32 coordinator = entry.runtime_data
35 def handle_coordinator_update() -> None:
36 """Add media player per session."""
37 entities: list[MediaPlayerEntity] = []
38 for session_id
in coordinator.data:
39 if session_id
not in coordinator.session_ids:
41 LOGGER.debug(
"Creating media player for session: %s", session_id)
42 coordinator.session_ids.add(session_id)
43 entities.append(entity)
46 handle_coordinator_update()
48 entry.async_on_unload(coordinator.async_add_listener(handle_coordinator_update))
52 """Represents a Jellyfin Player device."""
56 coordinator: JellyfinDataUpdateCoordinator,
59 """Initialize the Jellyfin Media Player entity."""
60 super().
__init__(coordinator, session_id)
84 """Process session data to update entity properties."""
86 media_content_type =
None
87 media_content_id =
None
89 media_series_title =
None
92 media_album_name =
None
93 media_album_artist =
None
98 media_position_updated =
None
103 state = MediaPlayerState.IDLE
104 media_position_updated = (
111 state = MediaPlayerState.PLAYING
112 media_content_type = CONTENT_TYPE_MAP.get(self.
now_playingnow_playing[
"Type"],
None)
113 media_content_id = self.
now_playingnow_playing[
"Id"]
117 media_duration =
int(self.
now_playingnow_playing[
"RunTimeTicks"] / 10000000)
119 if media_content_type == MediaType.EPISODE:
120 media_content_type = MediaType.TVSHOW
121 media_series_title = self.
now_playingnow_playing.
get(
"SeriesName")
122 media_season = self.
now_playingnow_playing.
get(
"ParentIndexNumber")
124 elif media_content_type == MediaType.MUSIC:
126 media_album_artist = self.
now_playingnow_playing.
get(
"AlbumArtist")
129 media_artist =
str(media_artists[0])
133 state = MediaPlayerState.PAUSED
137 if "PositionTicks" in self.
play_stateplay_state
167 """Image url of current playing media."""
177 """Flag media player features that are supported."""
178 commands: list[str] = self.capabilities.
get(
"SupportedCommands", [])
179 controllable = self.capabilities.
get(
"SupportsMediaControl",
False)
184 MediaPlayerEntityFeature.BROWSE_MEDIA
185 | MediaPlayerEntityFeature.PLAY_MEDIA
186 | MediaPlayerEntityFeature.PAUSE
187 | MediaPlayerEntityFeature.PLAY
188 | MediaPlayerEntityFeature.STOP
189 | MediaPlayerEntityFeature.SEEK
192 if "Mute" in commands:
193 features |= MediaPlayerEntityFeature.VOLUME_MUTE
195 if "VolumeSet" in commands:
196 features |= MediaPlayerEntityFeature.VOLUME_SET
201 """Send seek command."""
202 self.coordinator.api_client.jellyfin.remote_seek(
207 """Send pause command."""
208 self.coordinator.api_client.jellyfin.remote_pause(self.
session_idsession_id)
209 self.
_attr_state_attr_state = MediaPlayerState.PAUSED
212 """Send play command."""
213 self.coordinator.api_client.jellyfin.remote_unpause(self.
session_idsession_id)
214 self.
_attr_state_attr_state = MediaPlayerState.PLAYING
217 """Send the PlayPause command to the session."""
218 self.coordinator.api_client.jellyfin.remote_playpause(self.
session_idsession_id)
221 """Send stop command."""
222 self.coordinator.api_client.jellyfin.remote_stop(self.
session_idsession_id)
223 self.
_attr_state_attr_state = MediaPlayerState.IDLE
226 self, media_type: MediaType | str, media_id: str, **kwargs: Any
228 """Play a piece of media."""
229 self.coordinator.api_client.jellyfin.remote_play_media(
234 """Set volume level, range 0..1."""
235 self.coordinator.api_client.jellyfin.remote_set_volume(
240 """Mute the volume."""
242 self.coordinator.api_client.jellyfin.remote_mute(self.
session_idsession_id)
244 self.coordinator.api_client.jellyfin.remote_unmute(self.
session_idsession_id)
248 media_content_type: MediaType | str |
None =
None,
249 media_content_id: str |
None =
None,
251 """Return a BrowseMedia instance.
253 The BrowseMedia instance will be used by the "media_player/browse_media" websocket command.
256 if media_content_id
is None or media_content_id ==
"media-source://jellyfin":
258 self.
hasshasshass, self.coordinator.api_client, self.coordinator.user_id
263 self.coordinator.api_client,
264 self.coordinator.user_id,
dict[str, Any] session_data(self)
datetime|None parse_datetime(str|None value)
web.Response get(self, web.Request request, str config_key)
str|None get_artwork_url(JellyfinClient client, dict[str, Any] item, int max_width=600)