diff --git a/db.py b/db.py
index bb3996fb199be9d34e2f4822104926c2fa88460a..a79326df3f9779ebbcd8eb6545df41a195e69fb8 100644
--- a/db.py
+++ b/db.py
@@ -1,5 +1,4 @@
 from server import *
-
 if config['DB_ENGINE'] == 'sqlite':
 	import sqlite3
 
diff --git a/db_schema.sql b/db_schema.sql
index ae064216eb3debe2adc69bfd81f3a518ea130289..e3e3efac87fa91e8378feed9a0cb76730d071b15 100644
--- a/db_schema.sql
+++ b/db_schema.sql
@@ -16,6 +16,13 @@ CREATE TABLE IF NOT EXISTS `areas` (
   `rank` INTEGER DEFAULT NULL,
   `coordinates` varchar(30) NOT NULL
 );
+CREATE TABLE IF NOT EXISTS `tokens` (
+  `token` varchar(20) NOT NULL PRIMARY KEY,
+  `valid_until` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `created_by` INTEGER NOT NULL,
+  `created_for` varchar(255) NOT NULL,
+  `lecture_id` INTEGER NOT NULL
+);
 CREATE TABLE IF NOT EXISTS `changelog` (
 `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
   `when` datetime NOT NULL,
diff --git a/edit.py b/edit.py
index c04b7917db3a6d6790c9c95dee51a961a904b26e..f0cc639b08c5968f533fb939281dfce31d180305 100644
--- a/edit.py
+++ b/edit.py
@@ -184,17 +184,23 @@ def edit(prefix='', ignore=[]):
 @mod_required
 @csrf_protect
 def create(table):
+	args = request.values.items()
+	if (request.method == 'POST') and (request.get_json()):
+		args = request.get_json().items()
+	id = internal_create(table, args, session["user"]["dbid"])
+	if 'ref' in request.values:
+		return redirect(request.values['ref'])
+	return str(id), 200
+
+def internal_create(table, args, user_dbid):
 	assert table in editable_tables
-	defaults = {'created_by': session['user']['dbid'], 'time_created': datetime.now(), 'time_updated': datetime.now()}
+	defaults = {'created_by': user_dbid, 'time_created': datetime.now(), 'time_updated': datetime.now()}
 	columns = []
 	values = []
 	for column, val in defaults.items():
 		if column in editable_tables[table]['creationtime_fields']:
 			columns.append(column)
 			values.append(val)
-	args = request.values.items()
-	if (request.method == 'POST') and (request.get_json()):
-		args = request.get_json().items()
 	for column, val in args:
 		if (column == 'ref') or (column == '_csrf_token'):
 			continue
@@ -205,10 +211,8 @@ def create(table):
 	id = modify('INSERT INTO %s (%s) VALUES (%s)'%(editable_tables[table]['table'],
 				','.join(columns), ','.join(['?']*len(values))), *values)
 	if table == 'courses':
-		set_responsible(id, session['user']['dbid'], 1)
-	if 'ref' in request.values:
-		return redirect(request.values['ref'])
-	return str(id), 200
+		set_responsible(id, user_dbid, 1)
+	return id
 
 @app.route('/internal/changelog')
 @register_navbar('Changelog', icon='book', group='weitere')
diff --git a/server.py b/server.py
index 6e969ee95f51423e13f3a72c5273be5f54d68902..503edc207535fad1738f358bae8284453e608cf1 100644
--- a/server.py
+++ b/server.py
@@ -1,5 +1,6 @@
 from flask import Flask, g, request, url_for, redirect, session, render_template, flash, Response, make_response
 from werkzeug.routing import Rule
+from werkzeug.utils import secure_filename
 from functools import wraps
 from datetime import date, timedelta, datetime, time, MINYEAR
 import os
@@ -508,10 +509,85 @@ def dbstatus():
 		clusters[cluster].append(host)
 	return render_template('dbstatus.html', clusters=clusters, statuses=status, vars=variables), 200
 
+def allowed_file(filename):
+	return '.' in filename and filename.split(".")[-1].lower() in ["mkv", "mp4", "webm", "mov", "avi", "wmv"]
+
+"""
+CREATE TABLE IF NOT EXISTS `tokens` (
+  `token` varchar(20) NOT NULL PRIMARY KEY,
+  `valid_until` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `created_by` INTEGER NOT NULL,
+  `created_for` varchar(255) NOT NULL,
+  `lecture_id` INTEGER NOT NULL,
+);
+"""
+
+def get_token(token):
+	res = query('select * from tokens where (token = ?)',token)
+	if res:
+		return res[0]
+	return None
+
+UPLOAD_FOLDER = "/mnt/IntenseShit/sciebo/code/videoag-website/"
+
+@app.route('/upload', methods=['GET', 'POST'])
+def upload():
+	if request.method == 'GET':
+		return render_template("upload.html"), 200
+	elif request.method == 'POST':
+		if not "uploadtoken" in request.values:
+			return "Token missing", 403
+		tokenobj = get_token(request.values["uploadtoken"])
+		if not tokenobj:
+			return "Token invalid!", 403
+		print(tokenobj["valid_until"])
+		print(datetime.now())
+		if tokenobj["valid_until"] <= datetime.now():
+			return "Token ist nicht mehr valide!", 403
+		user_dbid = -1 # create one for each token?
+		videos = []
+		i = 0
+		while f"time[{i}]" in request.values:
+			if not all(key in request.values for key in [f"time[{i}]", f"title[{i}]", f"speaker[{i}]", f"duration[{i}]", f"place[{i}]"]):
+				return "Es fehlt ein Argument!"+str([k for k in request.values.keys()]), 403
+			if f'videofile[{i}]' not in request.files:
+				return "Keine Datei übertragen!", 403
+			if request.files[f'videofile[{i}]'] == '':
+				return "Dateiname ist leer!", 403
+			if not allowed_file(request.files[f'videofile[{i}]'].filename):
+				return f'Dateityp {request.files[f"videofile[{i}]"].filename.split(".")[-1]} nicht erlaubt!', 403
+			videos.append(({"time": request.values[f"time[{i}]"],
+							"title": request.values[f"title[{i}]"],
+							"speaker": request.values[f"speaker[{i}]"],
+							"duration": request.values[f"duration[{i}]"],
+							"place": request.values[f"place[{i}]"],
+							"comment": request.values[f"comment[{i}]"] if f"comment[{i}]" in request.values else "",
+							"internal": "Uploaded with the token from "+str(tokenobj["created_for"]),
+							"course_id": tokenobj["lecture_id"],
+							"ref": "/80",
+							"visible": False},request.files[f'videofile[{i}]']))
+			i += 1
+		for vid in videos:
+			lec_id = internal_create("lectures", vid[0].items(), user_dbid)
+			lecturehandle = query('select * from courses_data where (id = ?)',tokenobj["lecture_id"])[0]["handle"]
+			datestring = vid[0]["time"].split(" ")[0].replace("-","")
+			fileformat = vid[1].filename.split(".")[-1]
+			filename = f"{lecturehandle}-{datestring}.{fileformat}"
+			path = os.path.join(UPLOAD_FOLDER, filename)
+			if os.path.exists(path):
+				timestring = vid[0]["time"].split(" ")[1].replace(":","")
+				filename = f"{lecturehandle}-{datestring}-{timestring}.{fileformat}"
+				path = os.path.join(UPLOAD_FOLDER, filename)
+				if os.path.exists(path):
+					return f"Eine Vorlesung existiert bereits am {datestring}!", 403
+			vid[1].save(path)
+			sorter.sort_autoencode_internal(filename)
+		return redirect(f"{request.base_url}/{lecturehandle}")
+
 def date_json_handler(obj):
 	return obj.isoformat() if hasattr(obj, 'isoformat') else obj
 
-from edit import edit_handler
+from edit import edit_handler, internal_create
 from jobmanagement import job_handler, job_handler_handle, job_set_state, schedule_job, cancel_job, restart_job
 import feeds
 import importer
diff --git a/sorter.py b/sorter.py
index 3da37e4f34bb3d9a208aa846e1478bc697a6289d..bbdee4ad961f4426c9d96247710d3d7969b59dd0 100644
--- a/sorter.py
+++ b/sorter.py
@@ -201,6 +201,10 @@ def sort_encoded(filename):
 @sort_api_token_required
 def sort_autoencode():
 	filename = request.values['path']
+	sort_autoencode_internal(filename)
+	return 'OK', 200
+
+def sort_autoencode_internal(filename):
 	path = 'autoencode/'+filename
 	matches, fmt = sort_file(filename)
 	if len(matches) != 1:
@@ -208,7 +212,6 @@ def sort_autoencode():
 		return "Could not match filename", 400
 	lecture = matches[0]
 	schedule_job('probe-raw', {'path': path, 'lecture_id': lecture['id'], 'import-chapters': True})
-	return 'OK', 200
 
 @job_handler('publish_video')
 def handle_published_video(jobid, jobtype, data, state, status):
diff --git a/templates/upload.html b/templates/upload.html
new file mode 100644
index 0000000000000000000000000000000000000000..965a18d0391827bf3dd95354b6e8dbe1e6d3e6b8
--- /dev/null
+++ b/templates/upload.html
@@ -0,0 +1,54 @@
+{% extends "base.html" %}
+{% block content %}
+<div class="panel-group">
+	<div class="panel panel-default">
+		<div class="panel-heading">
+			<h1 class="panel-title">Video Upload</h1>
+		</div>
+		<div class="panel-body">
+			<form method="post" enctype="multipart/form-data">
+				<label for="uploadtoken">Ihr Uploadtoken</label>
+				<input type="text" name="uploadtoken" id="uploadtoken" required></br></br>
+				<table class="table table-bordered" id="item_table">
+					<tr>
+						<th>Videodatei</th>
+						<th>Video-Titel</th>
+						<th>Gehalten von...</th>
+						<th>Datum/Uhrzeit der Vorlesung</th>
+						<th>Ort der Vorlesung</th>
+						<th>Dauer (in Minuten)</th>
+						<th>Kommentar</th>
+						<th><button type="button" onclick=addItem() name="add">X</button></th>
+					</tr>
+				</table>
+				<input type="submit" name="submit" value="Submit videos" />
+			</form>
+		</div>
+	</div>
+</div>
+<script type="text/javascript">
+	var videonumber = 0;
+	function getCurrentDate() {
+		return (new Date()).toISOString().replace(/(\d{4})\-(\d{2})\-(\d{2})T(\d{2}):(\d{2}):(\d{2}).*/, '$1-$2-$3 $4:$5:$6');
+	}
+	function removeItem(item) {
+		item.closest("tr").remove();
+		videonumber -= 1;
+	}
+	function addItem() {
+		var html = '';
+			html += '<tr>';
+			html += '<td><input type="file" name="videofile['+videonumber+']" class="form-control videofile" required/></td>';
+			html += '<td><input type="text" value="Vorlesung/Übung" name="title['+videonumber+']" class="form-control title" required/></td>';
+			html += '<td><input type="text" value="Prof. Mustermann" name="speaker['+videonumber+']" class="form-control speaker" required/></td>';
+			html += '<td><input type="text" value="'+getCurrentDate()+'" name="time['+videonumber+']" class="form-control time" required/></td>';
+			html += '<td><input type="text" value="H01" name="place['+videonumber+']" class="form-control place" required/></td>';
+			html += '<td><input type="text" value="60" name="duration['+videonumber+']" class="form-control duration" required/></td>';
+			html += '<td><input type="text" value="" name="comment['+videonumber+']" class="form-control comment" /></td>';
+			html += '<td><button type="button" name="remove" class="btn btn-danger btn-sm remove" onclick=removeItem($(this))>X</button></td></tr>';
+			videonumber += 1;
+		$('#item_table').append(html);
+	}
+	addItem();
+</script>
+{% endblock %}
\ No newline at end of file