/
/
/
1"""Setup flow for the Lidarr Integration plugin."""
2
3from __future__ import annotations
4
5from dataclasses import replace
6from typing import TYPE_CHECKING
7
8from music_assistant_models.config_entries import ConfigEntry
9from music_assistant_models.enums import ConfigEntryType
10
11from music_assistant.models.setup_flow import SetupFlowError
12from music_assistant.providers.lidarr_integration.constants import (
13 CONF_LIDARR_API_KEY,
14 CONF_LIDARR_URL,
15 CONF_METADATA_PROFILE_ID,
16 CONF_QUALITY_PROFILE_ID,
17 CONF_ROOT_FOLDER_PATH,
18)
19from music_assistant.providers.lidarr_integration.lidarr_client import LidarrClient
20
21if TYPE_CHECKING:
22 from music_assistant.models.setup_flow import SetupSession
23
24# The connection details the provider cannot start without: handle_async_init raises
25# SetupFailedError when the api key is missing or Lidarr is unreachable, so they are
26# collected here rather than on the options page.
27_ENTRIES = (
28 ConfigEntry(
29 key=CONF_LIDARR_URL,
30 type=ConfigEntryType.STRING,
31 label="Lidarr URL",
32 description="The base URL of your Lidarr instance (e.g. http://localhost:8686).",
33 default_value="http://localhost:8686",
34 required=True,
35 ),
36 ConfigEntry(
37 key=CONF_LIDARR_API_KEY,
38 type=ConfigEntryType.SECURE_STRING,
39 label="Lidarr API Key",
40 description="Found in Lidarr under Settings > General > Security.",
41 required=True,
42 ),
43 ConfigEntry(
44 key=CONF_ROOT_FOLDER_PATH,
45 type=ConfigEntryType.STRING,
46 label="Root folder path",
47 description="The root folder in Lidarr where music is stored. "
48 "This should match the path your filesystem_local provider watches.",
49 default_value="/music",
50 required=True,
51 ),
52 ConfigEntry(
53 key=CONF_QUALITY_PROFILE_ID,
54 type=ConfigEntryType.INTEGER,
55 label="Quality profile ID",
56 description="The Lidarr quality profile ID to use for new artists. "
57 "Find this in Lidarr under Settings > Profiles (usually 1).",
58 default_value=1,
59 required=True,
60 ),
61 ConfigEntry(
62 key=CONF_METADATA_PROFILE_ID,
63 type=ConfigEntryType.INTEGER,
64 label="Metadata profile ID",
65 description="The Lidarr metadata profile ID to use for new artists. "
66 "Find this in Lidarr under Settings > Profiles (usually 1).",
67 default_value=1,
68 required=True,
69 ),
70)
71
72
73async def run_setup(session: SetupSession) -> None:
74 """Run the setup flow: collect the Lidarr connection details and create the provider."""
75 errors: dict[str, str] | None = None
76 setup_data = dict(session.context.setup_data)
77 while True:
78 entries = [
79 replace(entry, value=setup_data.get(entry.key, entry.value)) for entry in _ENTRIES
80 ]
81 submitted = await session.form(entries, step_id="user", errors=errors, last_step=True)
82 setup_data.update(submitted)
83 # reaching Lidarr here turns a wrong url/key into a form error the user can correct,
84 # instead of a provider that only fails later during handle_async_init
85 if error := await _connection_error(session, setup_data):
86 errors = {"base": error}
87 continue
88 try:
89 await session.finish(setup_data)
90 return
91 except SetupFlowError as err:
92 errors = {"base": err.translation_key or str(err)}
93
94
95async def _connection_error(session: SetupSession, setup_data: dict) -> str | None:
96 """
97 Return an error message when Lidarr cannot be reached with the collected values.
98
99 :param session: The setup session driving the flow.
100 :param setup_data: The setup data collected so far.
101 """
102 url = str(setup_data.get(CONF_LIDARR_URL) or "").strip().rstrip("/")
103 api_key = str(setup_data.get(CONF_LIDARR_API_KEY) or "").strip()
104 if not api_key:
105 return "Lidarr API key is required"
106 client = LidarrClient(
107 base_url=url,
108 api_key=api_key,
109 quality_profile_id=int(setup_data.get(CONF_QUALITY_PROFILE_ID) or 1),
110 metadata_profile_id=int(setup_data.get(CONF_METADATA_PROFILE_ID) or 1),
111 root_folder_path=str(setup_data.get(CONF_ROOT_FOLDER_PATH) or "/music"),
112 logger=session.mass.logger.getChild("lidarr_integration"),
113 )
114 try:
115 if not await client.test_connection():
116 return f"Cannot connect to Lidarr at {url}"
117 finally:
118 await client.close()
119 return None
120