Une application de streaming en flask

Rédigé par jeromef


04 février 2014 Python, Flask, tutoriel flask tutoriel webapp python streaming



Le but de ce tuto est de montrer comment
- Mettre en place une base de site FLASK, gérer un formulaire, la sécurité et les téléchargements et lire des fichiers.
Prêt... ?



Tout d'abord, il s'agit de créer, comme à chaque début de projet python, un environnement propre et dédié.
Pour cela, reportez vous à l'article "Bien débutez un projet FLASK" qui récapitule les bases (structures fichiers / dossiers, la création d'environnement virtuel (virtualenv) et son utilisation... bref tout pour bien commencer.

On commence


Nous avons donc un environnement dédié sur lequel est installé Flask.
Dans cet exercice nous utiliserons l'arborescence suivante :

DIR : musicstream
  |
  |__ DIR : app
      |
      |__ DIR : static
      |     |
      |     |__ DIR : uploads
      |     |
      |     |__ DIR : css
      |
      |__ DIR : templates
      |     |
      |     |__ __init__.py
      |
      |__ run.py

En créant un fichier __init__.py dans le dossier "app" nous créons un "package python". Je dois peut être revoir la structure de ma base "Flask" sur cette architecture (qui permet ensuite d'envisager une évolutivité grâce à la méthode de classement des fichiers cf. la doc Flask : http://flask.pocoo.org/docs/patterns/packages/)

Envoie le code marcel


Dans le fichier "__init__.py" nous allons mettre ceci :

import os
from flask import Flask
app = Flask(__name__)

APP_ROOT = os.path.dirname(os.path.abspath(__file__))
UPLOAD_FOLDER = os.path.join(APP_ROOT, 'static/uploads')
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER


Ligne par ligne:
1 : on import le module os qui va permettre d'utiliser des fonctions pour parcourir des dossiers…
2 : On importe la classe flask et on créé une instance de FLask.
3 : On crée un objet depuis celle ci (on doit utiliser __name__ avec cette classe puisqu'on utilise un seul module)

5 : on déclare une variable globale d'application (valable partout) pour définir le dossier de stockage des UPLOADS (qui est une concaténation du dossier ROOT de l'application auquel on rajoute "static/uploads")

On sauve le tout et on créé un nouveau fichier que l'on appelle views.py qui prendra en charge les vues (sans blagues !)

On oeuvre views.py et on commence :

import os
from flask import render_template, request, send_from_directory
from werkzeug.utils import secure_filename
from app import app


On importe quelques modules propres à FLask (render_template and co) et l'objet app depuis le package créé précédemment.

On rajoute une méthode (en fait 2 pour prendre en compte l'appel des url / ou /index indifféremment) pour définir la home :

@app.route('/')
@app.route('/index')
def index():
    return render_template('index.html')


Le fait de rajouter un "@" devant la fonction veut dire que l'on a "décoré" la fonction route qui une fois appelée renverra le template "index.html" que l'on va définir.

Dans le rep template, nous créons le fichier layout.html qui sera la base pour tous nos templates futurs.

<!DOCTYPE html>
<html>
<head>
    <title>Audio Streaming App</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.css') }}"/>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/custom.css') }}"/>
</head>
<body>
<div class="container">
      <div class="page-header">
        <h1>Flask Audio Streaming App</h1>
      </div>
    {% block content %}
 
    {% endblock %}
    <hr/>
    <div class="modal-footer">
        <h4>© pypix</h4>
    </div>
</div>
</body>
</html>


Seul point notable pour appeler les CSS on utilise la fonction url_for() qui génère une URL vers le dossier static en ajoutant "css/bootstrap.css" pour aller jusqu'au bon fichier)

Pour respecter la bonne architecture de fichier, téléchargez Bootsrtap et ajoutez le fichier bootsrtap.css dans le dossier css avec un autre fichier que l'on créé custom.css

Dans ce dernier fichier on y met :

.audio {
 width: 100%;
 }
 .modal-footer {
 text-align: center;
 }
 .form-audio {
 margin-left: 350px;
 }
 .page-header {
 text-align: center;
 }


Maintenant, nous avons {% block content %} et {% end block%}, des blocs Jinja2, dans celui ci les fonctions sont délimitées par des {% %} et les variables à afficher dans le navigateur sont délimitées par de {{ }} .

le but du bloc étiquette fait est de dire au moteur de template Jinja2 que d'un template "enfant" remplace les parties de ce template et c'est ainsi que nous pouvons utiliser l'héritage dans les template et nous pouvons avoir des parties communes de l'ensemble du site en un seul endroit sans avoir à écrire la Code à nouveau dans toutes les pages.

Voyons cela avec la création de la page index qui va hériter du template Layout.html grâce à la fonction "extends".

{% extends 'layout.html' %}
 
{% block content %}
 
    <h1>Hello</h1>
 
{% endblock %}

Ainsi le contenu du tag h1 va être injecté dans layout.html à l'endroit ou été notifié le tag {% block content %} {% endblock %}
Maintenant insérons ceci dans run.py

from app import app
app.run(debug=True)

On lance l'application et on lui passe le param débugg pour afficher les erreurs (si il y a)


$ python run.py

Ce qui va entrainer une erreur puisque les vues ne sont pas linké avec l'app. Pour cela rajouter cette ligne en bas de __init__.py

from app import views

Maintenant tout devrait être ok !!

L'upload de musique


Nous allons créer deux parcours, l'un pour le téléchargement et l'autre pour l'affichage du contenu téléchargé.
Ajouter ces lignes ci-dessous la méthode de l'indice dans view.py

@app.route('/upload', methods=['GET','POST'])
def upload_file():
    if request.method == 'POST':
        file = request.files['file']
        if file.filename:
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            return render_template('index.html', filename=filename)
    return render_template('index.html')
 
@app.route('/uploads/')
def uploaded_file(filename):
    return send_from_directory(app.config['UPLOAD_FOLDER'],
                               filename, as_attachment=True)

Soit igne par ligne, d'abord, nous avons déclaré l'URL pour le téléchargement qui n'accepte que GET et POST pour cette méthode, c'est à dire que vous ne pouvez obtenir et publier que sur ce lien http://localhost:5000/upload.
Puis nous vérifions si la méthode, et on obtient le fichier de l'utilisateur et si il a un nom de fichier on l'enregistrer dans notre dossier UPLOADS que nous avons déclaré en globale et si la méthode et GET, on renvoie le template index.html
La méthode suivante prend le nom du fichier téléchargé comme argument et renvoie le fichier dans le répertoire avec le même nom et doit être afficher par le template dans le navigateur. Ces méthodes sont assez simple aussi.
Maintenant, nous avons nos différentes vues en place ce qui nous permet de modifier index.html pour télécharger le fichier et l'écouter !

{% extends 'layout.html' %}
{% block content %}
 
{% if filename %}
    <div>
        <h2>Song : {{ filename }}</h2>
        <div class="hero-unit">
        <audio class="audio" controls>
            <source src="uploads/{{ filename }}" type="audio/mpeg">
        </audio><hr/>
 
        <p>
            <a href="{{ url_for('index') }}" class="btn btn-primary">Home</a>
            <a href="/uploads/{{ filename }}" class="btn btn-info">Download</a>
            <a href="{{ url_for('upload_file') }} " class="btn btn-danger">Upload Another</a>
        </p>
 
        </div>
 
    </div>
{% else %}
 
<div class="form-audio">
    <h2>Upload Song</h2>
    <form action="{{ url_for('upload_file') }}" method="post" enctype="multipart/form-data">
        <input type="file" name="file" class="button" multiple> <br/><br/>
        <input type="submit" value="Upload" class="btn btn-danger">
      </form>
</div>
{% endif %}
{% endblock %}

Voila qui est fait !

Nous avons notre application de téléchargement / uploads de musique et découte !
J'apporterai quelques modifications pour vérifier que les fichiers uploadés sont bien des MP3 (sécurité) et pour affiher un message de confirmation à l'utilisateur (utilisation de la fonction flash de Flask)

N'hésitez pas à commenter

Cet article est la traduction / adpatation du site PIPYX, accessible ici
Merci à eux / thx to them !

Partager cet article sur :


Les commentaires sont fermés.