pyLiveMusic 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyLiveMusic/__init__.py +6 -0
- pyLiveMusic/_api/_errors.py +17 -0
- pyLiveMusic/_api/playback.py +213 -0
- pyLiveMusic/_api/queue.py +106 -0
- pyLiveMusic/_api/rooms.py +51 -0
- pyLiveMusic/_api/signaling.py +24 -0
- pyLiveMusic/_core/_core_func/_data_config.py +34 -0
- pyLiveMusic/_core/_core_func/_state/_data_state.py +26 -0
- pyLiveMusic/_core/_database/memory/_repo.py +41 -0
- pyLiveMusic/_core/_database/memory/_storage.py +13 -0
- pyLiveMusic/_core/_database/mongo/_repo.py +45 -0
- pyLiveMusic/_core/_database/mongo/_storage.py +31 -0
- pyLiveMusic/_core/_database/redis/_repo.py +5 -0
- pyLiveMusic/_core/_database/redis/_storage.py +3 -0
- pyLiveMusic/_static/audio.html +18 -0
- pyLiveMusic/_static/audio.js +271 -0
- pyLiveMusic/_utils/_app.py +98 -0
- pyLiveMusic/_utils/_settings.py +23 -0
- pyLiveMusic/_utils/auth.py +22 -0
- pyLiveMusic/_webrtc/audio.py +450 -0
- pyLiveMusic/_webrtc/backend.py +525 -0
- pyLiveMusic/_webrtc/peer.py +56 -0
- pyLiveMusic/core/__init__.py +1 -0
- pyLiveMusic/core/server.py +37 -0
- pyLiveMusic/storage/__init__.py +9 -0
- pyLiveMusic/storage/memory.py +11 -0
- pyLiveMusic/storage/mongodb.py +21 -0
- pyLiveMusic/storage/redis.py +3 -0
- pylivemusic-0.0.1.dist-info/METADATA +44 -0
- pylivemusic-0.0.1.dist-info/RECORD +33 -0
- pylivemusic-0.0.1.dist-info/WHEEL +5 -0
- pylivemusic-0.0.1.dist-info/licenses/LICENSE +674 -0
- pylivemusic-0.0.1.dist-info/top_level.txt +1 -0
pyLiveMusic/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from aiohttp import web
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
ERROR_STATUS = {
|
|
5
|
+
"NOT_PLAYING_ANYTHING_CURRENTLY": 409,
|
|
6
|
+
"INVALID_SEEK_LIMIT": 400,
|
|
7
|
+
"INVALID_SEEK_SECONDS": 400,
|
|
8
|
+
"INVALID_VOLUME": 400,
|
|
9
|
+
"INVALID_REPEAT_MODE": 400,
|
|
10
|
+
"INVALID_PLAY_ORDER": 400,
|
|
11
|
+
"INVALID_AUDIO_QUALITY": 400,
|
|
12
|
+
"TRACK_DURATION_UNKNOWN": 409,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def json_error(code):
|
|
17
|
+
return web.json_response({"error": code}, status=ERROR_STATUS.get(code, 400))
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
from aiohttp import web
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from pyLiveMusic._api._errors import json_error
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_room(request):
|
|
8
|
+
|
|
9
|
+
room = request.app["state"].rooms.get(request.match_info["room_id"])
|
|
10
|
+
if room is None:
|
|
11
|
+
raise web.HTTPNotFound(text="ROOM_NOT_FOUND")
|
|
12
|
+
|
|
13
|
+
return room
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def pause(request):
|
|
17
|
+
|
|
18
|
+
request.app["auth"]._verify(request)
|
|
19
|
+
room = get_room(request)
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
await room.pause()
|
|
23
|
+
except ValueError as error:
|
|
24
|
+
return json_error(str(error))
|
|
25
|
+
|
|
26
|
+
return web.json_response(room.state())
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def resume(request):
|
|
30
|
+
|
|
31
|
+
request.app["auth"]._verify(request)
|
|
32
|
+
room = get_room(request)
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
await room.resume()
|
|
36
|
+
except ValueError as error:
|
|
37
|
+
return json_error(str(error))
|
|
38
|
+
|
|
39
|
+
return web.json_response(room.state())
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async def skip(request):
|
|
43
|
+
|
|
44
|
+
request.app["auth"]._verify(request)
|
|
45
|
+
room = get_room(request)
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
await room.skip()
|
|
49
|
+
except ValueError as error:
|
|
50
|
+
return json_error(str(error))
|
|
51
|
+
|
|
52
|
+
return web.json_response(room.state_or_not_playing())
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def volume(request):
|
|
56
|
+
|
|
57
|
+
request.app["auth"]._verify(request)
|
|
58
|
+
room = get_room(request)
|
|
59
|
+
data = await request.json()
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
await room.set_volume(data["volume"])
|
|
63
|
+
|
|
64
|
+
except (
|
|
65
|
+
KeyError,
|
|
66
|
+
ValueError,
|
|
67
|
+
) as error:
|
|
68
|
+
|
|
69
|
+
code = (
|
|
70
|
+
str(error)
|
|
71
|
+
if isinstance(error, ValueError)
|
|
72
|
+
else "INVALID_VOLUME"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return json_error(code)
|
|
76
|
+
|
|
77
|
+
return web.json_response(room.state_or_not_playing())
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
async def shuffle(request):
|
|
81
|
+
|
|
82
|
+
request.app["auth"]._verify(request)
|
|
83
|
+
room = get_room(request)
|
|
84
|
+
data = await request.json()
|
|
85
|
+
|
|
86
|
+
await room.set_shuffle(data.get("enabled", False))
|
|
87
|
+
|
|
88
|
+
return web.json_response(room.state_or_not_playing())
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
async def repeat(request):
|
|
92
|
+
|
|
93
|
+
request.app["auth"]._verify(request)
|
|
94
|
+
room = get_room(request)
|
|
95
|
+
data = await request.json()
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
await room.set_repeat(data["mode"])
|
|
99
|
+
|
|
100
|
+
except (
|
|
101
|
+
KeyError,
|
|
102
|
+
ValueError,
|
|
103
|
+
) as error:
|
|
104
|
+
|
|
105
|
+
code = (
|
|
106
|
+
str(error)
|
|
107
|
+
if isinstance(error, ValueError)
|
|
108
|
+
else "INVALID_REPEAT_MODE"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return json_error(code)
|
|
112
|
+
|
|
113
|
+
return web.json_response(room.state_or_not_playing())
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
async def ascending(request):
|
|
117
|
+
|
|
118
|
+
request.app["auth"]._verify(request)
|
|
119
|
+
room = get_room(request)
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
await room.set_order("ascending")
|
|
123
|
+
except ValueError as error:
|
|
124
|
+
return json_error(str(error))
|
|
125
|
+
|
|
126
|
+
return web.json_response(room.state_or_not_playing())
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
async def descending(request):
|
|
130
|
+
|
|
131
|
+
request.app["auth"]._verify(request)
|
|
132
|
+
room = get_room(request)
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
await room.set_order("descending")
|
|
136
|
+
except ValueError as error:
|
|
137
|
+
return json_error(str(error))
|
|
138
|
+
|
|
139
|
+
return web.json_response(room.state_or_not_playing())
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
async def seek_front(request):
|
|
143
|
+
|
|
144
|
+
request.app["auth"]._verify(request)
|
|
145
|
+
room = get_room(request)
|
|
146
|
+
data = await request.json()
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
await room.seek_front(data["seconds"])
|
|
150
|
+
|
|
151
|
+
except (
|
|
152
|
+
KeyError,
|
|
153
|
+
ValueError,
|
|
154
|
+
) as error:
|
|
155
|
+
|
|
156
|
+
code = (
|
|
157
|
+
str(error)
|
|
158
|
+
if isinstance(error, ValueError)
|
|
159
|
+
else "INVALID_SEEK_SECONDS"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
return json_error(code)
|
|
163
|
+
|
|
164
|
+
return web.json_response(room.state())
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
async def seek_back(request):
|
|
168
|
+
|
|
169
|
+
request.app["auth"]._verify(request)
|
|
170
|
+
room = get_room(request)
|
|
171
|
+
data = await request.json()
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
await room.seek_back(data["seconds"])
|
|
175
|
+
except (
|
|
176
|
+
KeyError,
|
|
177
|
+
ValueError,
|
|
178
|
+
) as error:
|
|
179
|
+
|
|
180
|
+
code = (
|
|
181
|
+
str(error)
|
|
182
|
+
if isinstance(error, ValueError)
|
|
183
|
+
else "INVALID_SEEK_SECONDS"
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
return json_error(code)
|
|
187
|
+
|
|
188
|
+
return web.json_response(room.state())
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
async def quality(request):
|
|
192
|
+
|
|
193
|
+
request.app["auth"]._verify(request)
|
|
194
|
+
room = get_room(request)
|
|
195
|
+
data = await request.json()
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
await room.set_quality(data["quality"])
|
|
199
|
+
|
|
200
|
+
except (
|
|
201
|
+
KeyError,
|
|
202
|
+
ValueError,
|
|
203
|
+
) as error:
|
|
204
|
+
|
|
205
|
+
code = (
|
|
206
|
+
str(error)
|
|
207
|
+
if isinstance(error, ValueError)
|
|
208
|
+
else "INVALID_AUDIO_QUALITY"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
return json_error(code)
|
|
212
|
+
|
|
213
|
+
return web.json_response(room.state_or_not_playing())
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from aiohttp import web
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def get_queue(request):
|
|
5
|
+
|
|
6
|
+
room = request.app["state"].rooms.get(request.match_info["room_id"])
|
|
7
|
+
if room is None:
|
|
8
|
+
raise web.HTTPNotFound(text="ROOM_NOT_FOUND")
|
|
9
|
+
|
|
10
|
+
return web.json_response({
|
|
11
|
+
"room_id": room.id,
|
|
12
|
+
"current":
|
|
13
|
+
(
|
|
14
|
+
{
|
|
15
|
+
"id": room.current.id,
|
|
16
|
+
"title": room.current.title,
|
|
17
|
+
}
|
|
18
|
+
if room.current
|
|
19
|
+
else None
|
|
20
|
+
),
|
|
21
|
+
|
|
22
|
+
"queue": [
|
|
23
|
+
{
|
|
24
|
+
"id": item.id,
|
|
25
|
+
"title": item.title,
|
|
26
|
+
"path": item.path,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
for item in room.queue
|
|
30
|
+
],
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def add_track(request):
|
|
35
|
+
|
|
36
|
+
request.app["auth"]._verify(request)
|
|
37
|
+
|
|
38
|
+
room = request.app["state"].rooms.get(request.match_info["room_id"])
|
|
39
|
+
if room is None:
|
|
40
|
+
raise web.HTTPNotFound(text="ROOM_NOT_FOUND")
|
|
41
|
+
|
|
42
|
+
data = await request.json()
|
|
43
|
+
|
|
44
|
+
item = await room.add_track(
|
|
45
|
+
title=data["title"],
|
|
46
|
+
path=data["path"],
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return web.json_response(
|
|
50
|
+
{
|
|
51
|
+
"id": item.id,
|
|
52
|
+
"title": item.title,
|
|
53
|
+
},
|
|
54
|
+
status=201,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
async def remove_track(request):
|
|
59
|
+
|
|
60
|
+
request.app["auth"]._verify(request)
|
|
61
|
+
|
|
62
|
+
room = request.app["state"].rooms.get(request.match_info["room_id"])
|
|
63
|
+
if room is None:
|
|
64
|
+
raise web.HTTPNotFound(text="ROOM_NOT_FOUND")
|
|
65
|
+
|
|
66
|
+
track_id = request.match_info["track_id"]
|
|
67
|
+
before = len(room.queue)
|
|
68
|
+
|
|
69
|
+
room.queue = [
|
|
70
|
+
item
|
|
71
|
+
for item in room.queue
|
|
72
|
+
if item.id != track_id
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
if len(room.queue) == before:
|
|
76
|
+
raise web.HTTPNotFound(text="TRACK_NOT_FOUND")
|
|
77
|
+
|
|
78
|
+
return web.json_response(
|
|
79
|
+
{
|
|
80
|
+
"queue": [
|
|
81
|
+
{
|
|
82
|
+
"id": item.id,
|
|
83
|
+
"title": item.title,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
for item in room.queue
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
async def clear_queue(request):
|
|
93
|
+
request.app["auth"]._verify(request)
|
|
94
|
+
|
|
95
|
+
room = request.app["state"].rooms.get(request.match_info["room_id"])
|
|
96
|
+
if room is None:
|
|
97
|
+
raise web.HTTPNotFound(text="ROOM_NOT_FOUND")
|
|
98
|
+
|
|
99
|
+
room.queue.clear()
|
|
100
|
+
|
|
101
|
+
if room.current is None:
|
|
102
|
+
room.schedule_delete()
|
|
103
|
+
|
|
104
|
+
return web.json_response({
|
|
105
|
+
"queue": []
|
|
106
|
+
})
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from aiohttp import web
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def create_room(request):
|
|
5
|
+
request.app["auth"]._verify(request)
|
|
6
|
+
user_id = request.app["auth"]._get_auth_key()
|
|
7
|
+
|
|
8
|
+
data = await request.json()
|
|
9
|
+
|
|
10
|
+
name = data.get(
|
|
11
|
+
"name",
|
|
12
|
+
"Private Room",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
rooms = request.app["state"].rooms
|
|
16
|
+
|
|
17
|
+
room = await rooms.create(
|
|
18
|
+
name=name,
|
|
19
|
+
controllers={user_id},
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
return web.json_response(
|
|
23
|
+
room.state(),
|
|
24
|
+
status=201,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def get_room(request):
|
|
29
|
+
room_id = request.match_info["room_id"]
|
|
30
|
+
room = request.app["state"].rooms.get(room_id)
|
|
31
|
+
|
|
32
|
+
if room is None:
|
|
33
|
+
raise web.HTTPNotFound(text="ROOM_NOT_FOUND")
|
|
34
|
+
|
|
35
|
+
return web.json_response(room.state())
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def end_room(request):
|
|
39
|
+
|
|
40
|
+
request.app["auth"]._verify(request)
|
|
41
|
+
|
|
42
|
+
room_id = request.match_info["room_id"]
|
|
43
|
+
rooms = request.app["state"].rooms
|
|
44
|
+
room = rooms.get(room_id)
|
|
45
|
+
|
|
46
|
+
if room is None:
|
|
47
|
+
raise web.HTTPNotFound(text="ROOM_NOT_FOUND")
|
|
48
|
+
|
|
49
|
+
await rooms.remove(room_id)
|
|
50
|
+
|
|
51
|
+
return web.json_response({"status": "ended"})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from aiohttp import web
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def offer(request):
|
|
5
|
+
room_id = request.match_info["room_id"]
|
|
6
|
+
room = request.app["state"].rooms.get(room_id)
|
|
7
|
+
|
|
8
|
+
if room is None:
|
|
9
|
+
raise web.HTTPNotFound(text="Room not found")
|
|
10
|
+
|
|
11
|
+
data = await request.json()
|
|
12
|
+
peer = await room.add_peer()
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
answer = await room.offer(peer, data["sdp"])
|
|
16
|
+
except Exception:
|
|
17
|
+
await room.remove_peer(peer)
|
|
18
|
+
|
|
19
|
+
raise
|
|
20
|
+
|
|
21
|
+
return web.json_response({
|
|
22
|
+
"type": answer.type,
|
|
23
|
+
"sdp": answer.sdp,
|
|
24
|
+
})
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from pyLiveMusic._webrtc.backend import RoomManager
|
|
2
|
+
|
|
3
|
+
from pyLiveMusic._core._database.mongo._storage import _MongoStorage
|
|
4
|
+
from pyLiveMusic._core._database.mongo._repo import MongoRoomRepository, MongoUserRepository
|
|
5
|
+
from pyLiveMusic._core._database.memory._repo import MemoryRoomRepository, MemoryUserRepository
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class _core_state:
|
|
9
|
+
def __init__(self):
|
|
10
|
+
self.rooms = None
|
|
11
|
+
self.storage = None
|
|
12
|
+
self.users = None
|
|
13
|
+
self.room_repository = None
|
|
14
|
+
|
|
15
|
+
def MemoryState(self, storage):
|
|
16
|
+
self.storage = storage.storage
|
|
17
|
+
self.users = MemoryUserRepository(self.storage.users)
|
|
18
|
+
self.room_repository = MemoryRoomRepository(self.storage.rooms)
|
|
19
|
+
self.rooms = RoomManager(self.room_repository)
|
|
20
|
+
|
|
21
|
+
def MongoDBState(self, storage):
|
|
22
|
+
self.storage = _MongoStorage(
|
|
23
|
+
storage.db_url,
|
|
24
|
+
storage.db_name,
|
|
25
|
+
storage.user_collection,
|
|
26
|
+
storage.room_collection,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
self.users = MongoUserRepository(self.storage.users)
|
|
30
|
+
self.room_repository = MongoRoomRepository(self.storage.rooms)
|
|
31
|
+
self.rooms = RoomManager(self.room_repository)
|
|
32
|
+
|
|
33
|
+
def RedisState(self, storage):
|
|
34
|
+
raise NotImplementedError("Redis storage is not implemented yet")
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from pyLiveMusic._core._core_func._data_config import _core_state
|
|
2
|
+
|
|
3
|
+
from pyLiveMusic.storage import Memory, MongoDB, Redis
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class _data_state:
|
|
7
|
+
|
|
8
|
+
def __init__(self, storage):
|
|
9
|
+
state_container = _core_state()
|
|
10
|
+
|
|
11
|
+
if isinstance(storage, Memory):
|
|
12
|
+
state_container.MemoryState(storage)
|
|
13
|
+
|
|
14
|
+
elif isinstance(storage, MongoDB):
|
|
15
|
+
state_container.MongoDBState(storage)
|
|
16
|
+
|
|
17
|
+
elif isinstance(storage, Redis):
|
|
18
|
+
state_container.RedisState(storage)
|
|
19
|
+
|
|
20
|
+
else:
|
|
21
|
+
raise TypeError("Unsupported storage type")
|
|
22
|
+
|
|
23
|
+
self.rooms = state_container.rooms
|
|
24
|
+
self.storage = state_container.storage
|
|
25
|
+
self.users = state_container.users
|
|
26
|
+
self.room_repository = state_container.room_repository
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
class MemoryRoomRepository:
|
|
2
|
+
|
|
3
|
+
def __init__(self, rooms):
|
|
4
|
+
self.rooms = rooms
|
|
5
|
+
|
|
6
|
+
async def create(self, room_id, name, controllers):
|
|
7
|
+
document = {
|
|
8
|
+
"_id": room_id,
|
|
9
|
+
"name": name,
|
|
10
|
+
"controllers": list(controllers),
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
self.rooms[room_id] = document
|
|
14
|
+
return document
|
|
15
|
+
|
|
16
|
+
async def get(self, room_id):
|
|
17
|
+
return self.rooms.get(room_id)
|
|
18
|
+
|
|
19
|
+
async def list(self):
|
|
20
|
+
return list(self.rooms.values())
|
|
21
|
+
|
|
22
|
+
async def delete(self, room_id):
|
|
23
|
+
self.rooms.pop(room_id, None)
|
|
24
|
+
|
|
25
|
+
class MemoryUserRepository:
|
|
26
|
+
|
|
27
|
+
def __init__(self, users):
|
|
28
|
+
self.users = users
|
|
29
|
+
|
|
30
|
+
async def get(self, user_id):
|
|
31
|
+
return self.users.get(user_id)
|
|
32
|
+
|
|
33
|
+
async def create(self, user_id, username):
|
|
34
|
+
document = {
|
|
35
|
+
"_id": user_id,
|
|
36
|
+
"username": username
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
self.users[user_id] = document
|
|
40
|
+
|
|
41
|
+
return document
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class _MemoryStorage:
|
|
2
|
+
def __init__(self):
|
|
3
|
+
self.users = {}
|
|
4
|
+
self.rooms = {}
|
|
5
|
+
|
|
6
|
+
async def connect(self):
|
|
7
|
+
print("Memory storage initialized")
|
|
8
|
+
|
|
9
|
+
async def close(self):
|
|
10
|
+
self.users.clear()
|
|
11
|
+
self.rooms.clear()
|
|
12
|
+
|
|
13
|
+
print("Memory storage cleared")
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
class MongoRoomRepository:
|
|
2
|
+
|
|
3
|
+
def __init__(self, collection):
|
|
4
|
+
self.collection = collection
|
|
5
|
+
|
|
6
|
+
async def create(self, room_id, name, controllers):
|
|
7
|
+
document = {
|
|
8
|
+
"_id": room_id,
|
|
9
|
+
"name": name,
|
|
10
|
+
"controllers": list(controllers),
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
await self.collection.insert_one(document)
|
|
14
|
+
return document
|
|
15
|
+
|
|
16
|
+
async def get(self, room_id):
|
|
17
|
+
return await self.collection.find_one({"_id": room_id})
|
|
18
|
+
|
|
19
|
+
async def list(self):
|
|
20
|
+
return [document async for document in self.collection.find({})]
|
|
21
|
+
|
|
22
|
+
async def delete(self, room_id):
|
|
23
|
+
await self.collection.delete_one({"_id": room_id})
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class MongoUserRepository:
|
|
27
|
+
|
|
28
|
+
def __init__(self, collection):
|
|
29
|
+
|
|
30
|
+
self.collection = collection
|
|
31
|
+
|
|
32
|
+
async def get(self, user_id):
|
|
33
|
+
return await self.collection.find_one({"_id": user_id})
|
|
34
|
+
|
|
35
|
+
async def create(
|
|
36
|
+
self,
|
|
37
|
+
user_id,
|
|
38
|
+
username,
|
|
39
|
+
):
|
|
40
|
+
|
|
41
|
+
document = {"_id": user_id, "username": username}
|
|
42
|
+
|
|
43
|
+
await self.collection.insert_one(document)
|
|
44
|
+
|
|
45
|
+
return document
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class _MongoStorage:
|
|
2
|
+
from pymongo import AsyncMongoClient
|
|
3
|
+
|
|
4
|
+
def __init__(
|
|
5
|
+
self,
|
|
6
|
+
db_url: str,
|
|
7
|
+
db_name: str,
|
|
8
|
+
user_collection: str,
|
|
9
|
+
room_collection: str
|
|
10
|
+
):
|
|
11
|
+
try:
|
|
12
|
+
from pymongo import AsyncMongoClient
|
|
13
|
+
except ImportError as exc:
|
|
14
|
+
raise ImportError(
|
|
15
|
+
"To use the MongoDB, you must install pyLiveMusic via "
|
|
16
|
+
"pip install 'pyLiveMusic[mongo]'"
|
|
17
|
+
) from exc
|
|
18
|
+
self.client = AsyncMongoClient(db_url)
|
|
19
|
+
self.storage = self.client[db_name]
|
|
20
|
+
|
|
21
|
+
self.users = self.storage[user_collection]
|
|
22
|
+
self.rooms = self.storage[room_collection]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def connect(self):
|
|
26
|
+
await self.client.admin.command("ping")
|
|
27
|
+
print("MongoDB connected")
|
|
28
|
+
|
|
29
|
+
async def close(self):
|
|
30
|
+
await self.client.close()
|
|
31
|
+
print("MongoDB disconnected")
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Live Stream</title>
|
|
7
|
+
</head>
|
|
8
|
+
|
|
9
|
+
<body>
|
|
10
|
+
<audio id="audio" controls autoplay playsinline preload="auto" controlslist="nodownload noplaybackrate"></audio>
|
|
11
|
+
<br>
|
|
12
|
+
<label for="volume">Volume</label>
|
|
13
|
+
<input id="volume" type="range" min="0" max="1" step="0.01" value="1">
|
|
14
|
+
<span id="status">CONNECTING...</span>
|
|
15
|
+
<script src="/static/audio.js?v=3"></script>
|
|
16
|
+
</body>
|
|
17
|
+
|
|
18
|
+
</html>
|