Add Jellyfin audio_codec optionflow (#113036)

* Fix #92419; Add Jellyfin audio_codec optionflow

* Use CONF_AUDIO_CODEC constant, clean up code based on suggestions

* Fixed typos

* Parameterize Tests

* Use parameterized test for jellyfin test media resolve

* Apply suggestions from code review

* Update homeassistant/components/jellyfin/config_flow.py

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
Dennis Lee 2024-05-15 09:11:11 -05:00 committed by GitHub
parent d2d39bce3a
commit 4d34350f66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 176 additions and 5 deletions

View file

@ -144,6 +144,8 @@ def api_artwork_side_effect(*args, **kwargs):
def api_audio_url_side_effect(*args, **kwargs):
"""Handle variable responses for audio_url method."""
item_id = args[0]
if audio_codec := kwargs.get("audio_codec"):
return f"http://localhost/Audio/{item_id}/universal?UserId=test-username,DeviceId=TEST-UUID,MaxStreamingBitrate=140000000,AudioCodec={audio_codec}"
return f"http://localhost/Audio/{item_id}/universal?UserId=test-username,DeviceId=TEST-UUID,MaxStreamingBitrate=140000000"

View file

@ -1,4 +1,16 @@
# serializer version: 1
# name: test_audio_codec_resolve[aac]
'http://localhost/Audio/TRACK-UUID/universal?UserId=test-username,DeviceId=TEST-UUID,MaxStreamingBitrate=140000000,AudioCodec=aac'
# ---
# name: test_audio_codec_resolve[mp3]
'http://localhost/Audio/TRACK-UUID/universal?UserId=test-username,DeviceId=TEST-UUID,MaxStreamingBitrate=140000000,AudioCodec=mp3'
# ---
# name: test_audio_codec_resolve[vorbis]
'http://localhost/Audio/TRACK-UUID/universal?UserId=test-username,DeviceId=TEST-UUID,MaxStreamingBitrate=140000000,AudioCodec=vorbis'
# ---
# name: test_audio_codec_resolve[wma]
'http://localhost/Audio/TRACK-UUID/universal?UserId=test-username,DeviceId=TEST-UUID,MaxStreamingBitrate=140000000,AudioCodec=wma'
# ---
# name: test_movie_library
dict({
'can_expand': False,

View file

@ -3,9 +3,14 @@
from unittest.mock import MagicMock
import pytest
from voluptuous.error import Invalid
from homeassistant import config_entries
from homeassistant.components.jellyfin.const import CONF_CLIENT_DEVICE_ID, DOMAIN
from homeassistant.components.jellyfin.const import (
CONF_AUDIO_CODEC,
CONF_CLIENT_DEVICE_ID,
DOMAIN,
)
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
@ -435,3 +440,57 @@ async def test_reauth_exception(
)
assert result3["type"] is FlowResultType.ABORT
assert result3["reason"] == "reauth_successful"
async def test_options_flow(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_jellyfin: MagicMock,
mock_client: MagicMock,
) -> None:
"""Test config flow options."""
config_entry = MockConfigEntry(domain=DOMAIN)
config_entry.add_to_hass(hass)
assert config_entry.options == {}
result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "init"
# Audio Codec
# Default
result = await hass.config_entries.options.async_configure(
result["flow_id"], user_input={}
)
assert result["type"] == FlowResultType.CREATE_ENTRY
assert CONF_AUDIO_CODEC not in config_entry.options
# Bad
result = await hass.config_entries.options.async_init(config_entry.entry_id)
with pytest.raises(Invalid):
result = await hass.config_entries.options.async_configure(
result["flow_id"], user_input={CONF_AUDIO_CODEC: "ogg"}
)
@pytest.mark.parametrize(
"codec",
[("aac"), ("wma"), ("vorbis"), ("mp3")],
)
async def test_setting_codec(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_jellyfin: MagicMock,
mock_client: MagicMock,
codec: str,
) -> None:
"""Test setting the audio_codec."""
config_entry = MockConfigEntry(domain=DOMAIN)
config_entry.add_to_hass(hass)
result = await hass.config_entries.options.async_init(config_entry.entry_id)
result = await hass.config_entries.options.async_configure(
result["flow_id"], user_input={CONF_AUDIO_CODEC: codec}
)
assert result["type"] == FlowResultType.CREATE_ENTRY
assert config_entry.options[CONF_AUDIO_CODEC] == codec

View file

@ -48,6 +48,10 @@ async def test_resolve(
assert play_media.mime_type == "audio/flac"
assert play_media.url == snapshot
mock_api.audio_url.assert_called_with("TRACK-UUID")
assert mock_api.audio_url.call_count == 1
mock_api.audio_url.reset_mock()
# Test resolving a movie
mock_api.get_item.side_effect = None
mock_api.get_item.return_value = load_json_fixture("movie.json")
@ -71,6 +75,42 @@ async def test_resolve(
)
@pytest.mark.parametrize(
"audio_codec",
[("aac"), ("wma"), ("vorbis"), ("mp3")],
)
async def test_audio_codec_resolve(
hass: HomeAssistant,
mock_client: MagicMock,
init_integration: MockConfigEntry,
mock_jellyfin: MagicMock,
mock_api: MagicMock,
snapshot: SnapshotAssertion,
audio_codec: str,
) -> None:
"""Test resolving Jellyfin media items with audio codec."""
# Test resolving a track
mock_api.get_item.side_effect = None
mock_api.get_item.return_value = load_json_fixture("track.json")
result = await hass.config_entries.options.async_init(init_integration.entry_id)
await hass.config_entries.options.async_configure(
result["flow_id"], user_input={"audio_codec": audio_codec}
)
assert init_integration.options["audio_codec"] == audio_codec
play_media = await async_resolve_media(
hass, f"{URI_SCHEME}{DOMAIN}/TRACK-UUID", "media_player.jellyfin_device"
)
assert play_media.mime_type == "audio/flac"
assert play_media.url == snapshot
mock_api.audio_url.assert_called_with("TRACK-UUID", audio_codec=audio_codec)
assert mock_api.audio_url.call_count == 1
async def test_root(
hass: HomeAssistant,
mock_client: MagicMock,