wip web playback
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
from hashlib import blake2b
|
||||
from hmac import compare_digest
|
||||
from typing import List
|
||||
import os
|
||||
|
||||
|
||||
def encode(args: List, uri: str) -> str:
|
||||
"""
|
||||
Encode a request and cryptographically sign it. This serves two purposes:
|
||||
First, it enables the handler that receives the request to verify that the
|
||||
request was meant for it, preventing various routing and relay-induced bugs.
|
||||
Second, it ensures the request wasn't corrutped or tampered with during
|
||||
transmission.
|
||||
|
||||
Args:
|
||||
args (List): a list of parameters to pass along with the request.
|
||||
uri (String): the URI of the intended handler for the request.
|
||||
|
||||
Returns:
|
||||
String: A cryptographically signed request.
|
||||
"""
|
||||
return sign(uri + '\0' + '\0'.join(args))
|
||||
|
||||
|
||||
def sign(request):
|
||||
"""
|
||||
Sign a request with a cryptographic hash. Returns the hex digest.
|
||||
"""
|
||||
h = blake2b(digest_size=16, key=bytes(os.environ['SECRET_KEY'].encode()))
|
||||
h.update(request.encode())
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def verify(request, digest):
|
||||
return compare_digest(request, digest)
|
||||
|
||||
|
||||
def url():
|
||||
return f"http://{os.environ['HOST']}:{os.environ['PORT']}"
|
||||
@@ -0,0 +1,100 @@
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import bottle
|
||||
from bottle import HTTPResponse, template, static_file
|
||||
from bottle.ext import sqlalchemy
|
||||
|
||||
import groove.db
|
||||
from groove.auth import is_authenticated
|
||||
from groove.db.manager import database_manager
|
||||
from groove.playlist import Playlist
|
||||
from groove.webserver import requests
|
||||
|
||||
from groove.exceptions import APIHandlingException
|
||||
|
||||
server = bottle.Bottle()
|
||||
|
||||
|
||||
def start(host: str, port: int, debug: bool) -> None: # pragma: no cover
|
||||
"""
|
||||
Start the Bottle app.
|
||||
"""
|
||||
logging.debug(f"Configuring sqllite using {os.environ.get('DATABASE_PATH')}")
|
||||
|
||||
with database_manager() as manager:
|
||||
server.install(sqlalchemy.Plugin(
|
||||
manager.engine,
|
||||
groove.db.metadata,
|
||||
keyword='db',
|
||||
create=True,
|
||||
commit=True,
|
||||
))
|
||||
logging.debug(f"Configuring webserver with host={host}, port={port}, debug={debug}")
|
||||
server.run(
|
||||
host=os.getenv('HOST', host),
|
||||
port=os.getenv('PORT', port),
|
||||
debug=debug,
|
||||
server='paste',
|
||||
quiet=True
|
||||
)
|
||||
|
||||
|
||||
@server.route('/')
|
||||
def index():
|
||||
return "Groovy."
|
||||
|
||||
|
||||
@server.route('/build')
|
||||
@bottle.auth_basic(is_authenticated)
|
||||
def build():
|
||||
return "Authenticated. Groovy."
|
||||
|
||||
|
||||
@bottle.auth_basic(is_authenticated)
|
||||
@server.route('/build/search/playlist')
|
||||
def search_playlist(slug, db):
|
||||
playlist = Playlist(slug=slug, session=db, create_ok=False)
|
||||
response = json.dumps(playlist.as_dict)
|
||||
logging.debug(response)
|
||||
return HTTPResponse(status=200, content_type='application/json', body=response)
|
||||
|
||||
|
||||
@server.route('/track/<request>/<track_id>')
|
||||
def serve_track(request, track_id, db):
|
||||
|
||||
expected = requests.encode([track_id], '/track')
|
||||
if not requests.verify(request, expected):
|
||||
return HTTPResponse(status=404, body="Not found")
|
||||
|
||||
track_id = int(track_id)
|
||||
track = db.query(groove.db.track).filter(
|
||||
groove.db.track.c.id == track_id
|
||||
).one()
|
||||
|
||||
path = Path(os.environ['MEDIA_ROOT']) / Path(track['relpath'])
|
||||
return static_file(path.name, root=path.parent)
|
||||
|
||||
|
||||
@server.route('/playlist/<slug>')
|
||||
def serve_playlist(slug, db):
|
||||
"""
|
||||
Retrieve a playlist and its entries by a slug.
|
||||
"""
|
||||
logging.debug(f"Looking up playlist: {slug}...")
|
||||
playlist = Playlist(slug=slug, session=db, create_ok=False).load()
|
||||
if not playlist.record:
|
||||
logging.debug(f"Playist {slug} doesn't exist.")
|
||||
return HTTPResponse(status=404, body="Not found")
|
||||
logging.debug(f"Loaded {playlist.record}")
|
||||
logging.debug(playlist.as_dict['entries'])
|
||||
|
||||
args = [
|
||||
(requests.encode([str(entry['track_id'])], uri='/track'), entry['track_id'])
|
||||
for entry in playlist.as_dict['entries']
|
||||
]
|
||||
|
||||
template_path = Path(os.environ['TEMPLATE_PATH']) / Path('playlist.tpl')
|
||||
return template(str(template_path), url=requests.url(), playlist=playlist.as_dict, args=args)
|
||||
Reference in New Issue
Block a user