encoding.py 6.42 KB
Newer Older
1
from server import *
2
from sorter import insert_video
Julian Rother's avatar
Julian Rother committed
3
import os.path
4
import json
5

Julian Rother's avatar
Julian Rother committed
6
def set_metadata(dest, course, lecture):
7
	chapters = query('SELECT text, time FROM chapters WHERE lecture_id = ? AND visible ORDER BY time', lecture['id'])
8 9 10 11
	metadata = {'title': lecture['title'], 'album': course['title'],
		'description': lecture['comment'],
		'date': lecture['time'].strftime('%m/%d/%Y'),
		'artist': lecture['speaker'] if lecture['speaker'] else course['organizer']}
Julian Rother's avatar
Julian Rother committed
12 13 14
	dest['metadata'] = metadata
	dest['chapters'] = chapters

Andreas Valder's avatar
Andreas Valder committed
15 16 17 18 19 20 21
def schedule_intro(lectureid):
	lecture = query('SELECT * FROM lectures where id = ?', lectureid)
	course = query('SELECT * FROM course where id = ?', lecture['course_id'])
	data = {'path': path, 'lecture_id': lectureid}
	set_metadata(data, course, lecture)
	schedule_job('intro', data)

22
def schedule_remux(lectureid, videoid=None):
Julian Rother's avatar
Julian Rother committed
23 24 25 26 27 28 29
	lecture = query('SELECT * FROM lectures WHERE id = ?', lectureid)[0]
	course = query('SELECT * FROM courses WHERE id = ?', lecture['course_id'])[0]
	videos = query('''SELECT videos.*, sources.path AS srcpath, sources.hash AS srchash, formats.options AS fmtopts
			FROM videos
			JOIN sources ON videos.source = sources.id
			JOIN formats ON videos.video_format = formats.id
			WHERE videos.lecture_id = ?''', lectureid)
30 31 32
	for video in videos:
		if not video['source']:
			continue
33 34
		if videoid and video['id'] != videoid:
			continue
Julian Rother's avatar
Julian Rother committed
35 36
		data = {'path': video['path'], 'srcpath': video['srcpath'],
			'srchash': video['srchash'], 'video_id': video['id']}
Julian Rother's avatar
Julian Rother committed
37 38 39 40 41
		fmt = json.loads(video['fmtopts'])
		if 'format' in fmt:
			data['format'] = fmt['format']
		data['options'] = fmt.get('options', {})
		set_metadata(data, course, lecture)
42
		schedule_job('remux', data)
43 44 45 46 47 48 49 50 51 52 53

@app.route('/internal/jobs/add/remux', methods=['GET', 'POST'])
@mod_required
@csrf_protect
def add_remux_job():
	lectureid = request.values.get('lectureid')
	videoid = int(request.values.get('videoid', 0))
	if not lectureid:
		lectureid = query('SELECT lecture_id FROM videos WHERE id = ?', videoid)[0]['lecture_id']
	schedule_remux(lectureid, videoid)
	return redirect(request.values.get('ref', url_for('jobs_overview')))
54

Julian Rother's avatar
Julian Rother committed
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
def schedule_transcode(source, fmt_id=None, video=None):
	if video:
		fmt_id = video['video_format']
		assert(video['lecture_id'] == source['lecture_id'])
	assert(fmt_id != None)
	fmt = query('SELECT * FROM formats WHERE id = ?', fmt_id)[0]
	lecture = query('SELECT * FROM lectures WHERE id = ?', source['lecture_id'])[0]
	course = query('SELECT * FROM courses WHERE id = ?', lecture['course_id'])[0]
	data = {'input': {'path': source['path'], 'streams': []}, 'output': json.loads(fmt['options']), 'filters': []}
	if source['type'] == 'plain':
		stream = {'name': 'video', 'type': 'video'}
		data['input']['streams'].append(stream)
		stream = {'name': 'audio', 'type': 'audio'}
		data['input']['streams'].append(stream)
	else:
		assert(False)
	set_metadata(data['output'], course, lecture)
	basename = os.path.basename(source['path']).rsplit('.', 1)[0]
	data['output']['path'] = 'pub/'+course['handle']+'/'+basename+fmt['suffix']
	if video:
75
		old_source = query('SELECT * FROM sources WHERE id = ?', video['source'])[0]
Julian Rother's avatar
Julian Rother committed
76 77 78 79 80 81 82 83
		data['output']['path'] = video['path']
		data['video_id'] = video['id']
		data['srcpath'] = old_source['path']
		data['srchash'] = old_source['hash']
	else:
		data['lecture_id'] = lecture['id']
	data['format_id'] = fmt['id']
	data['source_id'] = source['id']
84 85 86 87 88 89 90 91 92 93
	return schedule_job('transcode', data, queue="background")

@job_handler('transcode')
def insert_transcoded_video(jobid, jobtype, data, state, status):
	if 'lecture_id' not in data or 'source_id' not in data or 'format_id' not in data:
		return
	if 'video_id' in data:
		return
	video_id = insert_video(data['lecture_id'], data['output']['path'], data['format_id'], status['hash'], status['filesize'], status['duration'], data['source_id'])
	schedule_remux(data['lecture_id'], video_id)
Julian Rother's avatar
Julian Rother committed
94

95 96 97 98
@app.route('/internal/jobs/add/reencode', methods=['GET', 'POST'])
@mod_required
@csrf_protect
@handle_errors('jobs_overview', 'Video nicht gefunden!', 404, IndexError)
99
def add_reencode_job():
100 101 102 103 104 105 106 107
	video = query('SELECT * FROM videos WHERE id = ?', request.values['videoid'])[0]
	if not video['source']:
		flash('Manuell erstellte Videos können nicht neukodiert werden!')
	else:
		source = query('''SELECT sources.* FROM sources WHERE sources.id = ? ORDER BY time_created''', video['source'])[-1]
		schedule_transcode(source, video=video)
	return redirect(request.values.get('ref', url_for('jobs_overview')))

Andreas Valder's avatar
Andreas Valder committed
108
@job_handler('probe-raw', 'intro')
Julian Rother's avatar
Julian Rother committed
109 110 111
def update_lecture_videos(jobid, jobtype, data, state, status):
	if 'lecture_id' not in data:
		return
Andreas Valder's avatar
Andreas Valder committed
112 113 114 115
	if jobtype == 'probe-raw':
		if 'source_id' not in data:
			modify('INSERT INTO sources (lecture_id, path, type, hash, time_created) VALUES (?, ?, ?, ?, ?)',
					data['lecture_id'], data['path'], 'plain', status['hash'], datetime.now())
Julian Rother's avatar
Julian Rother committed
116 117 118 119
	sources = query('SELECT * FROM sources WHERE sources.lecture_id = ? ORDER BY time_created', data['lecture_id'])
	if not sources:
		return
	latest = sources[-1]
Andreas Valder's avatar
Andreas Valder committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
	lecture = query('SELECT * FROM lectures where id = ?', data['lecture_id'])
	if 'lecture is autointro' and jobtype == 'probe-raw':
		schedule_intro(data['lecture_id'])
	else:
		videos = query('SELECT * FROM videos WHERE videos.lecture_id = ?', data['lecture_id'])
		current_fmts = [v['video_format'] for v in videos]
		formats = query('''SELECT formats.* FROM formats
				JOIN profiles ON formats.id = profiles.format
				JOIN courses ON profiles.name = courses.profile
				JOIN lectures ON courses.id = lectures.course_id
				WHERE lectures.id = ?''', data['lecture_id'])
		for fmt in formats:
			if fmt['id'] not in current_fmts:
				schedule_transcode(latest, fmt_id=fmt['id'])
		for video in videos:
			if video['source'] != latest['id']:
				schedule_transcode(latest, video=video)
Julian Rother's avatar
Julian Rother committed
137

138
@edit_handler('chapters')
139
def chapter_changed(table, column, value, id, user):
140 141 142 143 144 145 146 147
	chapters = query('SELECT * FROM chapters WHERE id = ?', id)
	if not chapters:
		return
	chapter = chapters[0]
	if column in ['visible', 'deleted'] or (chapter['visible'] and not chapter['deleted']):
		schedule_remux(chapter['lecture_id'])

@edit_handler('courses')
148
def course_changed(table, column, value, id, user):
149 150 151 152 153 154 155
	if column not in ['title', 'organizer']:
		return
	lectures = query('SELECT * FROM lectures WHERE course_id = ?', id)
	for lecture in lectures:
		schedule_remux(lecture['id'])

@edit_handler('lectures')
156
def lecture_changed(table, column, value, id, user):
157 158 159
	if column in ['title', 'comment', 'time', 'speaker']:
		schedule_remux(id)