Python

what are you looking at

fork
loading
Files
  • main.py
  • ws-audio
  • customogg.py
  • deco.py
  • form.html
  • requirements.txt
  • room.html
  • shelter.ogg
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
from aiohttp import web
from customogg import OggFile, stringint
from asyncio import sleep
from time import time
import aiohttp
from aiohttp_session import setup, new_session
from aiohttp_session.cookie_storage import EncryptedCookieStorage
from cryptography import fernet
from motor.motor_asyncio import AsyncIOMotorClient
from os import getenv
import base64
import json
from hashlib import sha1

from deco import authenticate

secrets = __import__('secrets') # repl workaround

routes = web.RouteTableDef()

with open('shelter.ogg', 'rb') as f:
	shelter = OggFile(f.read())

@routes.get('/example')
@authenticate
async def index(request):
	response = web.StreamResponse(
		status=200,
		reason='OK',
		headers={'Content-Type': 'application/ogg', 'X-Accel-Buffering': 'no'}
  )
	response.enable_chunked_encoding()
	await response.prepare(request)

	start_time = time()

	for page in shelter.pages:
		await response.write(page.data)
		await sleep(page.end_second-(time()-start_time))

	return response

@routes.get('/room/{room_id:\d+}')
@authenticate
async def get_room(request):
	room_id = int(request.match_info['room_id'])
	
	if room_id == 1:
		return web.Response(text=open('room.html').read(), content_type='text/html')
	else:
		return web.Response(text='uh oh')

@routes.get('/roomws/{room_id:\d+}')
async def add_room_listener(request):
	room_id = int(request.match_info['room_id'])
	ws = web.WebSocketResponse()
	await ws.prepare(request)

	if not app['rooms'].get(room_id):
		app['rooms'][room_id] = []
		app['songs'][room_id] = [shelter]
		app['page'][room_id] = 2
		app['starts'][room_id] = time()
	
	i = len(app['rooms'][room_id])
	app['rooms'][room_id].append(ws) # ;)
	await ws.write_bytes(b''.join(app['songs'][room_id][0].pages[:2]))

	async for msg in ws:
		if msg.type == aiohttp.WSMsgType.TEXT:
			if msg.data == 'close':
				await ws.close()
		elif msg.type == aiohttp.WSMsgType.ERROR:
			print('ws connection closed with exception %s' %
                  ws.exception())
	
	app['rooms'][room_id].pop(i)
	
	return ws

@routes.get('/dist/wsaudio.js') # https://github.com/Ivan-Feofanov/ws-audio-api
@authenticate
async def get_audiojs(request):
	return web.Response(text=open('ws-audio/wsaudio.js').read(), content_type='application/js')

		

@routes.get('/')
async def homepage(request):
	return web.Response(text='hello')

@routes.get('/login')
async def login_form(request):
	return web.Response(text=open('form.html').read(), content_type='text/html')

# login processing
@routes.post('/login')
async def login(request):
	session = await new_session(request)
	data = await request.post()
	db = request.app['db']

	possible = await db['users'].find_one({'username': data['username']})

	if data['new_account'] == 'true' and not possible:
		await db['users'].insert_one({'username': data.get('username'), 'password': data.get('password')})
		session['username'] = data.get('username')
		return web.Response(text='Done!')

	server_salts = [salt['server'] for salt in app['salts'][data['client_salt']] if salt['iat']+60 > time()]

	if server_salts == []:
		return web.Response(text='Not Logged In.')
	elif not possible:
		return web.Response(text='Not Logged In.')
	elif any([sha1((possible['password']+data['client_salt']+server_salt).encode('utf-8')).hexdigest() == data['password'] for server_salt in server_salts]):
		session['username'] = data['username']
		return web.Response(text='Done!')
	else:
		return web.Response(text='Not Logged In.')
		

@routes.post('/salt')
async def salted(request):
	data = await request.post()

	client_salt = data['client_salt']
	if not client_salt:
		return web.Response(text='NOPE!')
	
	salt = secrets.token_urlsafe(15).replace('/','') # i'm so quirky
	if not request.app['salts'].get(client_salt):
		request.app['salts'][client_salt] = []
	
	request.app['salts'][client_salt].append({'server':salt,'iat':int(time())})

	return web.Response(text=json.dumps({'salt':salt}), content_type='application/json')

# app creation
app = web.Application()

# database creation
mongo = AsyncIOMotorClient(getenv('DB_URL'))
db = mongo['nani']
app['db'] = db

async def mongo_cleanup(app):
	mongo.close()

app.on_cleanup.append(mongo_cleanup)

# salts for login
app['salts'] = {}

# responses for smoother streaming
async def ws_sendloop(app):
	while True:
		async for room_id in app['rooms'].keys():
			next_page = app['songs'][room_id][0].pages[app['page'][room_id]]
			past_page_end = app['songs'][room_id][0].pages[app['page'][room_id]-1].end_second

			if time()-app['starts'][room_id] > past_page_end:
				async for listener in app['rooms'][room_id]:
					listener.write(next_page.data)
			
			if len(app['songs'][room_id][0].pages) < app['page'][room_id] + 2:
				if len(app['songs'][room_id]) == 1:
					# cleanup
					async for listener in app['rooms'][room_id]:
						listener.close()
					del app['rooms'][room_id]
					continue
				else:
					app['songs'][room_id] = app['songs'][room_id][1:]
					app['page'][room_id] = 2
					async for listener in app['rooms'][room_id]:
						await listener.write_bytes(b''.join(app['songs'][room_id][0].pages[:2]))
					app['starts'][room_id] = time()
					continue
			
			app['page'][room_id] += 1
					

app['rooms'] = {} # {room_id: [listeners]}
app['songs'] = {} # {room_id: [song_queue]}
app['page'] = {} # {room_id: page_number}
app['starts'] = {} # {room_id: start_time}

async def start_loop_task(app):
  app['wsloop'] = app.loop.create_task(ws_sendloop(app))

async def clean_loop_task(app):
	app['wsloop'].cancel()
	await app['wsloop']

app.on_startup.append(start_loop_task)
app.on_cleanup.append(clean_loop_task)

# secret key
fernet_key = fernet.Fernet.generate_key()
secret_key = base64.urlsafe_b64decode(fernet_key)
del fernet_key

# session setup
setup(app, EncryptedCookieStorage(secret_key, cookie_name='StorageCookie', max_age=24*60*60))  # storage lasts for a day

# add routes
app.add_routes(routes)

# run the app
web.run_app(app)