diff --git a/db_schema.sql b/db_schema.sql
index 9f5895d11377eba4fd444420e5527de7235e6102..ae064216eb3debe2adc69bfd81f3a518ea130289 100644
--- a/db_schema.sql
+++ b/db_schema.sql
@@ -108,7 +108,8 @@ CREATE TABLE IF NOT EXISTS `lectures_data` (
   `live` INTEGER NOT NULL DEFAULT 0,
   `norecording` INTEGER NOT NULL DEFAULT 0,
 	`profile` varchar(64),
-  `stream_settings` text NOT NULL DEFAULT ''
+  `stream_settings` text NOT NULL DEFAULT '',
+  `stream_job` INTEGER
 );
 CREATE TABLE IF NOT EXISTS `places` (
   `place` varchar(20) NOT NULL PRIMARY KEY,
diff --git a/livestreams.py b/livestreams.py
index 76a15ca13bbe0c08c628a3bf1369eef540469d4c..063c7fa139e4018ec1e104a90bcde3e4cb49309a 100644
--- a/livestreams.py
+++ b/livestreams.py
@@ -7,8 +7,9 @@ import string
 @sched_func(120)
 def livestream_thumbnail():
 	livestreams = query('SELECT streams.lecture_id, streams.handle AS livehandle FROM streams WHERE streams.active')
-	for v in genlive(livestreams):
-		schedule_job('thumbnail', {'src': v['path'], 'filename': 'l_%i.jpg'%lecture['id']})
+	lectures = query('SELECT * FROM lectures WHERE stream_job IS NOT NULL')
+	for v in genlive(livestreams)+genlive_new(lectures):
+		schedule_job('thumbnail', {'src': v['path'], 'filename': 'l_%i.jpg'%v['lecture_id']})
 
 @app.route('/internal/streaming/legacy_auth', methods=['GET', 'POST'])
 @app.route('/internal/streaming/legacy_auth/<server>', methods=['GET', 'POST'])
@@ -149,7 +150,14 @@ def streamauth(server):
 			return 'Ok', 200
 		return 'Forbidden', 403
 	elif request.values['call'] == 'publish_done':
+		source = (query('SELECT * FROM live_sources WHERE server = ? AND clientid = ?', server, request.values['clientid']) or [None])[0]
 		modify('UPDATE live_sources SET server = NULL, clientid = NULL, preview_key = NULL, last_active = ? WHERE server = ? AND clientid = ?', datetime.now(), server, request.values['clientid'])
+		if not source:
+			return 'Ok', 200
+		for lecture in query('SELECT lectures FROM lectures WHERE stream_job IS NOT NULL'):
+			settings = json.loads(lecture['stream_settings'])
+			if source['id'] in [settings.get('source1'), settings.get('source2')]:
+				cancel_job(lecture['stream_job'])
 		return 'Ok', 200
 	return 'Bad request', 400
 
@@ -166,6 +174,9 @@ def schedule_livestream(lecture_id):
 		if obj:
 			server = obj['server']
 			data['src%i'%idx]['url'] = 'rtmp://%s/src/%i'%(obj['server'], obj['id'])
+			if not obj['clientid']:
+				flash('Quelle „%s“ ist nicht aktiv!'%obj['name'])
+				return None
 		mode = settings.get('source%i_audiomode'%idx)
 		leftvol = float(settings.get('source%i_leftvolume'%idx, 100))/100.0
 		rightvol = float(settings.get('source%i_rightvolume'%idx, 100))/100.0
@@ -204,17 +215,36 @@ def schedule_livestream(lecture_id):
 	data['src2']['afilter'] = build_filter(data['src2']['afilter'])
 	data['src2']['vfilter'] = build_filter(data['src2']['vfilter'])
 	data['destbase'] = 'rtmp://%s/hls/l_%i'%(server, lecture['id'])
+	if lecture['stream_job']:
+		flash('Stream läuft bereits!')
+		return None
 	job_id = schedule_job('complex_live_transcode', data, priority=10)
+	modify('UPDATE lectures_data SET stream_job = ? WHERE id = ? AND stream_job IS NULL', job_id, lecture_id)
+	if query('SELECT stream_job FROM lectures WHERE id = ?', lecture_id)[0]['stream_job'] != job_id:
+		flash('Stream läuft bereits!')
+		cancel_job(job_id)
+		return None
 	return job_id
 
 @job_handler('complex_live_transcode', state='failed')
 def restart_failed_complex_live_transcode(id, type, data, state, status):
 	restart_job(id)
 
-@app.route('/internal/streaming/start', methods=['POST'])
+@job_handler('complex_live_transcode')
+def cleanup_after_complex_live_transcode_ended(id, type, data, state, status):
+	job = query('SELECT * FROM jobs WHERE id = ?', id, nlfix=False)[0]
+	if state == 'finished' or (state == 'failed' and job['canceled']):
+		modify('UPDATE lectures_data SET stream_job = NULL WHERE stream_job = ?', id)
+
+@app.route('/internal/streaming/control', methods=['POST'])
 @mod_required
-def start_stream():
+def control_stream():
+	action = request.values['action']
 	lecture_id = int(request.values['lecture_id'])
 	course = (query('SELECT courses.* FROM courses JOIN lectures ON (courses.id = lectures.course_id) WHERE lectures.id = ?', lecture_id) or [None])[0]
-	schedule_livestream(lecture_id)
+	if action == 'start':
+		schedule_livestream(lecture_id)
+	elif action == 'stop':
+		lecture = query('SELECT * FROM lectures WHERE id = ?', lecture_id)[0]
+		cancel_job(lecture['stream_job'])
 	return redirect(url_for('course', handle=course['handle']))
diff --git a/server.py b/server.py
index d6e16ebf5ed847978660a7d6c81ca5c2672f786d..3a9a7b06e96630503d9178476c183fa5fa488d57 100644
--- a/server.py
+++ b/server.py
@@ -135,6 +135,16 @@ def genlive(streams):
 		stream['file_size'] = 0
 	return streams
 
+def genlive_new(lectures):
+	hls_format = (query('SELECT * FROM formats WHERE keywords = "hls"') or [{}])[0]
+	res = []
+	for lecture in lectures:
+		if not lecture['stream_job']:
+			continue
+		res.append({'livehandle': 'l_%i'%lecture['id'], 'visible': True,
+				'downloadable': False, 'path': 'pub/hls/l_%i.m3u8'%lecture['id'],
+				'file_size': 0, 'formats': hls_format, 'lecture_id': lecture['id']})
+	return res
 
 @app.route('/')
 @register_navbar('Home', icon='home')
@@ -170,6 +180,13 @@ def index():
 		JOIN courses ON courses.id = lectures.course_id
 		WHERE streams.active AND (? OR (streams.visible AND courses.visible AND courses.listed AND lectures.visible))
 		''', ismod())
+	livestreams_new = query('''SELECT lectures.*, "course" AS sep, courses.*
+		FROM lectures
+		JOIN courses ON courses.id = lectures.course_id
+		WHERE lectures.stream_job IS NOT NULL AND (? OR (courses.visible AND courses.listed AND lectures.visible))
+		''', ismod())
+	for stream in livestreams_new:
+		stream['livehandle'] = 'l_%i'%stream['id']
 	featured = query('SELECT * FROM featured WHERE (? OR visible) ORDER BY `order`', ismod())
 	featured = list(filter(lambda x: not x['deleted'], featured))
 	for item in featured:
@@ -192,7 +209,7 @@ def index():
 					WHERE videos.lecture_id = ? AND videos.visible
 					ORDER BY formats.prio DESC
 					''', item['param'])+genlive(streams)
-	return render_template('index.html', latestvideos=livestreams+latestvideos, upcomming=upcomming, featured=featured)
+	return render_template('index.html', latestvideos=livestreams_new+livestreams+latestvideos, upcomming=upcomming, featured=featured)
 
 @app.route('/courses')
 @register_navbar('Videos', icon='film')
@@ -247,10 +264,11 @@ def course(id=None, handle=None):
 			JOIN formats ON formats.keywords = "hls"
 			WHERE streams.active AND (? OR streams.visible) AND lectures.course_id = ?
 			''', ismod(), course['id'])
+	videos += genlive(livestreams)
+	videos += genlive_new(lectures)
 	chapters = []
 	if course['coursechapters']:
 		chapters = query('SELECT chapters.* FROM chapters JOIN lectures ON lectures.id = chapters.lecture_id WHERE lectures.course_id = ? AND NOT chapters.deleted AND chapters.visible ORDER BY time ASC', course['id'])
-	videos += genlive(livestreams)
 	responsible = query('''SELECT users.*, responsible.course_id AS responsible
 			FROM users
 			LEFT JOIN responsible ON (responsible.user_id = users.id AND responsible.course_id = ?)
@@ -286,6 +304,7 @@ def lecture(id, course=None, courseid=None):
 			WHERE streams.active AND (? OR streams.visible) AND lectures.id = ?
 			''', ismod(), id)
 	videos += genlive(livestreams)
+	videos += genlive_new([lecture])
 	perms = query('SELECT perm.* FROM perm WHERE ((NOT perm.deleted) AND (perm.lecture_id = ? OR perm.course_id = ?))',
 			lecture['id'], lecture['course_id'])
 	if not videos:
@@ -379,15 +398,24 @@ def auth(): # For use with nginx auth_request
 	if url.endswith('jpg') or ismod():
 		return "OK", 200
 	if url.startswith('pub/hls/'):
-		handle = url[len('pub/hls/'):].split('_')[0].split('.')[0]
-		perms = query('''SELECT lectures.id AS lecture, perm.*
-				FROM streams
-				JOIN lectures ON (streams.lecture_id = lectures.id)
-				JOIN courses ON (lectures.course_id = courses.id)
-				LEFT JOIN perm ON ((lectures.id = perm.lecture_id OR courses.id = perm.course_id) AND NOT perm.deleted)
-				WHERE streams.handle = ?
-				AND (courses.visible AND lectures.visible AND streams.visible)
-				ORDER BY perm.video_id DESC, perm.lecture_id DESC, perm.course_id DESC''', handle)
+		handle = url[len('pub/hls/'):].rsplit('_')[0].split('.')[0]
+		if handle.startswith('l_'):
+			perms = query('''SELECT lectures.id AS lecture, perm.*
+					FROM lectures
+					JOIN courses ON (lectures.course_id = courses.id)
+					LEFT JOIN perm ON ((lectures.id = perm.lecture_id OR courses.id = perm.course_id) AND NOT perm.deleted)
+					WHERE lectures.id = ?
+					AND (courses.visible AND lectures.visible)
+					ORDER BY perm.video_id DESC, perm.lecture_id DESC, perm.course_id DESC''', int(handle[2:]))
+		else:
+			perms = query('''SELECT lectures.id AS lecture, perm.*
+					FROM streams
+					JOIN lectures ON (streams.lecture_id = lectures.id)
+					JOIN courses ON (lectures.course_id = courses.id)
+					LEFT JOIN perm ON ((lectures.id = perm.lecture_id OR courses.id = perm.course_id) AND NOT perm.deleted)
+					WHERE streams.handle = ?
+					AND (courses.visible AND lectures.visible AND streams.visible)
+					ORDER BY perm.video_id DESC, perm.lecture_id DESC, perm.course_id DESC''', handle)
 	else:
 		perms = query('''SELECT videos.path, videos.id AS vid, perm.*
 				FROM videos
diff --git a/templates/course.html b/templates/course.html
index b6677345af7cb1f8142fb7723d2aa871098e668a..535afa8c378cd98ad0f04619ea1ffee3568fb05f 100644
--- a/templates/course.html
+++ b/templates/course.html
@@ -199,8 +199,9 @@
 				</form>
 			</div>
 			<div class="modal-footer">
-				<form class="form-inline" method="post" action="{{ url_for('start_stream') }}">
+				<form class="form-inline" method="post" action="{{ url_for('control_stream') }}">
 				<input type="hidden" id="editstream-lectureid" name="lecture_id" value="">
+				<input type="hidden" id="editstream-action" name="action" value="">
 				<button type="submit" id="editstream-start" class="btn btn-danger">Speichern und starten</button>
 				<button type="button" id="editstream-submit" class="btn btn-primary">Speichern</button>
 				</form>
@@ -257,6 +258,14 @@ $('#editstream-start').on('click', function () {
 $('#editstream').on('show.bs.modal', function (event) {
 	var button = $(event.relatedTarget);
 	$('#editstream').data('currentpath', button.data('path'));
+	alert(button.data('active'));
+	if (button.data('active')) {
+		$('#editstream-action').val('stop');
+		$('#editstream-start').text('Stream stoppen');
+	} else {
+		$('#editstream-action').val('start');
+		$('#editstream-start').text('Speichern und starten');
+	}
 	$('#editstream-lectureid').val(button.data('lectureid'));
 	$("#editstream-form")[0].reset();
 	if (button.data('value'))
diff --git a/templates/macros.html b/templates/macros.html
index f0de863e4ee9192180f095daf86de0532b253884..961f26ab5e6213c2475b27b5f731fcbc7f516792 100644
--- a/templates/macros.html
+++ b/templates/macros.html
@@ -262,7 +262,7 @@ $('#embedcodebtn').popover(
 						</li>
 						{% if ismod() %}
 						<li class="pull-right">
-							<button class="btn btn-default" data-toggle="modal" data-target="#editstream" data-lectureid="{{ lecture.id }}" data-path="{{ 'lectures.%i.stream_settings'%lecture.id }}" data-value='{{ lecture.stream_settings|e }}'>
+							<button class="btn btn-{{ 'default' if not lecture.stream_settings else 'danger' if lecture.stream_job else 'primary' }}" data-toggle="modal" data-target="#editstream" data-lectureid="{{ lecture.id }}" data-active="{{ 1 if lecture.stream_job else '' }}" data-path="{{ 'lectures.%i.stream_settings'%lecture.id }}" data-value='{{ lecture.stream_settings|e }}'>
 								<span class="fas fa-broadcast-tower"></span>
 							</button>
 						</li>