Skip to content

Commit 93f52c2

Browse files
authored
Return the RTMP publishing address from register_rtmp_input (#95)
The register-input response now reports publish_url in place of port, so the caller no longer has to build an address from the composition origin and a port. Regenerated the composition client, which also picks up the 503 the endpoint can now answer with.
1 parent 1f417c2 commit 93f52c2

4 files changed

Lines changed: 51 additions & 17 deletions

File tree

‎fishjam/_composition_openapi_client/api/inputs/register_input.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ def _parse_response(
7979

8080
return response_500
8181

82+
if response.status_code == 503:
83+
response_503 = ApiError.from_dict(response.json())
84+
85+
return response_503
86+
8287
if client.raise_on_unexpected_status:
8388
raise errors.UnexpectedStatus(response.status_code, response.content)
8489
else:

‎fishjam/_composition_openapi_client/models/register_input_response.py‎

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ class RegisterInputResponse:
1818
endpoint_route (None | str | Unset):
1919
video_duration_ms (int | None | Unset):
2020
audio_duration_ms (int | None | Unset):
21-
port (int | None | Unset):
21+
publish_url (None | str | Unset):
2222
"""
2323

2424
bearer_token: None | str | Unset = UNSET
2525
endpoint_route: None | str | Unset = UNSET
2626
video_duration_ms: int | None | Unset = UNSET
2727
audio_duration_ms: int | None | Unset = UNSET
28-
port: int | None | Unset = UNSET
28+
publish_url: None | str | Unset = UNSET
2929

3030
def to_dict(self) -> dict[str, Any]:
3131
bearer_token: None | str | Unset
@@ -52,11 +52,11 @@ def to_dict(self) -> dict[str, Any]:
5252
else:
5353
audio_duration_ms = self.audio_duration_ms
5454

55-
port: int | None | Unset
56-
if isinstance(self.port, Unset):
57-
port = UNSET
55+
publish_url: None | str | Unset
56+
if isinstance(self.publish_url, Unset):
57+
publish_url = UNSET
5858
else:
59-
port = self.port
59+
publish_url = self.publish_url
6060

6161
field_dict: dict[str, Any] = {}
6262

@@ -69,8 +69,8 @@ def to_dict(self) -> dict[str, Any]:
6969
field_dict["video_duration_ms"] = video_duration_ms
7070
if audio_duration_ms is not UNSET:
7171
field_dict["audio_duration_ms"] = audio_duration_ms
72-
if port is not UNSET:
73-
field_dict["port"] = port
72+
if publish_url is not UNSET:
73+
field_dict["publish_url"] = publish_url
7474

7575
return field_dict
7676

@@ -114,21 +114,21 @@ def _parse_audio_duration_ms(data: object) -> int | None | Unset:
114114

115115
audio_duration_ms = _parse_audio_duration_ms(d.pop("audio_duration_ms", UNSET))
116116

117-
def _parse_port(data: object) -> int | None | Unset:
117+
def _parse_publish_url(data: object) -> None | str | Unset:
118118
if data is None:
119119
return data
120120
if isinstance(data, Unset):
121121
return data
122-
return cast(int | None | Unset, data)
122+
return cast(None | str | Unset, data)
123123

124-
port = _parse_port(d.pop("port", UNSET))
124+
publish_url = _parse_publish_url(d.pop("publish_url", UNSET))
125125

126126
register_input_response = cls(
127127
bearer_token=bearer_token,
128128
endpoint_route=endpoint_route,
129129
video_duration_ms=video_duration_ms,
130130
audio_duration_ms=audio_duration_ms,
131-
port=port,
131+
publish_url=publish_url,
132132
)
133133

134134
return register_input_response

‎fishjam/api/_composition_client.py‎

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -433,23 +433,38 @@ def register_mp4_input(
433433

434434
def register_rtmp_input(
435435
self, composition_id: str, input_id: str, *, stream_key: str
436-
) -> None:
436+
) -> str:
437437
"""Register an input that an RTMP publisher pushes media into.
438438
439-
The stream key identifies the input; the address to publish to belongs to the
440-
composition, not to this call.
439+
The stream key identifies the input and is carried in the returned address.
441440
442441
Args:
443442
composition_id: ID of the composition.
444443
input_id: ID to register the input under.
445444
stream_key: Key the publisher identifies the input with.
445+
446+
Returns:
447+
The address to publish the RTMP stream to.
448+
449+
Raises:
450+
InternalServerError: When the server reports no address, leaving the input
451+
impossible to publish to.
446452
"""
447-
self.register_input(
453+
response = self.register_input(
448454
composition_id,
449455
input_id,
450456
RtmpInput(type_=RtmpInputType.RTMP_SERVER, stream_key=stream_key),
451457
)
452458

459+
publish_url = _or_none(response.publish_url)
460+
if not publish_url:
461+
raise InternalServerError(
462+
f'Registering RTMP input "{input_id}" returned no publishing address, '
463+
"so it cannot be published to"
464+
)
465+
466+
return publish_url
467+
453468
def unregister_input(
454469
self,
455470
composition_id: str,

‎tests/test_composition.py‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
IMAGE_ID = "logo"
3636
LOCAL_URL = "http://localhost:8000"
3737
FONT_PATH = Path(__file__).parent / "fixtures" / "font.ttf"
38+
RTMP_PUBLISH_URL = "rtmps://rtmp.example.com:443/key"
3839

3940

4041
def client(composition_url: str | None = None) -> CompositionClient:
@@ -211,11 +212,24 @@ def test_sends_the_mp4_discriminant(self):
211212
}
212213

213214
def test_sends_the_rtmp_discriminant(self):
214-
with mock_response() as requests:
215+
with mock_response({"publish_url": RTMP_PUBLISH_URL}) as requests:
215216
client().register_rtmp_input(COMPOSITION_ID, INPUT_ID, stream_key="key")
216217

217218
assert sent_json(requests) == {"type": "rtmp_server", "stream_key": "key"}
218219

220+
def test_returns_the_rtmp_publishing_address_the_server_chose(self):
221+
with mock_response({"publish_url": RTMP_PUBLISH_URL}):
222+
url = client().register_rtmp_input(
223+
COMPOSITION_ID, INPUT_ID, stream_key="key"
224+
)
225+
226+
assert url == RTMP_PUBLISH_URL
227+
228+
def test_raises_when_no_rtmp_publishing_address_is_available(self):
229+
with mock_response():
230+
with pytest.raises(InternalServerError):
231+
client().register_rtmp_input(COMPOSITION_ID, INPUT_ID, stream_key="key")
232+
219233
def test_returns_the_durations_of_an_mp4_input(self):
220234
with mock_response({"video_duration_ms": 1000, "audio_duration_ms": 2000}):
221235
durations = client().register_mp4_input(

0 commit comments

Comments
 (0)