diff --git a/chapters.py b/chapters.py
index 44d37e4cf8d8f1c56fd919d284b399b709b834b1..5eb22d3aa515ddc886b51b027cba1611bed8e419 100644
--- a/chapters.py
+++ b/chapters.py
@@ -1,3 +1,4 @@
+import json
 from server import *
 
 @app.route('/internal/newchapter/<int:lectureid>', methods=['POST', 'GET'])
@@ -31,4 +32,6 @@ def chapters(lectureid):
 		c['start'] = c['time']
 		c['end'] = last['start'] if last else 9999
 		last = c
+	if 'json' in request.values:
+		return Response(json.dumps([{'time': c['time'], 'text': c['text']} for c in chapters]),  mimetype='application/json')
 	return Response(render_template('chapters.srt',chapters=chapters), 200, {'Content-Type':'text/vtt'})
diff --git a/db.py b/db.py
index 9ae0d82f465dce55d9dc852763a281773c084414..3965fae7dda6d8604551b11cfa309f9a16d046ca 100644
--- a/db.py
+++ b/db.py
@@ -42,6 +42,9 @@ if config['DB_ENGINE'] == 'sqlite':
 		params = [(p.replace(microsecond=0) if isinstance(p, datetime) else p) for p in params]
 		return operation, params
 
+	def show(operation, host=None):
+		return {}
+
 elif config['DB_ENGINE'] == 'mysql':
 	import mysql.connector
 
@@ -57,6 +60,29 @@ elif config['DB_ENGINE'] == 'mysql':
 		params = [(p.replace(microsecond=0) if isinstance(p, datetime) else p) for p in params]
 		return operation, params
 
+	def show(operation, host=config.get('MYSQL_HOST', None)):
+		if host:
+			db = mysql.connector.connect(user=config['MYSQL_USER'], password=config['MYSQL_PASSWD'], host=host, port=config.get('MYSQL_PORT', 3306))
+		else:
+			db = mysql.connector.connect(user=config['MYSQL_USER'], password=config['MYSQL_PASSWD'], unix_socket=config.get('MYSQL_UNIX', None))
+		cur = db.cursor()
+		cur.execute(operation)
+		rows = []
+		try:
+			rows = cur.fetchall()
+		except mysql.connector.errors.InterfaceError as ie:
+			if ie.msg == 'No result set to fetch from.':
+				# no problem, we were just at the end of the result set
+				pass
+			else:
+				raise
+		res = {}
+		for row in rows:
+			res[row[0]] = row[1]
+		cur.close()
+		db.close()
+		return res
+
 def query(operation, *params, delim="sep"):
 	operation, params = fix_query(operation, params)
 	cur = get_dbcursor()
diff --git a/db_schema.sql b/db_schema.sql
index 85036ec2148f7d5e7f6b6884b18ba8d2cb012e0f..44ac919a500b8b2dfb18350d02c58c20aa7efd71 100644
--- a/db_schema.sql
+++ b/db_schema.sql
@@ -61,7 +61,8 @@ CREATE TABLE IF NOT EXISTS `courses_data` (
   `description` text NOT NULL DEFAULT '',
   `internal` text NOT NULL DEFAULT '',
   `responsible` text NOT NULL DEFAULT '',
-  `feed_url` text NOT NULL DEFAULT ''
+  `feed_url` text NOT NULL DEFAULT '',
+  `external` INTEGER NOT NULL DEFAULT 0
 );
 CREATE TABLE IF NOT EXISTS `filesizes` (
   `path` varchar(255) NOT NULL PRIMARY KEY,
@@ -98,7 +99,8 @@ CREATE TABLE IF NOT EXISTS `lectures_data` (
   `time_updated` datetime NOT NULL,
   `jumplist` text NOT NULL DEFAULT '',
   `titlefile` varchar(255) NOT NULL DEFAULT '',
-  `live` INTEGER NOT NULL DEFAULT 0
+  `live` INTEGER NOT NULL DEFAULT 0,
+  `norecording` INTEGER NOT NULL DEFAULT 0
 );
 CREATE TABLE IF NOT EXISTS `places` (
   `place` varchar(20) NOT NULL PRIMARY KEY,
diff --git a/edit.py b/edit.py
index a00f7a6d6801ddf4ad505c0dc5929f44fa6b0e06..cfd7d1e7debcc85dbd8ef8f29bf259e7def2296d 100644
--- a/edit.py
+++ b/edit.py
@@ -25,7 +25,8 @@ editable_tables = {
 			'internal':	{'type': 'text'},
 			'responsible':	{'type': 'shortstring'},
 			'deleted':	{'type': 'boolean'},
-			'description':	{'type': 'text'} },
+			'description':	{'type': 'text'},
+			'external':  {'type': 'boolean'}},
 		'creationtime_fields': ['created_by', 'time_created', 'time_updated'] },
 	'lectures': {
 		'table': 'lectures_data',
@@ -41,7 +42,8 @@ editable_tables = {
 			'duration':	{'type': 'duration'},
 			'jumplist':	{'type': ''},
 			'deleted':	{'type': 'boolean'},
-			'live': {'type': 'boolean'}},
+			'live': {'type': 'boolean'},
+			'norecording': {'type': 'boolean'}},
 		'creationtime_fields': ['course_id', 'time_created', 'time_updated'] },
 	'videos': {
 		'table': 'videos_data',
diff --git a/server.py b/server.py
index 288b462e67c6524345b8d7c144229e3b101052d4..3cf8f28dc17ff0c58dc8dfecb1193d0a08039d62 100644
--- a/server.py
+++ b/server.py
@@ -70,7 +70,7 @@ app.jinja_env.globals['gitversion'] = { 'hash': output[1], 'longhash': output[0]
 if not config.get('SECRET_KEY', None):
 	config['SECRET_KEY'] = os.urandom(24)
 
-from db import query, modify, searchquery, ldapauth, ldapget
+from db import query, modify, show, searchquery, ldapauth, ldapget
 
 mod_endpoints = []
 
@@ -325,14 +325,14 @@ def index():
 				return "Not found", 404
 			return redirect(url_for('lecture', course=courses[0]['handle'], id=request.args['lectureid']),code=302)
 
-	start = date.today() - timedelta(days=1)
+	start = date.today()
 	end = start + timedelta(days=7)
 	upcomming = query('''
 		SELECT lectures.*, "course" AS sep, courses.*
 		FROM lectures
 		JOIN courses ON (lectures.course_id = courses.id)
-		WHERE (time > ?) AND (time < ?) and lectures.visible and courses.visible and courses.listed
-		ORDER BY time ASC LIMIT 30''',start,end)
+		WHERE (time > ?) AND (time < ?) AND (? OR (lectures.visible AND courses.visible AND courses.listed)) AND NOT lectures.norecording
+		ORDER BY time ASC LIMIT 30''', start, end, ismod())
 	for i in upcomming:
 		i['date'] = i['time'].date()
 	latestvideos=query('''
@@ -509,7 +509,7 @@ def login():
 
 @app.route('/internal/logout', methods=['GET', 'POST'])
 def logout():
-	session.pop('user')
+	session.pop('user', None)
 	return redirect(request.values.get('ref', url_for('index')))
 
 @app.route('/internal/auth')
@@ -617,7 +617,33 @@ def legacy(phpfile=None):
 		return redirect(url_for('feed', handle=request.args.copy().popitem()[0]),code=302)
 	print("Unknown legacy url:",request.url)
 	return redirect(url_for('index'),code=302)
-	
+
+import json
+
+@app.route('/internal/dbstatus')
+@register_navbar('DB-Status', icon='ok')
+@mod_required
+def dbstatus():
+	hosts = set()
+	clusters = {}
+	status = {}
+	variables = {}
+	for host in config.get('MYSQL_DBSTATUS_HOSTS', [])+[config.get('MYSQL_HOST', None)]:
+		for _host in show('SHOW VARIABLES LIKE "wsrep_cluster_address"', host=host)['wsrep_cluster_address'][len('gcomm://'):].split(','):
+			hosts.add(_host)
+	for host in sorted(list(hosts)):
+		try:
+			status[host] = show('SHOW GLOBAL STATUS LIKE "wsrep%"', host=host)
+			variables[host] = show('SHOW GLOBAL VARIABLES LIKE "wsrep%"', host=host)
+		except:
+			status[host] = {'wsrep_cluster_state_uuid': '', 'wsrep_local_state_comment': 'Not reachable', 'wsrep_cluster_conf_id': '0', 'wsrep_cluster_status': 'Unknown'}
+			variables[host] = {'wsrep_node_name': host, 'wsrep_cluster_name': 'unknown'}
+		cluster = variables[host]['wsrep_cluster_name']+'-'+status[host]['wsrep_cluster_conf_id']
+		if cluster not in clusters:
+			clusters[cluster] = []
+		clusters[cluster].append(host)
+	return render_template('dbstatus.html', clusters=clusters, statuses=status, vars=variables), 200
+
 import edit
 import feeds
 import importer
diff --git a/static/videojs/video-js.css b/static/videojs/video-js.css
index ef9ff8a37598bf7a1fd12d88f94b57f2378aaa61..821bac9627ed58b0d49383b976f3bcfd7c234dca 100644
--- a/static/videojs/video-js.css
+++ b/static/videojs/video-js.css
@@ -450,7 +450,7 @@ body.vjs-full-window {
   line-height: 1.4em;
   font-size: 1.2em;
   text-align: center;
-  text-transform: lowercase; }
+ /* text-transform: lowercase;}*/ }
 
 .vjs-menu li:focus,
 .vjs-menu li:hover {
diff --git a/static/videojs/video.js b/static/videojs/video.js
index 9419308b41bce458e90c619ac76ba8c3d21089c9..9cb476d7c5aff8e675d464533d17fe1b74987726 100644
--- a/static/videojs/video.js
+++ b/static/videojs/video.js
@@ -4404,7 +4404,7 @@ var ChaptersButton = function (_TextTrackButton) {
 
       var title = Dom.createEl('li', {
         className: 'vjs-menu-title',
-        innerHTML: (0, _toTitleCase2['default'])(this.kind_),
+        innerHTML: 'Kapitel',
         tabIndex: -1
       });
 
diff --git a/static/videojs/videojs-markers.js b/static/videojs/videojs-markers.js
new file mode 100644
index 0000000000000000000000000000000000000000..d89b4531485cd8fedf83d0a81d389ca5eee55738
--- /dev/null
+++ b/static/videojs/videojs-markers.js
@@ -0,0 +1,379 @@
+/*! videojs-markers - v0.6.1 - 2016-10-24
+* Copyright (c) 2016 ; Licensed  */
+'use strict';
+
+(function ($, videojs, undefined) {
+  // default setting
+  var defaultSetting = {
+    markerStyle: {
+      'width': '7px',
+      'border-radius': '30%',
+      'background-color': 'red'
+    },
+    markerTip: {
+      display: true,
+      text: function text(marker) {
+        return "Break: " + marker.text;
+      },
+      time: function time(marker) {
+        return marker.time;
+      }
+    },
+    breakOverlay: {
+      display: false,
+      displayTime: 3,
+      text: function text(marker) {
+        return "Break overlay: " + marker.overlayText;
+      },
+      style: {
+        'width': '100%',
+        'height': '20%',
+        'background-color': 'rgba(0,0,0,0.7)',
+        'color': 'white',
+        'font-size': '17px'
+      }
+    },
+    onMarkerClick: function onMarkerClick(marker) {},
+    onMarkerReached: function onMarkerReached(marker, index) {},
+    markers: []
+  };
+
+  // create a non-colliding random number
+  function generateUUID() {
+    var d = new Date().getTime();
+    var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
+      var r = (d + Math.random() * 16) % 16 | 0;
+      d = Math.floor(d / 16);
+      return (c == 'x' ? r : r & 0x3 | 0x8).toString(16);
+    });
+    return uuid;
+  };
+
+  var NULL_INDEX = -1;
+
+  function registerVideoJsMarkersPlugin(options) {
+    /**
+     * register the markers plugin (dependent on jquery)
+     */
+
+    var setting = $.extend(true, {}, defaultSetting, options),
+        markersMap = {},
+        markersList = [],
+        // list of markers sorted by time
+    videoWrapper = $(this.el()),
+        currentMarkerIndex = NULL_INDEX,
+        player = this,
+        markerTip = null,
+        breakOverlay = null,
+        overlayIndex = NULL_INDEX;
+
+    function sortMarkersList() {
+      // sort the list by time in asc order
+      markersList.sort(function (a, b) {
+        return setting.markerTip.time(a) - setting.markerTip.time(b);
+      });
+    }
+
+    function addMarkers(newMarkers) {
+      newMarkers.forEach(function (marker) {
+        marker.key = generateUUID();
+
+        videoWrapper.find('.vjs-progress-holder').append(createMarkerDiv(marker));
+
+        // store marker in an internal hash map
+        markersMap[marker.key] = marker;
+        markersList.push(marker);
+      });
+
+      sortMarkersList();
+    }
+
+    function getPosition(marker) {
+      return setting.markerTip.time(marker) / player.duration() * 100;
+    }
+
+    function createMarkerDiv(marker) {
+      var markerDiv = $("<div class='vjs-marker'></div>");
+      markerDiv.css(setting.markerStyle).css({
+        "margin-left": -parseFloat(markerDiv.css("width")) / 2 + 'px',
+        "left": getPosition(marker) + '%'
+      }).attr("data-marker-key", marker.key).attr("data-marker-time", setting.markerTip.time(marker));
+
+      // add user-defined class to marker
+      if (marker.class) {
+        markerDiv.addClass(marker.class);
+      }
+
+      // bind click event to seek to marker time
+      markerDiv.on('click', function (e) {
+        var preventDefault = false;
+        if (typeof setting.onMarkerClick === "function") {
+          // if return false, prevent default behavior
+          preventDefault = setting.onMarkerClick(marker) === false;
+        }
+
+        if (!preventDefault) {
+          var key = $(this).data('marker-key');
+          player.currentTime(setting.markerTip.time(markersMap[key]));
+        }
+      });
+
+      if (setting.markerTip.display) {
+        registerMarkerTipHandler(markerDiv);
+      }
+
+      return markerDiv;
+    }
+
+    function updateMarkers() {
+      // update UI for markers whose time changed
+      markersList.forEach(function (marker) {
+        var markerDiv = videoWrapper.find(".vjs-marker[data-marker-key='" + marker.key + "']");
+        var markerTime = setting.markerTip.time(marker);
+
+        if (markerDiv.data('marker-time') !== markerTime) {
+          markerDiv.css({ "left": getPosition(marker) + '%' }).attr("data-marker-time", markerTime);
+        }
+      });
+      sortMarkersList();
+    }
+
+    function removeMarkers(indexArray) {
+      // reset overlay
+      if (!!breakOverlay) {
+        overlayIndex = NULL_INDEX;
+        breakOverlay.css("visibility", "hidden");
+      }
+      currentMarkerIndex = NULL_INDEX;
+
+      var deleteIndexList = [];
+      indexArray.forEach(function (index) {
+        var marker = markersList[index];
+        if (marker) {
+          // delete from memory
+          delete markersMap[marker.key];
+          deleteIndexList.push(index);
+
+          // delete from dom
+          videoWrapper.find(".vjs-marker[data-marker-key='" + marker.key + "']").remove();
+        }
+      });
+
+      // clean up markers array
+      deleteIndexList.reverse();
+      deleteIndexList.forEach(function (deleteIndex) {
+        markersList.splice(deleteIndex, 1);
+      });
+
+      // sort again
+      sortMarkersList();
+    }
+
+    // attach hover event handler
+    function registerMarkerTipHandler(markerDiv) {
+      markerDiv.on('mouseover', function () {
+        var marker = markersMap[$(markerDiv).data('marker-key')];
+
+        if (!!markerTip) {
+          markerTip.find('.vjs-tip-inner').text(setting.markerTip.text(marker));
+
+          // margin-left needs to minus the padding length to align correctly with the marker
+          markerTip.css({
+            "left": getPosition(marker) + '%',
+            "margin-left": -parseFloat(markerTip.width()) / 2 - 5 + 'px',
+            "visibility": "visible"
+          });
+        }
+      });
+
+      markerDiv.on('mouseout', function () {
+        !!markerTip && markerTip.css("visibility", "hidden");
+      });
+    }
+
+    function initializeMarkerTip() {
+      markerTip = $("<div class='vjs-tip'><div class='vjs-tip-arrow'></div><div class='vjs-tip-inner'></div></div>");
+      videoWrapper.find('.vjs-progress-holder').append(markerTip);
+    }
+
+    // show or hide break overlays
+    function updateBreakOverlay() {
+      if (!setting.breakOverlay.display || currentMarkerIndex < 0) {
+        return;
+      }
+
+      var currentTime = player.currentTime();
+      var marker = markersList[currentMarkerIndex];
+      var markerTime = setting.markerTip.time(marker);
+
+      if (currentTime >= markerTime && currentTime <= markerTime + setting.breakOverlay.displayTime) {
+        if (overlayIndex !== currentMarkerIndex) {
+          overlayIndex = currentMarkerIndex;
+          breakOverlay && breakOverlay.find('.vjs-break-overlay-text').html(setting.breakOverlay.text(marker));
+        }
+
+        breakOverlay && breakOverlay.css('visibility', "visible");
+      } else {
+        overlayIndex = NULL_INDEX;
+        breakOverlay && breakOverlay.css("visibility", "hidden");
+      }
+    }
+
+    // problem when the next marker is within the overlay display time from the previous marker
+    function initializeOverlay() {
+      breakOverlay = $("<div class='vjs-break-overlay'><div class='vjs-break-overlay-text'></div></div>").css(setting.breakOverlay.style);
+      videoWrapper.append(breakOverlay);
+      overlayIndex = NULL_INDEX;
+    }
+
+    function onTimeUpdate() {
+      onUpdateMarker();
+      updateBreakOverlay();
+      options.onTimeUpdateAfterMarkerUpdate && options.onTimeUpdateAfterMarkerUpdate();
+    }
+
+    function onUpdateMarker() {
+      /*
+        check marker reached in between markers
+        the logic here is that it triggers a new marker reached event only if the player
+        enters a new marker range (e.g. from marker 1 to marker 2). Thus, if player is on marker 1 and user clicked on marker 1 again, no new reached event is triggered)
+      */
+      if (!markersList.length) {
+        return;
+      }
+
+      var getNextMarkerTime = function getNextMarkerTime(index) {
+        if (index < markersList.length - 1) {
+          return setting.markerTip.time(markersList[index + 1]);
+        }
+        // next marker time of last marker would be end of video time
+        return player.duration();
+      };
+      var currentTime = player.currentTime();
+      var newMarkerIndex = NULL_INDEX;
+
+      if (currentMarkerIndex !== NULL_INDEX) {
+        // check if staying at same marker
+        var nextMarkerTime = getNextMarkerTime(currentMarkerIndex);
+        if (currentTime >= setting.markerTip.time(markersList[currentMarkerIndex]) && currentTime < nextMarkerTime) {
+          return;
+        }
+
+        // check for ending (at the end current time equals player duration)
+        if (currentMarkerIndex === markersList.length - 1 && currentTime === player.duration()) {
+          return;
+        }
+      }
+
+      // check first marker, no marker is selected
+      if (currentTime < setting.markerTip.time(markersList[0])) {
+        newMarkerIndex = NULL_INDEX;
+      } else {
+        // look for new index
+        for (var i = 0; i < markersList.length; i++) {
+          nextMarkerTime = getNextMarkerTime(i);
+          if (currentTime >= setting.markerTip.time(markersList[i]) && currentTime < nextMarkerTime) {
+            newMarkerIndex = i;
+            break;
+          }
+        }
+      }
+
+      // set new marker index
+      if (newMarkerIndex !== currentMarkerIndex) {
+        // trigger event if index is not null
+        if (newMarkerIndex !== NULL_INDEX && options.onMarkerReached) {
+          options.onMarkerReached(markersList[newMarkerIndex], newMarkerIndex);
+        }
+        currentMarkerIndex = newMarkerIndex;
+      }
+    }
+
+    // setup the whole thing
+    function initialize() {
+      if (setting.markerTip.display) {
+        initializeMarkerTip();
+      }
+
+      // remove existing markers if already initialized
+      player.markers.removeAll();
+      addMarkers(options.markers);
+
+      if (setting.breakOverlay.display) {
+        initializeOverlay();
+      }
+      onTimeUpdate();
+      player.on("timeupdate", onTimeUpdate);
+    }
+
+    // setup the plugin after we loaded video's meta data
+    player.on("loadedmetadata", function () {
+      initialize();
+    });
+
+    // exposed plugin API
+    player.markers = {
+      getMarkers: function getMarkers() {
+        return markersList;
+      },
+      next: function next() {
+        // go to the next marker from current timestamp
+        var currentTime = player.currentTime();
+        for (var i = 0; i < markersList.length; i++) {
+          var markerTime = setting.markerTip.time(markersList[i]);
+          if (markerTime > currentTime) {
+            player.currentTime(markerTime);
+            break;
+          }
+        }
+      },
+      prev: function prev() {
+        // go to previous marker
+        var currentTime = player.currentTime();
+        for (var i = markersList.length - 1; i >= 0; i--) {
+          var markerTime = setting.markerTip.time(markersList[i]);
+          // add a threshold
+          if (markerTime + 0.5 < currentTime) {
+            player.currentTime(markerTime);
+            return;
+          }
+        }
+      },
+      add: function add(newMarkers) {
+        // add new markers given an array of index
+        addMarkers(newMarkers);
+      },
+      remove: function remove(indexArray) {
+        // remove markers given an array of index
+        removeMarkers(indexArray);
+      },
+      removeAll: function removeAll() {
+        var indexArray = [];
+        for (var i = 0; i < markersList.length; i++) {
+          indexArray.push(i);
+        }
+        removeMarkers(indexArray);
+      },
+      updateTime: function updateTime() {
+        // notify the plugin to update the UI for changes in marker times
+        updateMarkers();
+      },
+      reset: function reset(newMarkers) {
+        // remove all the existing markers and add new ones
+        player.markers.removeAll();
+        addMarkers(newMarkers);
+      },
+      destroy: function destroy() {
+        // unregister the plugins and clean up even handlers
+        player.markers.removeAll();
+        breakOverlay && breakOverlay.remove();
+        markerTip && markerTip.remove();
+        player.off("timeupdate", updateBreakOverlay);
+        delete player.markers;
+      }
+    };
+  }
+
+  videojs.plugin('markers', registerVideoJsMarkersPlugin);
+})(jQuery, window.videojs);
+//# sourceMappingURL=videojs-markers.js.map
diff --git a/static/videojs/videojs.markers.css b/static/videojs/videojs.markers.css
new file mode 100644
index 0000000000000000000000000000000000000000..7655a64f877f1876e46c464435aedb29b5bd99b8
--- /dev/null
+++ b/static/videojs/videojs.markers.css
@@ -0,0 +1,59 @@
+.vjs-marker {
+  position: absolute;
+  left: 0;
+  bottom: 0em;
+  opacity: 1;
+  height: 100%;
+  transition: opacity .2s ease;
+  -webkit-transition: opacity .2s ease;
+  -moz-transition: opacity .2s ease;
+  z-index: 100;
+}
+.vjs-marker:hover {
+  cursor: pointer;
+  -webkit-transform: scale(1.3, 1.3);
+  -moz-transform: scale(1.3, 1.3);
+  -o-transform: scale(1.3, 1.3);
+  -ms-transform: scale(1.3, 1.3);
+  transform: scale(1.3, 1.3);
+}
+.vjs-tip {
+  visibility: hidden;
+  display: block;
+  opacity: 0.8;
+  padding: 5px;
+  font-size: 10px;
+  position: absolute;
+  bottom: 14px;
+  z-index: 100000;
+}
+.vjs-tip .vjs-tip-arrow {
+  background: url(data:image/gif;base64,R0lGODlhCQAJAIABAAAAAAAAACH5BAEAAAEALAAAAAAJAAkAAAIRjAOnwIrcDJxvwkplPtchVQAAOw==) no-repeat top left;
+  bottom: 0;
+  left: 50%;
+  margin-left: -4px;
+  background-position: bottom left;
+  position: absolute;
+  width: 9px;
+  height: 5px;
+}
+.vjs-tip .vjs-tip-inner {
+  border-radius: 3px;
+  -moz-border-radius: 3px;
+  -webkit-border-radius: 3px;
+  padding: 5px 8px 4px 8px;
+  background-color: black;
+  color: white;
+  max-width: 200px;
+  text-align: center;
+}
+.vjs-break-overlay {
+  visibility: hidden;
+  position: absolute;
+  z-index: 100000;
+  top: 0;
+}
+.vjs-break-overlay .vjs-break-overlay-text {
+  padding: 9px;
+  text-align: center;
+}
diff --git a/templates/base.html b/templates/base.html
index 4ac3bad76935eb1eaaa4257b52d2f7bf24e4f486..31bdd63c5df108ddf0271eab367dd16c295d5128 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -18,6 +18,7 @@
 		<link rel="stylesheet" type="text/css" href="{{url_for('static', filename='style.css')}}">
 		<link rel="stylesheet" href="{{url_for('static', filename='font-awesome/css/font-awesome.css')}}">
 		<link rel="stylesheet" type="text/css" href="{{url_for('static', filename='videojs/video-js.css')}}">
+		<link rel="stylesheet" type="text/css" href="{{url_for('static', filename='videojs/videojs.markers.css')}}">
 		<link rel="stylesheet" type="text/css" href="{{url_for('static', filename='videojs/videojs-resolution-switcher.css')}}">
 
 		<script src="{{url_for('static', filename='jquery.js')}}"></script>
@@ -33,6 +34,7 @@
 		<script src="{{url_for('static', filename='videojs/videojs-resolution-switcher.js')}}"></script>
 		<script src="{{url_for('static', filename='videojs/videojs-contrib-hls.js')}}"></script>
 		<script src="{{url_for('static', filename='videojs/videojs.hotkeys.js')}}"></script>
+		<script src="{{url_for('static', filename='videojs/videojs-markers.js')}}"></script>
 		{% endblock %}
 	</head>
 	<body>
diff --git a/templates/course.html b/templates/course.html
index 9868782fac52a58f22e958483f919f012ad6e4a2..6bbb9e915a58af67c96861574ed0937f2ddd3f3a 100644
--- a/templates/course.html
+++ b/templates/course.html
@@ -41,6 +41,7 @@
 				<tbody>
 					<tr><td>Sichtbar:</td><td>{{ moderator_checkbox(['courses',course.id,'visible'], course.visible) }}</td></tr>
 					<tr><td>Gelistet:</td><td>{{ moderator_checkbox(['courses',course.id,'listed'], course.listed) }}</td></tr>
+					<tr><td>Nicht im Drehplan:</td><td>{{ moderator_checkbox(['courses',course.id,'external'], course.external) }}</td></tr>
 					<tr><td>Videos downloadbar:</td><td>{{ moderator_checkbox(['courses',course.id,'downloadable'], course.downloadable) }}</td></tr>
 					<tr><td>Short:</td><td>{{ moderator_editor(['courses',course.id,'short'], course.short) }}</td></tr>
 					<tr><td>Handle:</td><td>{{ moderator_editor(['courses',course.id,'handle'], course.handle) }}</td></tr>
diff --git a/templates/dbstatus.html b/templates/dbstatus.html
new file mode 100644
index 0000000000000000000000000000000000000000..384e3a84446cd6003c08ae676d673cffb72281fc
--- /dev/null
+++ b/templates/dbstatus.html
@@ -0,0 +1,53 @@
+{% extends "base.html" %}
+{% block content %}
+<div class="panel panel-default">
+	<div class="panel-heading"><h1 class="panel-title">Cluster</b></h1></div>
+	<ul class="list-group">
+		{% for clustername, clusternodes in clusters.items() %}
+		<li class="list-group-item"><strong>{{vars[clusternodes|first]['wsrep_cluster_name']}}</strong> ({{statuses[clusternodes|first]['wsrep_cluster_status']}}, Größe={{statuses[clusternodes|first]['wsrep_cluster_size']}}, Konfiguration={{statuses[clusternodes|first]['wsrep_cluster_conf_id']}})
+		<ul class="list-group">
+			{% for host in clusternodes %}
+			<li class="list-group-item list-group-item-{{{'1': 'warning', '2': 'warning', '3': 'info', '4': 'success'}.get(statuses[host]['wsrep_local_state'], 'danger')}}"><a href="#{{host|tagid}}">{{vars[host]['wsrep_node_name']}}</a> ({{statuses[host]['wsrep_local_state_comment']}}, Letzte Änderung={{statuses[host]['wsrep_last_committed']}}, Recv.-Avg.={{statuses[host]['wsrep_local_recv_queue_avg']}}, Send-Avg.={{statuses[host]['wsrep_local_send_queue_avg']}})</li>
+			{% endfor %}
+		</ul>
+		</li>
+		{% endfor %}
+	</ul>
+	</table>
+</div>
+{% for host, status in statuses.items() %}
+<div class="panel panel-default" id="{{host|tagid}}">
+	<div class="panel-heading"><h1 class="panel-title">{{vars[host]['wsrep_node_name']}}</h1></div>
+	<div class="row" style="margin: 0px;">
+		<div class="col-xs-6 table-responsive">
+			<table class="table">
+				<tr>
+					<th>Variable</th>
+					<th>Wert</th>
+				</tr>
+				{% for key, value in vars[host].items() %}
+				<tr>
+					<td>{{key}}</td>
+					<td>{{value}}</td>
+				</tr>
+				{% endfor %}
+			</table>
+		</div>
+		<div class="col-xs-6 table-responsive">
+			<table class="table">
+				<tr>
+					<th>Status-Variable</th>
+					<th>Wert</th>
+				</tr>
+				{% for key, value in status.items() %}
+				<tr>
+					<td>{{key}}</td>
+					<td>{{value}}</td>
+				</tr>
+				{% endfor %}
+			</table>
+		</div>
+	</div>
+</div>
+{% endfor %}
+{% endblock %}
diff --git a/templates/index.html b/templates/index.html
index fbb2cbc3de210ccef6341a03ef077f6e66965da0..70b72bf0971752f5b2c93093936a067a29df8f05 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -111,7 +111,7 @@
 							<ul class="list-group" style="margin: 0px;">
 								{% for i in g.list %}
 								<li class="list-group-item list-group-item-condensed">
-									{{i.time|time}} <a href="{{url_for('course', id=i.course_id)}}">{{i.course.title}}</a>: <a href="{{url_for('course', id=i.course_id)}}#lecture-{{i.id}}">{{i.title}}</a> {{livelabel(i.live, False)}}
+									{{i.time|time}} <a href="{{url_for('course', handle=i.course.handle)}}">{{i.course.title}}</a>: <a href="{{url_for('course', handle=i.course.handle)}}#lecture-{{i.id}}">{{i.title}}</a> {{livelabel(i.live, False)}}
 								</li>
 								{% endfor %}
 							</ul>
diff --git a/templates/macros.html b/templates/macros.html
index c0f27fed22d74b2e0e89451c79d6993a3e20b1c7..4acc84304ea6b645338d74701ee4a63fc127a470 100644
--- a/templates/macros.html
+++ b/templates/macros.html
@@ -97,6 +97,25 @@ $(function() {
 		videojs("videoplayer").createModal('',{"uncloseable": true }).contentEl().innerHTML='<div class="hidden-print alert alert-danger" role="alert">{{ msg|safe }}</div>';
 		
 	{% endfor %}
+
+	//markers
+	$.ajax({method: "GET", url: "{{url_for('chapters',lectureid=lecture.id, json=1)}}", dataType: "json",
+		success: function (data) {
+			videojs("videoplayer").markers({
+				markerStyle: {
+					'width':'5px',
+					'border-radius': '40%',
+					'background-color': 'black'
+				},
+				markerTip:{
+					display: true,
+					text: function(marker) {
+						return marker.text;
+					}
+				},
+				markers: data});
+		}});
+
 });
 </script>
 {% endmacro %}
@@ -165,7 +184,7 @@ $('#embedcodebtn').popover(
 {% endmacro %}
 
 {% macro lecture_list_item(lecture,videos,global_permissions) %}
-<li class="list-group-item" id="lecture-{{lecture.id}}">
+<li class="list-group-item{% if lecture.norecording %} text-muted{% endif %}" id="lecture-{{lecture.id}}">
 	<div class="row">
 		{% if ismod() or (videos|length > 0)  %}
 			<div style="background-image: url('{{ config.VIDEOPREFIX }}/thumbnail/l_{{lecture.id}}.jpg')" class="col-sm-2 col-xs-12 thumbnailimg">
@@ -190,6 +209,7 @@ $('#embedcodebtn').popover(
 				<li>{{ moderator_editor(['lectures',lecture.id,'internal'], lecture.internal) }}</li>
 				<li>Sichtbar: {{ moderator_checkbox(['lectures',lecture.id,'visible'], lecture.visible) }}</li>
 				<li>Livestream geplant: {{ moderator_checkbox(['lectures',lecture.id,'live'], lecture.live) }}</li>
+				<li>Wird nicht aufgenommen: {{ moderator_checkbox(['lectures',lecture.id,'norecording'], lecture.norecording) }}</li>
 				<li>Hörsaal: {{ moderator_editor(['lectures',lecture.id,'place'], lecture.place) }} </li>
 				{% endif %}
 			</ul>
diff --git a/templates/sortlog.html b/templates/sortlog.html
index 184af50c9510d00fcd17941ceb9c187ddb532d66..21d05c37147e80c5176e31520c95b368506d9e53 100644
--- a/templates/sortlog.html
+++ b/templates/sortlog.html
@@ -54,7 +54,7 @@
 						<td><a href="{{ config.VIDEOPREFIX }}/{{i.path}}">{{i.path}}</a></td>
 						<td><a href="{{url_for('course', id=i.course_id)}}">{{i.course_id}}</a></td>
 						<td><a href="{{url_for('course', id=i.course_id)}}#lecture-{{i.lecture_id}}">{{i.lecture_id}}</a></td>
-						<td>{{i.id}}</td>
+						<td>{{i.video_id}}</td>
 					</tr>
 				{% endfor %}
 			</table>
diff --git a/timetable.py b/timetable.py
index 2ccb548870b4a16c87876ed5b1e0b04eec5511e1..ad2a54dfeeb82a585466efdb0b7c4bc0e4d53cb9 100644
--- a/timetable.py
+++ b/timetable.py
@@ -26,7 +26,7 @@ def timetable():
 					SELECT lectures.*, courses.short, "course" AS sep, courses.*
 					FROM lectures 
 					JOIN courses ON (lectures.course_id = courses.id) 
-					WHERE time < ? and time > ?
+					WHERE time < ? AND time > ? AND NOT norecording AND NOT external
 					ORDER BY time ASC''', i['date']+timedelta(weeks=2), i['date']-timedelta(weeks=2)):
 			# we can not use the where clause of sql to match against the time, because sqlite and mysql use a different syntax -.-
 			# we still use it to only get the lectures for a 3 week periode