Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 deletions homeassistant/components/media_player/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import asyncio
import base64
import collections
from collections.abc import Callable
from contextlib import suppress
from dataclasses import dataclass
import datetime as dt
Expand All @@ -12,7 +13,7 @@
from http import HTTPStatus
import logging
import secrets
from typing import final
from typing import Any, cast, final
from urllib.parse import urlparse

from aiohttp import web
Expand Down Expand Up @@ -63,6 +64,7 @@
from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.network import get_url
from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import bind_hass

from .const import (
Expand Down Expand Up @@ -208,15 +210,15 @@ def is_on(hass, entity_id=None):
)


def _rename_keys(**keys):
def _rename_keys(**keys: Any) -> Callable[[dict[str, Any]], dict[str, Any]]:
"""Create validator that renames keys.

Necessary because the service schema names do not match the command parameters.

Async friendly.
"""

def rename(value):
def rename(value: dict[str, Any]) -> dict[str, Any]:
for to_key, from_key in keys.items():
if from_key in value:
value[to_key] = value.pop(from_key)
Expand All @@ -225,7 +227,7 @@ def rename(value):
return rename


async def async_setup(hass, config):
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Track states and offer events for media_players."""
component = hass.data[DOMAIN] = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL
Expand Down Expand Up @@ -508,7 +510,7 @@ def media_image_hash(self) -> str | None:

return None

async def async_get_media_image(self):
async def async_get_media_image(self) -> tuple[bytes | None, str | None]:
"""Fetch media image of current playing image."""
if (url := self.media_image_url) is None:
return None, None
Expand All @@ -520,7 +522,7 @@ async def async_get_browse_image(
media_content_type: str,
media_content_id: str,
media_image_id: str | None = None,
) -> tuple[str | None, str | None]:
) -> tuple[bytes | None, str | None]:
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the type hint here was previously wrong.

"""
Optionally fetch internally accessible image for media browser.

Expand Down Expand Up @@ -965,13 +967,15 @@ async def async_unjoin_player(self):
"""Remove this player from any group."""
await self.hass.async_add_executor_job(self.unjoin_player)

async def _async_fetch_image_from_cache(self, url):
async def _async_fetch_image_from_cache(
self, url: str
) -> tuple[bytes | None, str | None]:
"""Fetch image.

Images are cached in memory (the images are typically 10-100kB in size).
"""
cache_images = ENTITY_IMAGE_CACHE[CACHE_IMAGES]
cache_maxsize = ENTITY_IMAGE_CACHE[CACHE_MAXSIZE]
cache_images = cast(collections.OrderedDict, ENTITY_IMAGE_CACHE[CACHE_IMAGES])
cache_maxsize = cast(int, ENTITY_IMAGE_CACHE[CACHE_MAXSIZE])

if urlparse(url).hostname is None:
url = f"{get_url(self.hass)}{url}"
Expand All @@ -981,7 +985,7 @@ async def _async_fetch_image_from_cache(self, url):

async with cache_images[url][CACHE_LOCK]:
if CACHE_CONTENT in cache_images[url]:
return cache_images[url][CACHE_CONTENT]
return cache_images[url][CACHE_CONTENT] # type:ignore[no-any-return]

(content, content_type) = await self._async_fetch_image(url)

Expand All @@ -992,7 +996,7 @@ async def _async_fetch_image_from_cache(self, url):

return content, content_type

async def _async_fetch_image(self, url):
async def _async_fetch_image(self, url: str) -> tuple[bytes | None, str | None]:
"""Retrieve an image."""
content, content_type = (None, None)
websession = async_get_clientsession(self.hass)
Expand Down Expand Up @@ -1037,7 +1041,7 @@ class MediaPlayerImageView(HomeAssistantView):
url + "/browse_media/{media_content_type}/{media_content_id}",
]

def __init__(self, component):
def __init__(self, component: EntityComponent) -> None:
"""Initialize a media player view."""
self.component = component

Expand All @@ -1057,6 +1061,7 @@ async def get(
)
return web.Response(status=status)

assert isinstance(player, MediaPlayerEntity)
authenticated = (
request[KEY_AUTHENTICATED]
or request.query.get("token") == player.access_token
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/roku/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ async def async_get_browse_image(
media_content_type: str,
media_content_id: str,
media_image_id: str | None = None,
) -> tuple[str | None, str | None]:
) -> tuple[bytes | None, str | None]:
"""Fetch media browser image to serve via proxy."""
if media_content_type == MEDIA_TYPE_APP and media_content_id:
image_url = self.coordinator.roku.app_icon_url(media_content_id)
Expand Down