Initial commit

This commit is contained in:
Martin Müller 2017-07-13 22:38:05 +02:00
commit 3391c7e076
4 changed files with 231 additions and 0 deletions

45
soundboard.py Normal file
View File

@ -0,0 +1,45 @@
#!/usr/bin/env python3
import os
import subprocess
from flask import Flask, render_template, request, redirect, url_for
path = "/home/pi/sounds"
app = Flask(__name__)
app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True
@app.route("/")
@app.route("/play/<sound>")
@app.route("/say/", methods=["POST"])
@app.route("/say/<text>")
def index(sound=None, text=None, video=None):
sounds = sorted(os.listdir(path))
if sound is not None and sound in sounds:
subprocess.Popen(["omxplayer", os.path.join(path, sound)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
if text is None:
text = request.form.get("text")
if text is not None:
voice = request.form.get("voice", default="")
voice = voice if voice.strip() != "" else "DE"
speed = request.form.get("speed", default="")
speed = speed if speed.strip() != "" else "160"
pitch = request.form.get("pitch", default="")
pitch = pitch if pitch.strip() != "" else "50"
subprocess.Popen(["espeak", "-v", voice, "-s", speed, "-p", pitch, text], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return redirect("/")
video = request.args.get("video")
if video is not None:
url = subprocess.check_output(["youtube-dl", "-g", "-f", "mp4", video]).decode()
subprocess.Popen(["omxplayer", url.split("\n")[1]])
subprocess.Popen(["omxplayer", "-b", url.split("\n")[0]])
return render_template("index.html", sounds=sounds)

91
static/main.css Normal file
View File

@ -0,0 +1,91 @@
htl, body {
margin: 0;
}
html {
font-family: Helvetica, "Open Sans", sans-serif;
}
body {
background-color: #333;
color: #eee;
}
a {
text-decoration: none;
}
h1 {
text-align: center;
}
form {
text-align: center;
}
input {
background-color: #666;
border: 1px solid #999;
border-radius: 5px;
color: #eee;
margin: 0;
padding: 5px;
vertical-align: middle;
}
label {
vertical-align: middle;
}
nav {
text-align: center;
margin: 1em;
}
nav a {
background-color: #eee;
border: 1px solid #000;
border-radius: 5px;
color: #333;
display: inline-block;
padding: 1em;
}
content {
position: absolute;
width: 100%;
}
section {
position: absolute;
left: 0;
text-align: center;
top: 0;
width: 100%;
}
.sound {
display: inline-block;
}
.sound a {
background-color: #ff4136;
border: 1px solid #000;
border-radius: 50px;
box-shadow: inset -5px -5px 5px rgba(0, 0, 0, 0.6);
color: #eee;
display: inline-block;
font-size: 0.6em;
height: 100px;
line-height: 100px;
margin: 10px;
overflow: hidden;
text-align: center;
text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;
width: 100px;
}
.sound a:hover {
background-color: #ff4136;
box-shadow: inset 5px 5px 5px rgba(0, 0, 0, 0.6);
}

53
static/main.js Normal file
View File

@ -0,0 +1,53 @@
function ready(fn) {
if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading") {
fn();
} else {
document.addEventListener("DOMContentLoaded", fn);
}
}
ready(function() {
hideSections();
var sections = document.querySelectorAll("section");
var nav = document.querySelectorAll("nav a");
sections[0].style.display = "block";
nav.forEach(function(item) {
item.onclick = function(e) {
e.preventDefault();
var target = e.target.href.split("#");
var id = target[target.length - 1];
hideSections();
document.querySelector("#" + id).style.display = "block";
};
});
var searchfield = document.querySelector("#search");
searchfield.addEventListener("keypress", function() {
console.log("Search!");
var buttons = document.querySelectorAll(".sound");
buttons.forEach(function(item) {
var name = item.firstChild.innerHTML;
if (name.indexOf(searchfield.value) === -1) {
item.style.display = "none";
}
});
});
});
function hideSections() {
var sections = document.querySelectorAll("section");
sections.forEach(function(item, i) {
item.style.display = "none";
});
}

42
templates/index.html Normal file
View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>WIAI Soundboard</title>
<link rel="stylesheet" href="/static/main.css" />
</head>
<body>
<nav>
<a href="#sounds">Sounds</a>
<a href="#youtube">YouTube</a>
<a href="#voice">Voice</a>
</nav>
<content>
<section id="sounds">
<div><input type="text" id="search" /></div>
{% for sound in sounds %}
<div class="sound"><a href="{{ '/play/' + sound }}">{{ sound.split('.', 1)[0] }}</a></div>
{% endfor %}
</section>
<section id="youtube">
<form action="/" method="GET">
<label for="video">YouTube URL</label>
<input type="text" name="video" />
<input type="submit" value="Play" />
</form>
</section>
<section id="voice">
<form action="/say/" method="POST">
<input type="text" name="text" />
<input type="text" name="voice" placeholder="DE" />
<label for="speed">Speed</label>
<input type="range" name="speed" min="80" max="450" step="10" value="160" id="speed" />
<label for="pitch">Pitch</label>
<input type="range" name="pitch" min="0" max="99" step="1" value="50" id="pitch" />
<input type="submit" value="Voice" />
</form>
</section>
</content>
<script src="/static/main.js"></script>
</body>
</html>