Skip to content

Commit 87eca2d

Browse files
committed
Create named pipes before opening them
1 parent cd59504 commit 87eca2d

File tree

2 files changed

+26
-8
lines changed

2 files changed

+26
-8
lines changed

music_assistant/providers/airplay/protocols/_protocol.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,27 @@ async def finish_pairing(self, pin: str) -> str:
104104
"""Finish pairing process with given PIN (if supported)."""
105105
raise NotImplementedError("Pairing not implemented for this protocol")
106106

107-
async def _open_pipes(self) -> None:
108-
"""Open both named pipes in non-blocking mode for async I/O."""
109-
# Create named pipes first if they don't exist
107+
async def _create_pipes(self) -> None:
108+
"""Create named pipes (FIFOs) before starting CLI process.
109+
110+
This must be called before starting the CLI binary so the FIFOs exist
111+
when the CLI tries to open them for reading.
112+
"""
110113
await asyncio.to_thread(self._create_named_pipe, self.audio_named_pipe)
111114
await asyncio.to_thread(self._create_named_pipe, self.commands_named_pipe)
115+
self.player.logger.debug("Named pipes created for streaming session")
116+
117+
def _create_named_pipe(self, pipe_path: str) -> None:
118+
"""Create a named pipe (FIFO) if it doesn't exist."""
119+
if not os.path.exists(pipe_path):
120+
os.mkfifo(pipe_path)
112121

122+
async def _open_pipes(self) -> None:
123+
"""Open both named pipes in non-blocking mode for async I/O.
124+
125+
This must be called AFTER the CLI process has started and opened the pipes
126+
for reading. Otherwise opening with O_WRONLY | O_NONBLOCK will fail with ENXIO.
127+
"""
113128
# Open audio pipe with buffer size optimization
114129
self._audio_pipe = AsyncNamedPipeWriter(self.audio_named_pipe, logger=self.player.logger)
115130
await self._audio_pipe.open(increase_buffer=True)
@@ -122,11 +137,6 @@ async def _open_pipes(self) -> None:
122137

123138
self.player.logger.debug("Named pipes opened in non-blocking mode for streaming session")
124139

125-
def _create_named_pipe(self, pipe_path: str) -> None:
126-
"""Create a named pipe (FIFO) if it doesn't exist."""
127-
if not os.path.exists(pipe_path):
128-
os.mkfifo(pipe_path)
129-
130140
async def stop(self) -> None:
131141
"""Stop playback and cleanup."""
132142
# Send stop command before setting _stopped flag

music_assistant/providers/airplay/protocols/raop.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ async def start(self, start_ntp: int, skip: int = 0) -> None:
100100
# https://github.com/music-assistant/libraop
101101
# we use this intermediate binary to do the actual streaming because attempts to do
102102
# so using pure python (e.g. pyatv) were not successful due to the realtime nature
103+
104+
# Create named pipes before starting CLI process
105+
await self._create_pipes()
106+
103107
cliraop_args = [
104108
cli_binary,
105109
"-ntpstart",
@@ -152,6 +156,10 @@ async def start_pairing(self) -> None:
152156
"""Start pairing process for this protocol (if supported)."""
153157
assert self.player.discovery_info is not None # for type checker
154158
cli_binary = await get_cli_binary(self.player.protocol)
159+
160+
# Create named pipes before starting CLI process
161+
await self._create_pipes()
162+
155163
cliraop_args = [
156164
cli_binary,
157165
"-pair",

0 commit comments

Comments
 (0)