Commit dbe16b57 authored by Andreas Valder's avatar Andreas Valder
Browse files

Merge branch 'master' of git.fsmpi.rwth-aachen.de:videoagwebsite/videoagwebsite

parents afe05743 608898c2
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'})
......@@ -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()
......
......@@ -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,
......
......@@ -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',
......
......@@ -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
......
......@@ -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 {
......
......@@ -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
});
 
......
/*! 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
.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 {