Skip to content

Commit 0949eea

Browse files
authored
pythonic API (#77)
1 parent 907be69 commit 0949eea

34 files changed

+1655
-792
lines changed

.github/workflows/ruff.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Ruff - Checks
2+
on: push
3+
jobs:
4+
build:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- uses: actions/checkout@v3
8+
- uses: actions/setup-python@v4
9+
with:
10+
python-version: "3.9"
11+
12+
- name: Install dependencies
13+
run: |
14+
python -m pip install --upgrade pip
15+
pip install ruff
16+
17+
- name: Ruff livekit-api
18+
run: ruff check --output-format=github .
19+
20+
- name: Check format
21+
run: ruff format --check .
22+

examples/basic_room.py

Lines changed: 75 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,51 +5,61 @@
55

66
from livekit import rtc
77

8-
URL = 'ws://localhost:7880'
9-
TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE5MDY2MTMyODgsImlzcyI6IkFQSVRzRWZpZFpqclFvWSIsIm5hbWUiOiJuYXRpdmUiLCJuYmYiOjE2NzI2MTMyODgsInN1YiI6Im5hdGl2ZSIsInZpZGVvIjp7InJvb20iOiJ0ZXN0Iiwicm9vbUFkbWluIjp0cnVlLCJyb29tQ3JlYXRlIjp0cnVlLCJyb29tSm9pbiI6dHJ1ZSwicm9vbUxpc3QiOnRydWV9fQ.uSNIangMRu8jZD5mnRYoCHjcsQWCrJXgHCs0aNIgBFY' # noqa
8+
URL = "ws://localhost:7880"
9+
TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE5MDY2MTMyODgsImlzcyI6IkFQSVRzRWZpZFpqclFvWSIsIm5hbWUiOiJuYXRpdmUiLCJuYmYiOjE2NzI2MTMyODgsInN1YiI6Im5hdGl2ZSIsInZpZGVvIjp7InJvb20iOiJ0ZXN0Iiwicm9vbUFkbWluIjp0cnVlLCJyb29tQ3JlYXRlIjp0cnVlLCJyb29tSm9pbiI6dHJ1ZSwicm9vbUxpc3QiOnRydWV9fQ.uSNIangMRu8jZD5mnRYoCHjcsQWCrJXgHCs0aNIgBFY" # noqa
1010

1111

1212
async def main(room: rtc.Room) -> None:
13-
14-
@room.listens_to("participant_connected")
13+
@room.on("participant_connected")
1514
def on_participant_connected(participant: rtc.RemoteParticipant) -> None:
1615
logging.info(
17-
"participant connected: %s %s", participant.sid, participant.identity)
16+
"participant connected: %s %s", participant.sid, participant.identity
17+
)
1818

19-
@room.listens_to("participant_disconnected")
19+
@room.on("participant_disconnected")
2020
def on_participant_disconnected(participant: rtc.RemoteParticipant):
21-
logging.info("participant disconnected: %s %s",
22-
participant.sid, participant.identity)
23-
24-
@room.listens_to("local_track_published")
25-
def on_local_track_published(publication: rtc.LocalTrackPublication,
26-
track: Union[rtc.LocalAudioTrack,
27-
rtc.LocalVideoTrack]):
21+
logging.info(
22+
"participant disconnected: %s %s", participant.sid, participant.identity
23+
)
24+
25+
@room.on("local_track_published")
26+
def on_local_track_published(
27+
publication: rtc.LocalTrackPublication,
28+
track: Union[rtc.LocalAudioTrack, rtc.LocalVideoTrack],
29+
):
2830
logging.info("local track published: %s", publication.sid)
2931

30-
@room.listens_to("active_speakers_changed")
32+
@room.on("active_speakers_changed")
3133
def on_active_speakers_changed(speakers: list[rtc.Participant]):
3234
logging.info("active speakers changed: %s", speakers)
3335

34-
@room.listens_to("local_track_unpublished")
36+
@room.on("local_track_unpublished")
3537
def on_local_track_unpublished(publication: rtc.LocalTrackPublication):
3638
logging.info("local track unpublished: %s", publication.sid)
3739

38-
@room.listens_to("track_published")
39-
def on_track_published(publication: rtc.RemoteTrackPublication,
40-
participant: rtc.RemoteParticipant):
41-
logging.info("track published: %s from participant %s (%s)",
42-
publication.sid, participant.sid, participant.identity)
43-
44-
@room.listens_to("track_unpublished")
45-
def on_track_unpublished(publication: rtc.RemoteTrackPublication,
46-
participant: rtc.RemoteParticipant):
40+
@room.on("track_published")
41+
def on_track_published(
42+
publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant
43+
):
44+
logging.info(
45+
"track published: %s from participant %s (%s)",
46+
publication.sid,
47+
participant.sid,
48+
participant.identity,
49+
)
50+
51+
@room.on("track_unpublished")
52+
def on_track_unpublished(
53+
publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant
54+
):
4755
logging.info("track unpublished: %s", publication.sid)
4856

49-
@room.listens_to("track_subscribed")
50-
def on_track_subscribed(track: rtc.Track,
51-
publication: rtc.RemoteTrackPublication,
52-
participant: rtc.RemoteParticipant):
57+
@room.on("track_subscribed")
58+
def on_track_subscribed(
59+
track: rtc.Track,
60+
publication: rtc.RemoteTrackPublication,
61+
participant: rtc.RemoteParticipant,
62+
):
5363
logging.info("track subscribed: %s", publication.sid)
5464
if track.kind == rtc.TrackKind.KIND_VIDEO:
5565
_video_stream = rtc.VideoStream(track)
@@ -59,57 +69,61 @@ def on_track_subscribed(track: rtc.Track,
5969
_audio_stream = rtc.AudioStream(track)
6070
# audio_stream is an async iterator that yields AudioFrame
6171

62-
@room.listens_to("track_unsubscribed")
63-
def on_track_unsubscribed(track: rtc.Track,
64-
publication: rtc.RemoteTrackPublication,
65-
participant: rtc.RemoteParticipant):
72+
@room.on("track_unsubscribed")
73+
def on_track_unsubscribed(
74+
track: rtc.Track,
75+
publication: rtc.RemoteTrackPublication,
76+
participant: rtc.RemoteParticipant,
77+
):
6678
logging.info("track unsubscribed: %s", publication.sid)
6779

68-
@room.listens_to("track_muted")
69-
def on_track_muted(publication: rtc.RemoteTrackPublication,
70-
participant: rtc.RemoteParticipant):
80+
@room.on("track_muted")
81+
def on_track_muted(
82+
publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant
83+
):
7184
logging.info("track muted: %s", publication.sid)
7285

73-
@room.listens_to("track_unmuted")
74-
def on_track_unmuted(publication: rtc.RemoteTrackPublication,
75-
participant: rtc.RemoteParticipant):
86+
@room.on("track_unmuted")
87+
def on_track_unmuted(
88+
publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant
89+
):
7690
logging.info("track unmuted: %s", publication.sid)
7791

78-
@room.listens_to("data_received")
79-
def on_data_received(data: bytes,
80-
kind: rtc.DataPacketKind,
81-
participant: rtc.Participant):
92+
@room.on("data_received")
93+
def on_data_received(
94+
data: bytes, kind: rtc.DataPacketKind, participant: rtc.Participant
95+
):
8296
logging.info("received data from %s: %s", participant.identity, data)
8397

84-
@room.listens_to("connection_quality_changed")
85-
def on_connection_quality_changed(participant: rtc.Participant,
86-
quality: rtc.ConnectionQuality):
98+
@room.on("connection_quality_changed")
99+
def on_connection_quality_changed(
100+
participant: rtc.Participant, quality: rtc.ConnectionQuality
101+
):
87102
logging.info("connection quality changed for %s", participant.identity)
88103

89-
@room.listens_to("track_subscription_failed")
90-
def on_track_subscription_failed(participant: rtc.RemoteParticipant,
91-
track_sid: str,
92-
error: str):
93-
logging.info("track subscription failed: %s %s",
94-
participant.identity, error)
104+
@room.on("track_subscription_failed")
105+
def on_track_subscription_failed(
106+
participant: rtc.RemoteParticipant, track_sid: str, error: str
107+
):
108+
logging.info("track subscription failed: %s %s", participant.identity, error)
95109

96-
@room.listens_to("connection_state_changed")
110+
@room.on("connection_state_changed")
97111
def on_connection_state_changed(state: rtc.ConnectionState):
98112
logging.info("connection state changed: %s", state)
99113

100-
@room.listens_to("connected")
114+
@room.on("connected")
101115
def on_connected() -> None:
102116
logging.info("connected")
103117

104-
@room.listens_to("disconnected")
118+
@room.on("disconnected")
105119
def on_disconnected() -> None:
106120
logging.info("disconnected")
107121

108-
@room.listens_to("reconnecting")
122+
@room.on("reconnecting")
109123
def on_reconnecting() -> None:
110124
logging.info("reconnecting")
111125

112-
@room.listens_to("reconnected")
126+
@room.on("reconnected")
113127
def on_reconnected() -> None:
114128
logging.info("reconnected")
115129

@@ -122,9 +136,10 @@ def on_reconnected() -> None:
122136

123137

124138
if __name__ == "__main__":
125-
logging.basicConfig(level=logging.INFO, handlers=[
126-
logging.FileHandler("basic_room.log"),
127-
logging.StreamHandler()])
139+
logging.basicConfig(
140+
level=logging.INFO,
141+
handlers=[logging.FileHandler("basic_room.log"), logging.StreamHandler()],
142+
)
128143

129144
loop = asyncio.get_event_loop()
130145
room = rtc.Room(loop=loop)
@@ -135,8 +150,7 @@ async def cleanup():
135150

136151
asyncio.ensure_future(main(room))
137152
for signal in [SIGINT, SIGTERM]:
138-
loop.add_signal_handler(
139-
signal, lambda: asyncio.ensure_future(cleanup()))
153+
loop.add_signal_handler(signal, lambda: asyncio.ensure_future(cleanup()))
140154

141155
try:
142156
loop.run_forever()

examples/e2ee.py

Lines changed: 71 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
import numpy as np
66
from livekit import rtc
77

8-
URL = 'ws://localhost:7880'
9-
TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE5MDY2MTMyODgsImlzcyI6IkFQSVRzRWZpZFpqclFvWSIsIm5hbWUiOiJuYXRpdmUiLCJuYmYiOjE2NzI2MTMyODgsInN1YiI6Im5hdGl2ZSIsInZpZGVvIjp7InJvb20iOiJ0ZXN0Iiwicm9vbUFkbWluIjp0cnVlLCJyb29tQ3JlYXRlIjp0cnVlLCJyb29tSm9pbiI6dHJ1ZSwicm9vbUxpc3QiOnRydWV9fQ.uSNIangMRu8jZD5mnRYoCHjcsQWCrJXgHCs0aNIgBFY' # noqa
8+
URL = "ws://localhost:7880"
9+
TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE5MDY2MTMyODgsImlzcyI6IkFQSVRzRWZpZFpqclFvWSIsIm5hbWUiOiJuYXRpdmUiLCJuYmYiOjE2NzI2MTMyODgsInN1YiI6Im5hdGl2ZSIsInZpZGVvIjp7InJvb20iOiJ0ZXN0Iiwicm9vbUFkbWluIjp0cnVlLCJyb29tQ3JlYXRlIjp0cnVlLCJyb29tSm9pbiI6dHJ1ZSwicm9vbUxpc3QiOnRydWV9fQ.uSNIangMRu8jZD5mnRYoCHjcsQWCrJXgHCs0aNIgBFY" # noqa
1010

1111
# ("livekitrocks") this is our shared key, it must match the one used by your clients
1212
SHARED_KEY = b"liveitrocks"
@@ -15,40 +15,78 @@
1515
async def draw_cube(source: rtc.VideoSource):
1616
W, H, MID_W, MID_H = 1280, 720, 640, 360
1717
cube_size = 60
18-
vertices = (np.array([[-1, -1, -1], [1, -1, -1], [1, 1, -1], [-1, 1, -1],
19-
[-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1]]) * cube_size)
20-
edges = [[0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6],
21-
[6, 7], [7, 4], [0, 4], [1, 5], [2, 6], [3, 7]]
22-
23-
frame = rtc.ArgbFrame(livekit.VideoFormatType.FORMAT_ARGB, W, H)
18+
vertices = (
19+
np.array(
20+
[
21+
[-1, -1, -1],
22+
[1, -1, -1],
23+
[1, 1, -1],
24+
[-1, 1, -1],
25+
[-1, -1, 1],
26+
[1, -1, 1],
27+
[1, 1, 1],
28+
[-1, 1, 1],
29+
]
30+
)
31+
* cube_size
32+
)
33+
edges = [
34+
[0, 1],
35+
[1, 2],
36+
[2, 3],
37+
[3, 0],
38+
[4, 5],
39+
[5, 6],
40+
[6, 7],
41+
[7, 4],
42+
[0, 4],
43+
[1, 5],
44+
[2, 6],
45+
[3, 7],
46+
]
47+
48+
frame = rtc.ArgbFrame(rtc.VideoFormatType.FORMAT_ARGB, W, H)
2449
arr = np.ctypeslib.as_array(frame.data)
2550
angle = 0
2651

2752
while True:
2853
start_time = asyncio.get_event_loop().time()
2954
arr.fill(0)
30-
rot = np.dot(np.array([[1, 0, 0],
31-
[0, np.cos(angle), -np.sin(angle)],
32-
[0, np.sin(angle), np.cos(angle)]]),
33-
np.array([[np.cos(angle), 0, np.sin(angle)],
34-
[0, 1, 0],
35-
[-np.sin(angle), 0, np.cos(angle)]]))
36-
proj_points = [[int(pt[0] / (pt[2] / 200 + 1)), int(pt[1] / (pt[2] / 200 + 1))]
37-
for pt in np.dot(vertices, rot)]
55+
rot = np.dot(
56+
np.array(
57+
[
58+
[1, 0, 0],
59+
[0, np.cos(angle), -np.sin(angle)],
60+
[0, np.sin(angle), np.cos(angle)],
61+
]
62+
),
63+
np.array(
64+
[
65+
[np.cos(angle), 0, np.sin(angle)],
66+
[0, 1, 0],
67+
[-np.sin(angle), 0, np.cos(angle)],
68+
]
69+
),
70+
)
71+
proj_points = [
72+
[int(pt[0] / (pt[2] / 200 + 1)), int(pt[1] / (pt[2] / 200 + 1))]
73+
for pt in np.dot(vertices, rot)
74+
]
3875

3976
for e in edges:
4077
x1, y1, x2, y2 = *proj_points[e[0]], *proj_points[e[1]]
4178
for t in np.linspace(0, 1, 100):
42-
x, y = int(MID_W + (1 - t) * x1 + t *
43-
x2), int(MID_H + (1 - t) * y1 + t * y2)
79+
x, y = (
80+
int(MID_W + (1 - t) * x1 + t * x2),
81+
int(MID_H + (1 - t) * y1 + t * y2),
82+
)
4483
for dx in [-1, 0, 1]:
4584
for dy in [-1, 0, 1]:
4685
if 0 <= x + dx < W and 0 <= y + dy < H:
4786
idx = (y + dy) * W * 4 + (x + dx) * 4
48-
arr[idx:idx+4] = [255, 255, 255, 255]
87+
arr[idx : idx + 4] = [255, 255, 255, 255]
4988

50-
f = rtc.VideoFrame(
51-
0, rtc.VideoRotation.VIDEO_ROTATION_0, frame.to_i420())
89+
f = rtc.VideoFrame(0, rtc.VideoRotation.VIDEO_ROTATION_0, frame.to_i420())
5290
source.capture_frame(f)
5391
angle += 0.02
5492

@@ -58,19 +96,19 @@ async def draw_cube(source: rtc.VideoSource):
5896

5997
async def main(room: rtc.Room):
6098
@room.listens_to("e2ee_state_changed")
61-
def on_e2ee_state_changed(participant: rtc.Participant,
62-
state: rtc.EncryptionState) -> None:
99+
def on_e2ee_state_changed(
100+
participant: rtc.Participant, state: rtc.EncryptionState
101+
) -> None:
63102
logging.info("e2ee state changed: %s %s", participant.identity, state)
64103

65104
logging.info("connecting to %s", URL)
66105
try:
67106
e2ee_options = rtc.E2EEOptions()
68107
e2ee_options.key_provider_options.shared_key = SHARED_KEY
69108

70-
await room.connect(URL, TOKEN, options=rtc.RoomOptions(
71-
auto_subscribe=True,
72-
e2ee=e2ee_options
73-
))
109+
await room.connect(
110+
URL, TOKEN, options=rtc.RoomOptions(auto_subscribe=True, e2ee=e2ee_options)
111+
)
74112

75113
logging.info("connected to room %s", room.name)
76114
except rtc.ConnectError as e:
@@ -87,10 +125,12 @@ def on_e2ee_state_changed(participant: rtc.Participant,
87125

88126
asyncio.ensure_future(draw_cube(source))
89127

128+
90129
if __name__ == "__main__":
91-
logging.basicConfig(level=logging.INFO, handlers=[
92-
logging.FileHandler("e2ee.log"),
93-
logging.StreamHandler()])
130+
logging.basicConfig(
131+
level=logging.INFO,
132+
handlers=[logging.FileHandler("e2ee.log"), logging.StreamHandler()],
133+
)
94134

95135
loop = asyncio.get_event_loop()
96136
room = rtc.Room(loop=loop)
@@ -101,8 +141,7 @@ async def cleanup():
101141

102142
asyncio.ensure_future(main(room))
103143
for signal in [SIGINT, SIGTERM]:
104-
loop.add_signal_handler(
105-
signal, lambda: asyncio.ensure_future(cleanup()))
144+
loop.add_signal_handler(signal, lambda: asyncio.ensure_future(cleanup()))
106145

107146
try:
108147
loop.run_forever()

0 commit comments

Comments
 (0)