Use server name as entry title in Jellyfin (#79965)

This commit is contained in:
Chris Talkington 2022-10-09 23:50:05 -05:00 committed by GitHub
parent ef719cf7ef
commit 84acb416b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1939 additions and 44 deletions

View File

@ -20,17 +20,17 @@ from .const import CLIENT_VERSION, USER_AGENT, USER_APP_NAME
async def validate_input(
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."""
url = user_input[CONF_URL]
username = user_input[CONF_USERNAME]
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
)
return userid
return (user_id, connect_result)
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
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."""
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)
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."""
state = connection_manager.connect_to_address(url)
if state["State"] != CONNECTION_STATE["ServerSignIn"]:
result: dict[str, Any] = connection_manager.connect_to_address(url)
if result["State"] != CONNECTION_STATE["ServerSignIn"]:
raise CannotConnect
return result
def _login(
connection_manager: ConnectionManager,
@ -76,7 +85,7 @@ def _login(
raise InvalidAuth
def _get_id(api: API) -> str:
def _get_user_id(api: API) -> str:
"""Set the unique userid from a Jellyfin server."""
settings: dict[str, Any] = api.get_user_settings()
userid: str = settings["Id"]

View File

@ -54,7 +54,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
client = create_client(device_id=self.client_device_id)
try:
userid = await validate_input(self.hass, user_input, client)
user_id, connect_result = await validate_input(
self.hass, user_input, client
)
except CannotConnect:
errors["base"] = "cannot_connect"
except InvalidAuth:
@ -63,11 +65,18 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors["base"] = "unknown"
_LOGGER.exception(ex)
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()
return self.async_create_entry(
title=user_input[CONF_URL],
title=entry_title,
data={CONF_CLIENT_DEVICE_ID: self.client_device_id, **user_input},
)

View File

@ -1 +1,17 @@
"""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)

View File

@ -10,11 +10,7 @@ from jellyfin_apiclient_python.configuration import Config
from jellyfin_apiclient_python.connection_manager import ConnectionManager
import pytest
from .const import (
MOCK_SUCCESFUL_CONNECTION_STATE,
MOCK_SUCCESFUL_LOGIN_RESPONSE,
MOCK_USER_SETTINGS,
)
from . import load_json_fixture
@pytest.fixture
@ -40,8 +36,10 @@ def mock_client_device_id() -> Generator[None, MagicMock, None]:
def mock_auth() -> MagicMock:
"""Return a mocked ConnectionManager."""
jf_auth = create_autospec(ConnectionManager)
jf_auth.connect_to_address.return_value = MOCK_SUCCESFUL_CONNECTION_STATE
jf_auth.login.return_value = MOCK_SUCCESFUL_LOGIN_RESPONSE
jf_auth.connect_to_address.return_value = load_json_fixture(
"auth-connect-address.json"
)
jf_auth.login.return_value = load_json_fixture("auth-login.json")
return jf_auth
@ -50,7 +48,7 @@ def mock_auth() -> MagicMock:
def mock_api() -> MagicMock:
"""Return a mocked 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

View File

@ -2,16 +2,6 @@
from typing import Final
from jellyfin_apiclient_python.connection_manager import CONNECTION_STATE
TEST_URL: Final = "https://example.com"
TEST_USERNAME: Final = "test-username"
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"}

View File

@ -0,0 +1,3 @@
{
"State": 0
}

View File

@ -0,0 +1,4 @@
{
"State": 2,
"Servers": [{ "Id": "SERVER-UUID", "Name": "JELLYFIN-SERVER" }]
}

View File

@ -0,0 +1 @@
{}

File diff suppressed because it is too large Load Diff

View 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"
}

View File

@ -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.core import HomeAssistant
from .const import (
MOCK_SUCCESFUL_LOGIN_RESPONSE,
MOCK_UNSUCCESFUL_CONNECTION_STATE,
MOCK_UNSUCCESFUL_LOGIN_RESPONSE,
TEST_PASSWORD,
TEST_URL,
TEST_USERNAME,
)
from . import async_load_json_fixture
from .const import TEST_PASSWORD, TEST_URL, TEST_USERNAME
from tests.common import MockConfigEntry
@ -55,7 +49,7 @@ async def test_form(
await hass.async_block_till_done()
assert result2["type"] == "create_entry"
assert result2["title"] == TEST_URL
assert result2["title"] == "JELLYFIN-SERVER"
assert result2["data"] == {
CONF_CLIENT_DEVICE_ID: "TEST-UUID",
CONF_URL: TEST_URL,
@ -82,7 +76,9 @@ async def test_form_cannot_connect(
assert result["type"] == "form"
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(
result["flow_id"],
@ -113,7 +109,9 @@ async def test_form_invalid_auth(
assert result["type"] == "form"
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(
result["flow_id"],
@ -174,7 +172,9 @@ async def test_form_persists_device_id_on_error(
assert result["errors"] == {}
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(
result["flow_id"],
@ -190,7 +190,9 @@ async def test_form_persists_device_id_on_error(
assert result2["errors"] == {"base": "invalid_auth"}
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(
result2["flow_id"],