fourth commit

This commit is contained in:
iou1name 2020-06-15 09:57:23 -04:00
parent 3282d38821
commit ab6324bf32
6 changed files with 186 additions and 24 deletions

View File

@ -3,7 +3,7 @@ A music streaming application.
## Requirements
Python 3.8+
Python packages: `gunicorn aiohttp aiohttp_jinja2 uvloop mutagen`
Python packages: `gunicorn aiohttp aiohttp_jinja2 uvloop mutagen asyncpg`
## Install
```

22
events.py Normal file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python3
"""
WebSocket events.
"""
import types
async def select(request, ws, data):
"""Retrieve information about an artist, album or track."""
if data.get('type') == 'artist':
async with request.app['pool'].acquire() as conn:
albums = await conn.fetch(
"SELECT DISTINCT album, date FROM track "
"WHERE albumartist = $1 ORDER BY date ASC",
data.get('artist', ''))
albums = [record['album'] for record in albums]
ret = {'event': 'albums', 'ok': True, 'data': albums}
await ws.send_json(ret)
events = {}
for obj in dir():
if type(locals()[obj]) == types.FunctionType:
events[locals()[obj].__name__] = locals()[obj]

View File

@ -2,6 +2,7 @@
"""
A music streaming application.
"""
import json
import asyncio
import aiohttp
@ -13,6 +14,7 @@ import uvloop
import asyncpg
import config
import events
import database
import buckler_aiohttp
@ -23,9 +25,10 @@ routes = web.RouteTableDef()
async def index(request):
"""The index page."""
async with request.app['pool'].acquire() as conn:
artists = await conn.execute(
artists = await conn.fetch(
"SELECT DISTINCT albumartist FROM track ORDER BY albumartist ASC")
return render_template('index.html', request, {})
artists = [record['albumartist'] for record in artists]
return render_template('index.html', request, locals())
@routes.get('/ws', name='ws')
@ -41,10 +44,15 @@ async def websocket_handler(request):
if msg.type != WSMsgType.TEXT:
break
if msg.data == "ping":
print('ping')
await ws.send_str("pong")
try:
data = json.loads(msg.data)
except json.JSONDecodeError:
break
event = data.get('event')
if not event or event not in events.events.keys():
break
await events.events[event](request, ws, data.get('data'))
await ws.close()
return ws

View File

@ -0,0 +1,46 @@
body {
height: 100vh;
margin: 0;
padding: 8px;
box-sizing: border-box;
}
main {
height: 100%;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto 1fr auto;
grid-template-areas:
"h h h"
"a b c"
"f f f";
}
#header {
grid-area: h;
}
.list {
height: 100%;
width: 100%;
}
#artistListContainer {
grid-area: a;
}
#albumListContainer {
grid-area: b;
}
#trackListContainer {
grid-area: c;
}
#playerContainer {
grid-area: f;
height: auto;
max-height: 30vh;
display: flex;
justify-content: center;
}

View File

@ -12,31 +12,90 @@ function init_websocket() {
socket.onclose = onclose;
socket.onerror = onerror;
socket.events = {};
socket.events['albums'] = albums_recv;
return socket;
}
function send_event(event_title, data) {
data = JSON.stringify({'event': event_title, 'data': data});
if (socket.readyState == 0) {
if (this.readyState == 0) {
console.log("Socket is still opening!");
return;
}
socket.send(data);
this.send(data);
}
function onmessage(e) {
console.log(e);
function onmessage(event) {
let data;
let event_title;
try {
data = JSON.parse(event.data);
} catch(err) {
// not JSON
console.log(err);
console.log(event);
throw new Error("Error decoding JSON");
return;
}
if (!data.ok) {
throw new Error("Socket error: event = " + event_title + ", error = " + data.error);
}
try {
event_title = data.event;
data = data.data;
} catch(err) {
// not proper event
console.log(err);
console.log(event);
throw new Error("Event malformed");
return;
}
if (socket.events[event_title] === undefined) {
console.log("Unknown socket event: " + event_title);
return;
}
socket.events[event_title](data);
}
function onclose(e) {
if (e.wasClean) { return; } // no need to reconnect
console.log(e);
async function onclose(event) {
if (event.wasClean) { return; } // no need to reconnect
console.log(event);
console.log('Websocket lost connection to server. Re-trying...');
//socket = init_websocket();
//await sleep(5000);
await sleep(3000);
socket = init_websocket();
}
function onerror(e) {
console.log("Websocket error!")
console.log(e);
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function onerror(event) {
console.log("Websocket error!")
console.log(event);
}
/* Websocket receive */
function albums_recv(data) {
let albums_list = document.querySelector('#albumList');
while (albums_list.firstChild) {
albums_list.removeChild(albums_list.lastChild);
}
for (let album of data) {
let option = document.createElement('option');
option.value = album;
option.innerText = album;
albums_list.appendChild(option);
}
}
/* Websocket send */
function select_artist(option) {
let artist = option.value;
let data = {'type': 'artist', 'artist': artist};
socket.send_event('select', data);
}
/* DOM */

View File

@ -7,15 +7,42 @@
<script type="text/javascript" src="/static/scorch.js"></script>
<script>window.onload = load;</script>
<meta name="viewport" content="width=device-width, initial-scale=0.8">
<meta name="description" content="A music streaming service.">
<meta name="description" content="A music streaming platform.">
</head>
<body>
<header>
<h1>Scorch</h1>
</header>
<main>
<button onclick="socket.send('test')">Test</button>
<audio controls></audio>
<div id="header">
<h1>Scorch</h1>
</div>
<div id="artistListContainer" class="list">
<select id="artistList" size="2" class="list" onchange="select_artist(this)">
{% for artist in artists %}
<option value="{{ artist }}">{{ artist }}</option>
{% endfor %}
</select>
</div>
<div id="albumListContainer" class="list">
<select id="albumList" size="2" class="list" onchange="select_album(this)">
</select>
</div>
<div id="trackListContainer" class="list">
<select id="trackList" size="2" class="list" onchange="select_track(this)">
</select>
</div>
<div id="playerContainer">
<span id="albumCover"><img src=""></span>
<span id="playerControls">
<h4><span id="nowPlayingArtist"></span> - <span id="nowPlayingAlbum"></span></h4>
<h3 id="nowPlayingTitle"></h3>
<audio id="player" controls>
<source id="stream" src="" type="audio/ogg">
</audio>
<div>
<input type="checkbox" name="shuffle" id="shuffle" checked="true">
<label for="shuffle">Shuffle</label>
</div>
</span>
</div>
</main>
</body>
</html>