Aleksmorshen commited on
Commit
f79000e
·
verified ·
1 Parent(s): 7b1743c

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +236 -0
app.py ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, redirect, url_for, jsonify
2
+ import random
3
+ import string
4
+ import json
5
+ from flask_socketio import SocketIO, join_room, leave_room
6
+
7
+ app = Flask(__name__)
8
+ app.config['SECRET_KEY'] = 'your-secret-key-here'
9
+ socketio = SocketIO(app)
10
+
11
+ # Хранилище комнат и пользователей
12
+ rooms = {}
13
+
14
+ # Генерация 15-значного токена
15
+ def generate_token():
16
+ return ''.join(random.choices(string.ascii_letters + string.digits, k=15))
17
+
18
+ # Главная страница
19
+ @app.route('/')
20
+ def index():
21
+ return render_template('index.html')
22
+
23
+ # Создание новой комнаты
24
+ @app.route('/create', methods=['POST'])
25
+ def create_room():
26
+ token = generate_token()
27
+ rooms[token] = {
28
+ 'users': [],
29
+ 'max_users': 5
30
+ }
31
+ return redirect(url_for('room', token=token))
32
+
33
+ # Вход в существующую комнату
34
+ @app.route('/join', methods=['POST'])
35
+ def join():
36
+ token = request.form.get('token')
37
+ if token in rooms and len(rooms[token]['users']) < rooms[token]['max_users']:
38
+ return redirect(url_for('room', token=token))
39
+ return "Комната не найдена или переполнена", 404
40
+
41
+ # Страница комнаты
42
+ @app.route('/room/<token>')
43
+ def room(token):
44
+ if token not in rooms:
45
+ return redirect(url_for('index'))
46
+ return render_template('room.html', token=token)
47
+
48
+ # WebSocket события
49
+ @socketio.on('join')
50
+ def handle_join(data):
51
+ token = data['token']
52
+ username = data['username']
53
+
54
+ if token in rooms and len(rooms[token]['users']) < rooms[token]['max_users']:
55
+ join_room(token)
56
+ rooms[token]['users'].append(username)
57
+ socketio.emit('user_joined', {
58
+ 'username': username,
59
+ 'users': rooms[token]['users']
60
+ }, room=token)
61
+
62
+ @socketio.on('leave')
63
+ def handle_leave(data):
64
+ token = data['token']
65
+ username = data['username']
66
+
67
+ if token in rooms and username in rooms[token]['users']:
68
+ leave_room(token)
69
+ rooms[token]['users'].remove(username)
70
+ socketio.emit('user_left', {
71
+ 'username': username,
72
+ 'users': rooms[token]['users']
73
+ }, room=token)
74
+
75
+ if len(rooms[token]['users']) == 0:
76
+ del rooms[token]
77
+
78
+ @socketio.on('signal')
79
+ def handle_signal(data):
80
+ socketio.emit('signal', data, room=data['token'], skip_sid=request.sid)
81
+
82
+ # Шаблоны HTML
83
+ @app.route('/templates/index.html')
84
+ def index_template():
85
+ return '''<!DOCTYPE html>
86
+ <html>
87
+ <head>
88
+ <title>Видеоконференция</title>
89
+ <style>
90
+ body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
91
+ button, input { padding: 10px; margin: 5px; }
92
+ </style>
93
+ </head>
94
+ <body>
95
+ <h1>Видеоконференция</h1>
96
+ <form action="/create" method="post">
97
+ <button type="submit">Создать комнату</button>
98
+ </form>
99
+ <form action="/join" method="post">
100
+ <input type="text" name="token" placeholder="Введите токен комнаты" required>
101
+ <button type="submit">Войти в комнату</button>
102
+ </form>
103
+ </body>
104
+ </html>'''
105
+
106
+ @app.route('/templates/room.html')
107
+ def room_template():
108
+ return '''<!DOCTYPE html>
109
+ <html>
110
+ <head>
111
+ <title>Комната {{ token }}</title>
112
+ <style>
113
+ .video-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 10px; padding: 20px; }
114
+ video { width: 100%; background: black; }
115
+ #users { margin: 10px; }
116
+ </style>
117
+ </head>
118
+ <body>
119
+ <h1>Комната: {{ token }}</h1>
120
+ <div id="users"></div>
121
+ <div class="video-grid" id="video-grid"></div>
122
+ <button onclick="leaveRoom()">Покинуть комнату</button>
123
+
124
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.1/socket.io.js"></script>
125
+ <script>
126
+ const socket = io.connect('http://localhost:7860');
127
+ const token = '{{ token }}';
128
+ let localStream;
129
+ const peers = {};
130
+ const username = 'User_' + Math.random().toString(36).substr(2, 5);
131
+
132
+ // Получение видео/аудио потока
133
+ navigator.mediaDevices.getUserMedia({ video: true, audio: true })
134
+ .then(stream => {
135
+ localStream = stream;
136
+ addVideoStream(stream, username, true);
137
+ socket.emit('join', { token: token, username: username });
138
+ });
139
+
140
+ // Добавление видео в сетку
141
+ function addVideoStream(stream, user, muted = false) {
142
+ const video = document.createElement('video');
143
+ video.srcObject = stream;
144
+ video.addEventListener('loadedmetadata', () => {
145
+ video.play();
146
+ });
147
+ if (muted) video.muted = true;
148
+ video.dataset.user = user;
149
+ document.getElementById('video-grid').appendChild(video);
150
+ }
151
+
152
+ // Создание peer соединения
153
+ function connectToNewUser(user) {
154
+ const peer = new RTCPeerConnection();
155
+ peers[user] = peer;
156
+
157
+ localStream.getTracks().forEach(track => {
158
+ peer.addTrack(track, localStream);
159
+ });
160
+
161
+ peer.ontrack = event => {
162
+ addVideoStream(event.streams[0], user);
163
+ };
164
+
165
+ peer.onicecandidate = event => {
166
+ if (event.candidate) {
167
+ socket.emit('signal', { token: token, to: user, signal: { type: 'candidate', candidate: event.candidate } });
168
+ }
169
+ };
170
+
171
+ peer.createOffer()
172
+ .then(offer => peer.setLocalDescription(offer))
173
+ .then(() => {
174
+ socket.emit('signal', { token: token, to: user, signal: peer.localDescription });
175
+ });
176
+ }
177
+
178
+ // Обработка сигналов
179
+ socket.on('signal', data => {
180
+ const peer = peers[data.from] || new RTCPeerConnection();
181
+ peers[data.from] = peer;
182
+
183
+ if (data.signal.type === 'offer') {
184
+ peer.setRemoteDescription(new RTCSessionDescription(data.signal));
185
+ localStream.getTracks().forEach(track => peer.addTrack(track, localStream));
186
+
187
+ peer.ontrack = event => {
188
+ addVideoStream(event.streams[0], data.from);
189
+ };
190
+
191
+ peer.onicecandidate = event => {
192
+ if (event.candidate) {
193
+ socket.emit('signal', { token: token, to: data.from, signal: { type: 'candidate', candidate: event.candidate } });
194
+ }
195
+ };
196
+
197
+ peer.createAnswer()
198
+ .then(answer => peer.setLocalDescription(answer))
199
+ .then(() => {
200
+ socket.emit('signal', { token: token, to: data.from, signal: peer.localDescription });
201
+ });
202
+ } else if (data.signal.type === 'answer') {
203
+ peer.setRemoteDescription(new RTCSessionDescription(data.signal));
204
+ } else if (data.signal.type === 'candidate') {
205
+ peer.addIceCandidate(new RTCIceCandidate(data.signal.candidate));
206
+ }
207
+ });
208
+
209
+ // Обновление списка пользователей
210
+ socket.on('user_joined', data => {
211
+ document.getElementById('users').innerText = 'Пользователи: ' + data.users.join(', ');
212
+ if (data.username !== username) {
213
+ connectToNewUser(data.username);
214
+ }
215
+ });
216
+
217
+ socket.on('user_left', data => {
218
+ document.getElementById('users').innerText = 'Пользователи: ' + data.users.join(', ');
219
+ if (peers[data.username]) {
220
+ peers[data.username].close();
221
+ delete peers[data.username];
222
+ document.querySelector(`video[data-user="${data.username}"]`).remove();
223
+ }
224
+ });
225
+
226
+ function leaveRoom() {
227
+ socket.emit('leave', { token: token, username: username });
228
+ localStream.getTracks().forEach(track => track.stop());
229
+ window.location.href = '/';
230
+ }
231
+ </script>
232
+ </body>
233
+ </html>'''
234
+
235
+ if __name__ == '__main__':
236
+ socketio.run(app, host='0.0.0.0', port=7860, debug=True)