You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
prolefeeder/prolefeeder.py

143 lines
4.7 KiB
Python

from flask import (Flask, render_template, flash, redirect,
url_for, request, send_from_directory, abort)
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm
from wtforms import DateTimeField, IntegerField, SubmitField
from wtforms.validators import InputRequired, NumberRange
from datetime import datetime, timedelta
from tempfile import mkstemp
import os
import random
import re
import subprocess
import time
from config import Config
app = Flask(__name__)
app.config.from_object(Config)
app.config['WTF_I18N_ENABLED'] = False # enables WTForms built-in translations
Bootstrap(app)
attachment_filenames = {}
class DownloadForm(FlaskForm):
class Meta:
locales = ['de_DE', 'de']
start_time = DateTimeField('Startzeit',
validators=[InputRequired()],
format='%d.%m.%Y %H:%M')
length = IntegerField('Länge',
validators=[
InputRequired(),
NumberRange(min=1, max=app.config['MAX_LENGTH'])])
submit = SubmitField('Download')
@app.route('/', methods=['GET', 'POST'])
def download():
form = DownloadForm()
if request.method == 'GET':
form.start_time.data = datetime.now().replace(minute=0, second=0) - timedelta(hours=1)
form.length.data = 60
elif form.validate_on_submit():
try:
output_filename, attachment_filename = prepare_download(form)
attachment_filenames[output_filename] = attachment_filename
flash('Der Download sollte sofort starten.', 'success')
return render_template('download.html', form=form, filename=output_filename)
except ValueError as e:
flash('Fehler beim Erstellen des Downloads: {}'.format(e), 'warning')
else:
flash('Fehler im Formular!', 'warning')
return render_template('download.html', form=form)
def clean_out_dir():
"""Clean up OUT_DIR"""
for fn in os.listdir(app.config['OUT_DIR']):
if not fn.startswith('prolefeeder'):
continue
creation_time = os.path.getctime(os.path.join(app.config['OUT_DIR'], fn))
if time.time() - creation_time >= 60:
os.unlink(os.path.join(app.config['OUT_DIR'], fn))
@app.route('/download_file/<filename>')
def download_file(filename):
"""Download an output file"""
if filename not in attachment_filenames:
abort(404)
attachment_filename = attachment_filenames.pop(filename)
clean_out_dir()
return send_from_directory(app.config['OUT_DIR'], filename,
as_attachment=True, attachment_filename=attachment_filename)
def prepare_download(form):
"""Prepare a download given the user's request form"""
def start_time_for_source_fn(fn):
return datetime.strptime(fn, 'qfhi-%Y%m%d-%H%M.mp3')
def length_for_source_fn(fn):
size = os.stat(os.path.join(app.config['DATA_DIR'], fn)).st_size
length = timedelta(minutes=size / (1000*app.config['KBITS']/8) / 60)
return length
# Get a sorted list of all source files with start time and length
sources = []
for fn in os.listdir(app.config['DATA_DIR']):
try:
start_time = start_time_for_source_fn(fn)
length = length_for_source_fn(fn)
sources.append({'fn': fn, 'start_time': start_time, 'length': length})
except ValueError:
pass
sources = sorted(sources, key=lambda s: s['start_time'])
# Only interested in the source files from the start file
start_index = None
for i, source in enumerate(sources):
if source['start_time'] <= form.start_time.data < source['start_time'] + source['length']:
start_index = i
if start_index is None:
raise ValueError('Konnte Startdatei nicht finden!')
sources = sources[start_index:]
# Super lazy: Limit to 5 source files input
sources = sources[:5]
# Seek into the first file
ss = (form.start_time.data - sources[0]['start_time']).total_seconds()
# Let ffmpeg do the rest of the job
_, output_filename = mkstemp(prefix='prolefeeder', suffix='.mp3', dir=app.config['OUT_DIR'])
output_filename = os.path.basename(output_filename)
attachment_filename = '{}_{}.mp3'.format(form.start_time.data, form.length.data)
c = [
'ffmpeg', '-y',
'-ss', str(ss),
'-i', 'concat:' + '|'.join(
[os.path.join(app.config['DATA_DIR'], s['fn']) for s in sources]),
'-codec', 'copy',
'-t', str(form.length.data * 60),
os.path.join(app.config['OUT_DIR'], output_filename)
]
app.logger.debug(' '.join(c))
subprocess.call(c)
return output_filename, attachment_filename
if __name__ == '__main__':
app.run(debug=True)