Commit 5208df95 authored by Tomas Rokos's avatar Tomas Rokos

Create basic frontend, try out server side graphs

parent ef8d024f
......@@ -7,3 +7,6 @@ input
upload
preprocessed_*
audio_classification.egg-info
*.cpython-37.pyc
node_modules
.cache
\ No newline at end of file
......@@ -21,6 +21,13 @@ preprocess: get-dataset
. ./venv/bin/activate && \
python3 audio_classification/preprocess.py
build-frontend:
rm -rf audio_classification/run/static
parcel build audio_classification/run/frontend/index.html --out-dir audio_classification/run/static
watch-frontend:
parcel audio_classification/run/frontend/index.html
export-libs:
./venv/bin/pip3 freeze > requirements.txt
......
import os
from random import random
from flask import Flask, request, flash, jsonify
from flask import Flask, request, flash, jsonify, send_from_directory, make_response
from werkzeug.utils import redirect, secure_filename
from audio_classification.classifier.base_classifier import MockClassifier
from audio_classification.run.utils import generate_soundwave
UPLOAD_FOLDER = 'upload'
app = Flask(__name__)
app = Flask(__name__, static_url_path='', static_folder='static')
classifiers = {
'mock': MockClassifier()
......@@ -17,27 +18,40 @@ if not os.path.exists(UPLOAD_FOLDER):
os.mkdir(UPLOAD_FOLDER)
@app.route('/classify', methods=['POST'])
@app.route('/')
def index():
return send_from_directory('static', 'index.html')
def _build_cors_prelight_response():
response = make_response()
response.headers.add("Access-Control-Allow-Origin", "*")
response.headers.add('Access-Control-Allow-Headers', "*")
response.headers.add('Access-Control-Allow-Methods', "*")
return response
@app.route('/classify', methods=['POST', 'OPTIONS'])
def classify():
if request.json is None:
flash('No json presented')
return redirect(request.url)
if request.method == "OPTIONS": # CORS
return _build_cors_prelight_response()
classifier_id = request.json['classifier_id']
# if request.json is None:
# flash('No json presented')
# return redirect(request.url)
if classifier_id not in classifiers:
flash('Unknown classifier id')
return redirect(request.url)
# classifier_id = request.json['classifier_id']
classifier = classifiers[classifier_id]
# if classifier_id not in classifiers:
# flash('Unknown classifier id')
# return redirect(request.url)
#
# classifier = classifiers[classifier_id]
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file')
return redirect(request.url)
filename = secure_filename(file.filename)
folder = os.path.join(UPLOAD_FOLDER, f'c{random()}')
......@@ -46,8 +60,11 @@ def classify():
try:
os.mkdir(folder)
file.save(filepath)
prediction = classifier.classify_file(filepath)
jsonify(prediction)
# prediction = classifier.classify_file(filepath)
return jsonify({
"filename": file.filename,
"soundwave": generate_soundwave(filepath)
})
except Exception as e:
flash(e)
return redirect(request.url)
......
body {
margin: 0;
}
#uploader {
width: 100%;
height: 400px;
}
.uppy-Dashboard-Item {
cursor: pointer;
}
.container {
max-width: 800px;
margin: 0 auto;
display: flex;
flex-direction: column;
align-items: center;
}
.uppy-Dashboard-poweredBy {
display: none !important;
}
\ No newline at end of file
<html lang="en">
<head>
<title>Audio classification</title>
</head>
<body>
<div class="container">
<h1>Audio Genre Classificator</h1>
<div id="uploader" class="dropzone"></div>
<div id="result"></div>
</div>
<script src="./index.tsx"></script>
</body>
</html>
\ No newline at end of file
import './index.css';
import '@uppy/core/dist/style.min.css';
import '@uppy/dashboard/dist/style.css';
import {Uppy} from '@uppy/core';
import Dashboard from '@uppy/dashboard';
import XHRUpload from '@uppy/xhr-upload';
import ReactDOM from 'react-dom';
import React from 'react';
import Result, {APIResponse} from './ts/Result';
import {base64tosrc} from "./ts/utils";
const uppy = new Uppy({
debug: true,
allowMultipleUploads: true,
autoProceed: true,
restrictions: {
allowedFileTypes: ['.wav']
}
}).use(Dashboard, {
inline: true,
target: '#uploader',
height: '100%',
width: '100%',
theme: 'light',
}).use(XHRUpload, {
method: 'POST',
endpoint: 'http://localhost:4666/classify',
fieldName: 'file'
})
// const onFileClick = (evt: MouseEvent): void => {
// const file = evt.currentTarget as Element;
// const fileId = file.getAttribute('id');
// if (fileId) {
// console.log(fileId);
// }
// }
// const reloadEventListeners = (): void => {
// // @ts-ignore
// const files = Array.from(document.getElementsByClassName('uppy-Dashboard-Item'));
//
// files.forEach(file => {
// file.removeEventListener('click', onFileClick);
// file.addEventListener('click', onFileClick)
// })
// }
uppy.on('file-added', (file) => {
uppy.getFiles()
.filter(oldFile => file.id != oldFile.id)
.forEach((oldFile) => uppy.removeFile(oldFile.id))
})
uppy.on('upload-success', (file, response) => {
const resElement = document.getElementById('result');
if (!resElement) {
throw "No res element!";
}
const body = response.body as APIResponse;
uppy.setFileState(file.id, {preview: base64tosrc(body.soundwave)})
ReactDOM.render(<Result response={body} file={URL.createObjectURL(file.data)}/>, resElement);
})
uppy.on('complete', (result) => {
const id = uppy.getFiles()[0].id;
})
\ No newline at end of file
import React, {useEffect, useRef} from 'react';
import {base64tosrc} from "./utils";
export interface APIResponse {
filename: string;
soundwave: string;
}
interface ResultProps {
response: APIResponse;
file: string;
}
const Result: React.FC<ResultProps> = ({response, file}) => {
const audio = useRef<HTMLAudioElement>();
useEffect(() => {
audio.current.pause();
audio.current.load();
}, [file])
return (
<div>
<h1>{response.filename}</h1>
<audio controls style={{'width': '100%'}} ref={audio}>
<source src={file}/>
</audio>
<img src={base64tosrc(response.soundwave)} alt="Sound wave"/>
</div>
)
}
export default Result;
\ No newline at end of file
export const base64tosrc = (bs64: string): string => 'data:image/jpg;base64,' + bs64;
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<html lang="en"><head><title>Audio classification</title><link rel="stylesheet" href="/frontend.56c3c255.css"></head><body> <div class="container"> <h1>Audio Genre Classificator</h1> <div id="uploader" class="dropzone"></div> <div id="result"></div> </div> <script src="/frontend.f8a14f1a.js"></script> </body></html>
\ No newline at end of file
import base64
import io
import librosa
import librosa.display
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg
def generate_soundwave(filepath):
fig = Figure()
axis = fig.add_subplot(1, 1, 1)
file, sr = librosa.load(filepath)
librosa.display.waveplot(y=file, sr=sr, ax=axis)
pic_IObytes = io.BytesIO()
FigureCanvasAgg(fig).print_jpg(pic_IObytes)
pic_IObytes.seek(0)
pic_hash = base64.b64encode(pic_IObytes.read())
return pic_hash.decode('ascii')
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
{
"compilerOptions": {
"target": "esnext",
"module": "commonjs",
"esModuleInterop": true,
"jsx": "react"
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment