mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 16:57:10 +00:00
Use server name as entry title in Jellyfin (#79965)
This commit is contained in:
parent
ef719cf7ef
commit
84acb416b8
@ -20,17 +20,17 @@ from .const import CLIENT_VERSION, USER_AGENT, USER_APP_NAME
|
|||||||
|
|
||||||
async def validate_input(
|
async def validate_input(
|
||||||
hass: HomeAssistant, user_input: dict[str, Any], client: JellyfinClient
|
hass: HomeAssistant, user_input: dict[str, Any], client: JellyfinClient
|
||||||
) -> str:
|
) -> tuple[str, dict[str, Any]]:
|
||||||
"""Validate that the provided url and credentials can be used to connect."""
|
"""Validate that the provided url and credentials can be used to connect."""
|
||||||
url = user_input[CONF_URL]
|
url = user_input[CONF_URL]
|
||||||
username = user_input[CONF_USERNAME]
|
username = user_input[CONF_USERNAME]
|
||||||
password = user_input[CONF_PASSWORD]
|
password = user_input[CONF_PASSWORD]
|
||||||
|
|
||||||
userid = await hass.async_add_executor_job(
|
user_id, connect_result = await hass.async_add_executor_job(
|
||||||
_connect, client, url, username, password
|
_connect, client, url, username, password
|
||||||
)
|
)
|
||||||
|
|
||||||
return userid
|
return (user_id, connect_result)
|
||||||
|
|
||||||
|
|
||||||
def create_client(device_id: str, device_name: str | None = None) -> JellyfinClient:
|
def create_client(device_id: str, device_name: str | None = None) -> JellyfinClient:
|
||||||
@ -47,21 +47,30 @@ def create_client(device_id: str, device_name: str | None = None) -> JellyfinCli
|
|||||||
return client
|
return client
|
||||||
|
|
||||||
|
|
||||||
def _connect(client: JellyfinClient, url: str, username: str, password: str) -> str:
|
def _connect(
|
||||||
|
client: JellyfinClient, url: str, username: str, password: str
|
||||||
|
) -> tuple[str, dict[str, Any]]:
|
||||||
"""Connect to the Jellyfin server and assert that the user can login."""
|
"""Connect to the Jellyfin server and assert that the user can login."""
|
||||||
client.config.data["auth.ssl"] = url.startswith("https")
|
client.config.data["auth.ssl"] = url.startswith("https")
|
||||||
|
|
||||||
_connect_to_address(client.auth, url)
|
connect_result = _connect_to_address(client.auth, url)
|
||||||
|
|
||||||
_login(client.auth, url, username, password)
|
_login(client.auth, url, username, password)
|
||||||
return _get_id(client.jellyfin)
|
|
||||||
|
return (_get_user_id(client.jellyfin), connect_result)
|
||||||
|
|
||||||
|
|
||||||
def _connect_to_address(connection_manager: ConnectionManager, url: str) -> None:
|
def _connect_to_address(
|
||||||
|
connection_manager: ConnectionManager, url: str
|
||||||
|
) -> dict[str, Any]:
|
||||||
"""Connect to the Jellyfin server."""
|
"""Connect to the Jellyfin server."""
|
||||||
state = connection_manager.connect_to_address(url)
|
result: dict[str, Any] = connection_manager.connect_to_address(url)
|
||||||
if state["State"] != CONNECTION_STATE["ServerSignIn"]:
|
|
||||||
|
if result["State"] != CONNECTION_STATE["ServerSignIn"]:
|
||||||
raise CannotConnect
|
raise CannotConnect
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _login(
|
def _login(
|
||||||
connection_manager: ConnectionManager,
|
connection_manager: ConnectionManager,
|
||||||
@ -76,7 +85,7 @@ def _login(
|
|||||||
raise InvalidAuth
|
raise InvalidAuth
|
||||||
|
|
||||||
|
|
||||||
def _get_id(api: API) -> str:
|
def _get_user_id(api: API) -> str:
|
||||||
"""Set the unique userid from a Jellyfin server."""
|
"""Set the unique userid from a Jellyfin server."""
|
||||||
settings: dict[str, Any] = api.get_user_settings()
|
settings: dict[str, Any] = api.get_user_settings()
|
||||||
userid: str = settings["Id"]
|
userid: str = settings["Id"]
|
||||||
|
@ -54,7 +54,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
client = create_client(device_id=self.client_device_id)
|
client = create_client(device_id=self.client_device_id)
|
||||||
try:
|
try:
|
||||||
userid = await validate_input(self.hass, user_input, client)
|
user_id, connect_result = await validate_input(
|
||||||
|
self.hass, user_input, client
|
||||||
|
)
|
||||||
except CannotConnect:
|
except CannotConnect:
|
||||||
errors["base"] = "cannot_connect"
|
errors["base"] = "cannot_connect"
|
||||||
except InvalidAuth:
|
except InvalidAuth:
|
||||||
@ -63,11 +65,18 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
errors["base"] = "unknown"
|
errors["base"] = "unknown"
|
||||||
_LOGGER.exception(ex)
|
_LOGGER.exception(ex)
|
||||||
else:
|
else:
|
||||||
await self.async_set_unique_id(userid)
|
entry_title = user_input[CONF_URL]
|
||||||
|
|
||||||
|
server_info: dict[str, Any] = connect_result["Servers"][0]
|
||||||
|
|
||||||
|
if server_name := server_info.get("Name"):
|
||||||
|
entry_title = server_name
|
||||||
|
|
||||||
|
await self.async_set_unique_id(user_id)
|
||||||
self._abort_if_unique_id_configured()
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title=user_input[CONF_URL],
|
title=entry_title,
|
||||||
data={CONF_CLIENT_DEVICE_ID: self.client_device_id, **user_input},
|
data={CONF_CLIENT_DEVICE_ID: self.client_device_id, **user_input},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1 +1,17 @@
|
|||||||
"""Tests for the jellyfin integration."""
|
"""Tests for the jellyfin integration."""
|
||||||
|
import json
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from tests.common import load_fixture
|
||||||
|
|
||||||
|
|
||||||
|
def load_json_fixture(filename: str) -> Any:
|
||||||
|
"""Load JSON fixture on-demand."""
|
||||||
|
return json.loads(load_fixture(f"jellyfin/{filename}"))
|
||||||
|
|
||||||
|
|
||||||
|
async def async_load_json_fixture(hass: HomeAssistant, filename: str) -> Any:
|
||||||
|
"""Load JSON fixture on-demand asynchronously."""
|
||||||
|
return await hass.async_add_executor_job(load_json_fixture, filename)
|
||||||
|
@ -10,11 +10,7 @@ from jellyfin_apiclient_python.configuration import Config
|
|||||||
from jellyfin_apiclient_python.connection_manager import ConnectionManager
|
from jellyfin_apiclient_python.connection_manager import ConnectionManager
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from .const import (
|
from . import load_json_fixture
|
||||||
MOCK_SUCCESFUL_CONNECTION_STATE,
|
|
||||||
MOCK_SUCCESFUL_LOGIN_RESPONSE,
|
|
||||||
MOCK_USER_SETTINGS,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -40,8 +36,10 @@ def mock_client_device_id() -> Generator[None, MagicMock, None]:
|
|||||||
def mock_auth() -> MagicMock:
|
def mock_auth() -> MagicMock:
|
||||||
"""Return a mocked ConnectionManager."""
|
"""Return a mocked ConnectionManager."""
|
||||||
jf_auth = create_autospec(ConnectionManager)
|
jf_auth = create_autospec(ConnectionManager)
|
||||||
jf_auth.connect_to_address.return_value = MOCK_SUCCESFUL_CONNECTION_STATE
|
jf_auth.connect_to_address.return_value = load_json_fixture(
|
||||||
jf_auth.login.return_value = MOCK_SUCCESFUL_LOGIN_RESPONSE
|
"auth-connect-address.json"
|
||||||
|
)
|
||||||
|
jf_auth.login.return_value = load_json_fixture("auth-login.json")
|
||||||
|
|
||||||
return jf_auth
|
return jf_auth
|
||||||
|
|
||||||
@ -50,7 +48,7 @@ def mock_auth() -> MagicMock:
|
|||||||
def mock_api() -> MagicMock:
|
def mock_api() -> MagicMock:
|
||||||
"""Return a mocked API."""
|
"""Return a mocked API."""
|
||||||
jf_api = create_autospec(API)
|
jf_api = create_autospec(API)
|
||||||
jf_api.get_user_settings.return_value = MOCK_USER_SETTINGS
|
jf_api.get_user_settings.return_value = load_json_fixture("get-user-settings.json")
|
||||||
|
|
||||||
return jf_api
|
return jf_api
|
||||||
|
|
||||||
|
@ -2,16 +2,6 @@
|
|||||||
|
|
||||||
from typing import Final
|
from typing import Final
|
||||||
|
|
||||||
from jellyfin_apiclient_python.connection_manager import CONNECTION_STATE
|
|
||||||
|
|
||||||
TEST_URL: Final = "https://example.com"
|
TEST_URL: Final = "https://example.com"
|
||||||
TEST_USERNAME: Final = "test-username"
|
TEST_USERNAME: Final = "test-username"
|
||||||
TEST_PASSWORD: Final = "test-password"
|
TEST_PASSWORD: Final = "test-password"
|
||||||
|
|
||||||
MOCK_SUCCESFUL_CONNECTION_STATE: Final = {"State": CONNECTION_STATE["ServerSignIn"]}
|
|
||||||
MOCK_SUCCESFUL_LOGIN_RESPONSE: Final = {"AccessToken": "Test"}
|
|
||||||
|
|
||||||
MOCK_UNSUCCESFUL_CONNECTION_STATE: Final = {"State": CONNECTION_STATE["Unavailable"]}
|
|
||||||
MOCK_UNSUCCESFUL_LOGIN_RESPONSE: Final = {""}
|
|
||||||
|
|
||||||
MOCK_USER_SETTINGS: Final = {"Id": "123"}
|
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"State": 0
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"State": 2,
|
||||||
|
"Servers": [{ "Id": "SERVER-UUID", "Name": "JELLYFIN-SERVER" }]
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
{}
|
1844
tests/components/jellyfin/fixtures/auth-login.json
Normal file
1844
tests/components/jellyfin/fixtures/auth-login.json
Normal file
File diff suppressed because it is too large
Load Diff
19
tests/components/jellyfin/fixtures/get-user-settings.json
Normal file
19
tests/components/jellyfin/fixtures/get-user-settings.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"Id": "string",
|
||||||
|
"ViewType": "string",
|
||||||
|
"SortBy": "string",
|
||||||
|
"IndexBy": "string",
|
||||||
|
"RememberIndexing": true,
|
||||||
|
"PrimaryImageHeight": 0,
|
||||||
|
"PrimaryImageWidth": 0,
|
||||||
|
"CustomPrefs": {
|
||||||
|
"property1": "string",
|
||||||
|
"property2": "string"
|
||||||
|
},
|
||||||
|
"ScrollDirection": "Horizontal",
|
||||||
|
"ShowBackdrop": true,
|
||||||
|
"RememberSorting": true,
|
||||||
|
"SortOrder": "Ascending",
|
||||||
|
"ShowSidebar": true,
|
||||||
|
"Client": "emby"
|
||||||
|
}
|
@ -6,14 +6,8 @@ from homeassistant.components.jellyfin.const import CONF_CLIENT_DEVICE_ID, DOMAI
|
|||||||
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME
|
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from .const import (
|
from . import async_load_json_fixture
|
||||||
MOCK_SUCCESFUL_LOGIN_RESPONSE,
|
from .const import TEST_PASSWORD, TEST_URL, TEST_USERNAME
|
||||||
MOCK_UNSUCCESFUL_CONNECTION_STATE,
|
|
||||||
MOCK_UNSUCCESFUL_LOGIN_RESPONSE,
|
|
||||||
TEST_PASSWORD,
|
|
||||||
TEST_URL,
|
|
||||||
TEST_USERNAME,
|
|
||||||
)
|
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
@ -55,7 +49,7 @@ async def test_form(
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert result2["type"] == "create_entry"
|
assert result2["type"] == "create_entry"
|
||||||
assert result2["title"] == TEST_URL
|
assert result2["title"] == "JELLYFIN-SERVER"
|
||||||
assert result2["data"] == {
|
assert result2["data"] == {
|
||||||
CONF_CLIENT_DEVICE_ID: "TEST-UUID",
|
CONF_CLIENT_DEVICE_ID: "TEST-UUID",
|
||||||
CONF_URL: TEST_URL,
|
CONF_URL: TEST_URL,
|
||||||
@ -82,7 +76,9 @@ async def test_form_cannot_connect(
|
|||||||
assert result["type"] == "form"
|
assert result["type"] == "form"
|
||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
mock_client.auth.connect_to_address.return_value = MOCK_UNSUCCESFUL_CONNECTION_STATE
|
mock_client.auth.connect_to_address.return_value = await async_load_json_fixture(
|
||||||
|
hass, "auth-connect-address-failure.json"
|
||||||
|
)
|
||||||
|
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
@ -113,7 +109,9 @@ async def test_form_invalid_auth(
|
|||||||
assert result["type"] == "form"
|
assert result["type"] == "form"
|
||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
mock_client.auth.login.return_value = MOCK_UNSUCCESFUL_LOGIN_RESPONSE
|
mock_client.auth.login.return_value = await async_load_json_fixture(
|
||||||
|
hass, "auth-login-failure.json"
|
||||||
|
)
|
||||||
|
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
@ -174,7 +172,9 @@ async def test_form_persists_device_id_on_error(
|
|||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
mock_client_device_id.return_value = "TEST-UUID-1"
|
mock_client_device_id.return_value = "TEST-UUID-1"
|
||||||
mock_client.auth.login.return_value = MOCK_UNSUCCESFUL_LOGIN_RESPONSE
|
mock_client.auth.login.return_value = await async_load_json_fixture(
|
||||||
|
hass, "auth-login-failure.json"
|
||||||
|
)
|
||||||
|
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
@ -190,7 +190,9 @@ async def test_form_persists_device_id_on_error(
|
|||||||
assert result2["errors"] == {"base": "invalid_auth"}
|
assert result2["errors"] == {"base": "invalid_auth"}
|
||||||
|
|
||||||
mock_client_device_id.return_value = "TEST-UUID-2"
|
mock_client_device_id.return_value = "TEST-UUID-2"
|
||||||
mock_client.auth.login.return_value = MOCK_SUCCESFUL_LOGIN_RESPONSE
|
mock_client.auth.login.return_value = await async_load_json_fixture(
|
||||||
|
hass, "auth-login.json"
|
||||||
|
)
|
||||||
|
|
||||||
result3 = await hass.config_entries.flow.async_configure(
|
result3 = await hass.config_entries.flow.async_configure(
|
||||||
result2["flow_id"],
|
result2["flow_id"],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user