diff --git a/db_example.sql b/db_example.sql index 26c85b08b8c653de269a6d4dc7d957d56f9e88c4..3c6754b52c22cf065ff3648f41b334a77557b49a 100644 --- a/db_example.sql +++ b/db_example.sql @@ -310,37 +310,38 @@ INSERT INTO "courses_data" VALUES(300,1,1,0,'AStA Vortragsabend','Vortragsabend' INSERT INTO "courses_data" VALUES(301,1,1,0,'HYP2016','HYP2016','16ss-hyp','Institut für Mathematik','Mathematik',0,60,'2016-08-01 13:21:48','2016-08-01 14:01:30','2016ss','',1,0,'XVI International Conference on Hyperbolic Problems\nTheory, Numerics, Applications \n\nLearn more at <a href="http://www.hyp2016.de/">hyp2016.de</a>','','',''); INSERT INTO "courses_data" VALUES(302,0,1,1,'Neue Veranstaltung','','16ss-new302','','',0,64,'2016-08-05 01:21:34','2016-08-05 01:24:59','2016ss','',1,0,'','','',''); INSERT INTO "courses_data" VALUES(303,1,1,0,'Aachen2025','Aachen2025','160923-aachen2025','REGINA,TEMA','',0,64,'2016-08-05 01:22:04','2016-08-05 01:25:27','2016ss','',1,0,'','','',''); -INSERT INTO "formats" VALUES(0,'Format unbekannt','','','',-100,0); -INSERT INTO "formats" VALUES(1,'SDV','sd,pal','720x576','4:3',1,0); -INSERT INTO "formats" VALUES(2,'SDV','ws,palws','720x576','16:9',1,0); -INSERT INTO "formats" VALUES(3,'HDV','hdv','1440x1080','16:9',9,6); -INSERT INTO "formats" VALUES(4,'1080p','1080p','1920x1080','16:9',10,7); -INSERT INTO "formats" VALUES(5,'720p','720p','1280x720','16:9',5,10); -INSERT INTO "formats" VALUES(6,'640x512','','640x512','16:9',-1,0); -INSERT INTO "formats" VALUES(7,'MP3','mp3','','',-10,0); -INSERT INTO "formats" VALUES(8,'720p F4V','','1280x720','16:9',4,10); -INSERT INTO "formats" VALUES(9,'480p','ipod,480p','640x480','4:3',2,8); -INSERT INTO "formats" VALUES(10,'360p','ipod,360p','640x360','16:9',3,8); -INSERT INTO "formats" VALUES(11,'AAC','','','',-2,0); -INSERT INTO "formats" VALUES(12,'270p','ipodsmall,270p','480x270','16:9',1,3); -INSERT INTO "formats" VALUES(13,'Screencast','','1024x768','4:3',4,5); -INSERT INTO "formats" VALUES(14,'WAV','wav','48 kHz','',0,0); -INSERT INTO "formats" VALUES(15,'DVD','dvd,iso','720x576','',1,0); -INSERT INTO "formats" VALUES(16,'WMV','wmv','','',-1,0); -INSERT INTO "formats" VALUES(17,'iPod','','','',0,3); -INSERT INTO "formats" VALUES(18,'original','','','',0,0); -INSERT INTO "formats" VALUES(19,'Screencast','','800x600','4:3',3,4); -INSERT INTO "formats" VALUES(20,'Screencast','','1280x800','16:10',2,5); -INSERT INTO "formats" VALUES(21,'Tonspur','tonspur,m4a','','',-10,0); -INSERT INTO "formats" VALUES(22,'360p WebM','360p webm','640x360','16:9',3,0); -INSERT INTO "formats" VALUES(23,'720p WebM','720p webm','1280x720','16:9',5,0); -INSERT INTO "formats" VALUES(24,'1080p WebM','1080p webm','1920x1080','16:9',10,0); -INSERT INTO "formats" VALUES(25,'Test 1','','1280x720','16:9',0,0); -INSERT INTO "formats" VALUES(26,'Test 2','','1280x720','16:9',0,0); -INSERT INTO "formats" VALUES(27,'Test 3','','1280x720','16:9',0,0); -INSERT INTO "formats" VALUES(28,'LQ iPod','','640x360','16:9',-7,-2); -INSERT INTO "formats" VALUES(29,'1080p 8M','1080p','1920x1080','16:9',0,14); -INSERT INTO "formats" VALUES(30,'Streamdump','streamdump','1920x1080','16:9',0,-10); +INSERT INTO "formats" VALUES(0,'Format unbekannt','','','',-100,0,'video/mp4'); +INSERT INTO "formats" VALUES(1,'SDV','sd,pal','720x576','4:3',1,0,'video/mp4'); +INSERT INTO "formats" VALUES(2,'SDV','ws,palws','720x576','16:9',1,0,'video/mp4'); +INSERT INTO "formats" VALUES(3,'HDV','hdv','1440x1080','16:9',9,6,'video/mp4'); +INSERT INTO "formats" VALUES(4,'1080p','1080p','1920x1080','16:9',10,7,'video/mp4'); +INSERT INTO "formats" VALUES(5,'720p','720p','1280x720','16:9',5,10,'video/mp4'); +INSERT INTO "formats" VALUES(6,'640x512','','640x512','16:9',-1,0,'video/mp4'); +INSERT INTO "formats" VALUES(7,'MP3','mp3','','',-10,0,'video/mp4'); +INSERT INTO "formats" VALUES(8,'720p F4V','','1280x720','16:9',4,10,'video/mp4'); +INSERT INTO "formats" VALUES(9,'480p','ipod,480p','640x480','4:3',2,8,'video/mp4'); +INSERT INTO "formats" VALUES(10,'360p','ipod,360p','640x360','16:9',3,8,'video/mp4'); +INSERT INTO "formats" VALUES(11,'AAC','','','',-2,0,'video/mp4'); +INSERT INTO "formats" VALUES(12,'270p','ipodsmall,270p','480x270','16:9',1,3,'video/mp4'); +INSERT INTO "formats" VALUES(13,'Screencast','','1024x768','4:3',4,5,'video/mp4'); +INSERT INTO "formats" VALUES(14,'WAV','wav','48 kHz','',0,0,'video/mp4'); +INSERT INTO "formats" VALUES(15,'DVD','dvd,iso','720x576','',1,0,'video/mp4'); +INSERT INTO "formats" VALUES(16,'WMV','wmv','','',-1,0,'video/mp4'); +INSERT INTO "formats" VALUES(17,'iPod','','','',0,3,'video/mp4'); +INSERT INTO "formats" VALUES(18,'original','','','',0,0,'video/mp4'); +INSERT INTO "formats" VALUES(19,'Screencast','','800x600','4:3',3,4,'video/mp4'); +INSERT INTO "formats" VALUES(20,'Screencast','','1280x800','16:10',2,5,'video/mp4'); +INSERT INTO "formats" VALUES(21,'Tonspur','tonspur,m4a','','',-10,0,'video/mp4'); +INSERT INTO "formats" VALUES(22,'360p WebM','360p webm','640x360','16:9',3,0,'video/mp4'); +INSERT INTO "formats" VALUES(23,'720p WebM','720p webm','1280x720','16:9',5,0,'video/mp4'); +INSERT INTO "formats" VALUES(24,'1080p WebM','1080p webm','1920x1080','16:9',10,0,'video/mp4'); +INSERT INTO "formats" VALUES(25,'Test 1','','1280x720','16:9',0,0,'video/mp4'); +INSERT INTO "formats" VALUES(26,'Test 2','','1280x720','16:9',0,0,'video/mp4'); +INSERT INTO "formats" VALUES(27,'Test 3','','1280x720','16:9',0,0,'video/mp4'); +INSERT INTO "formats" VALUES(28,'LQ iPod','','640x360','16:9',-7,-2,'video/mp4'); +INSERT INTO "formats" VALUES(29,'1080p 8M','1080p','1920x1080','16:9',0,14,'video/mp4'); +INSERT INTO "formats" VALUES(30,'Streamdump','streamdump','1920x1080','16:9',0,-10,'video/mp4'); +INSERT INTO "formats" VALUES(31,'Livestream','hls','1920x1080','16:9',-15,-10,'application/x-mpegURL'); INSERT INTO "lectures_data" VALUES(1,2,1,NULL,0,'egal',0,'Einführung zur Berechenbarkeit','','','','','2007-10-19 12:00:00',0,'0000-00-00 00:00:00','0000-00-00 00:00:00','','pub/07ws-buk/07ws-buk-071019-title.jpg'); INSERT INTO "lectures_data" VALUES(2,2,1,NULL,0,'egal',0,'Einführung zur Berechenbarkeit','','','','','2007-10-23 08:30:00',0,'0000-00-00 00:00:00','0000-00-00 00:00:00','','pub/07ws-buk/07ws-buk-071023-title.jpg'); INSERT INTO "lectures_data" VALUES(3,2,1,NULL,0,'egal',0,'Einführung zur Berechenbarkeit','','','','','2007-10-26 12:00:00',0,'0000-00-00 00:00:00','0000-00-00 00:00:00','','pub/07ws-buk/07ws-buk-071026-title.jpg'); diff --git a/db_schema.sql b/db_schema.sql index d02c4fabd6aa4ef0f6e2df62d01a75a67463eb4c..b4b0b54c056b34d43ab362ca11940194216e72f7 100644 --- a/db_schema.sql +++ b/db_schema.sql @@ -76,7 +76,8 @@ CREATE TABLE IF NOT EXISTS `formats` ( `resolution` varchar(16) NOT NULL, `aspect` varchar(16) NOT NULL, `prio` INTEGER NOT NULL DEFAULT '0', - `player_prio` INTEGER NOT NULL DEFAULT '0' + `player_prio` INTEGER NOT NULL DEFAULT '0', + `mimetype` varchar(32) NOT NULL ); CREATE TABLE IF NOT EXISTS `lectures_data` ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, diff --git a/server.py b/server.py index 4ae2bed0c5388b0bb17b0d8fc47e29a04543a8dd..111eeceee0477621bfd890858121d2b11ab41084 100644 --- a/server.py +++ b/server.py @@ -312,8 +312,14 @@ def index(): GROUP BY videos.lecture_id ORDER BY MAX(videos.time_updated) DESC LIMIT 6 ''',ismod()) + livestreams = query('''SELECT streams.handle AS live, lectures.*, "course" AS sep, courses.* + FROM streams + JOIN lectures ON lectures.id = streams.lecture_id + 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()) featured = query('SELECT * FROM featured WHERE NOT deleted AND (? OR visible)', ismod()) - return render_template('index.html', latestvideos=latestvideos, upcomming=upcomming, featured=featured) + return render_template('index.html', latestvideos=livestreams+latestvideos, upcomming=upcomming, featured=featured) @app.route('/course') @register_navbar('Videos', icon='film') @@ -327,6 +333,14 @@ def courses(): groupedby = 'semester' return render_template('courses.html', courses=courses, groupedby=groupedby) +def genlive(streams): + for stream in streams: + stream['visible'] = True + stream['downloadable'] = False + stream['path'] = 'pub/hls/%s.m3u8'%stream['live'] + stream['file_size'] = 0 + return streams + @app.route('/course/<handle>') @app.route('/course/<int:id>') @handle_errors('courses', 'Diese Veranstaltung existiert nicht!', 404, IndexError) @@ -354,6 +368,13 @@ def course(id=None, handle=None): WHERE lectures.course_id= ? AND (? OR videos.visible) ORDER BY lectures.time, formats.prio DESC ''', course['id'], ismod()) + livestreams = query('''SELECT streams.handle AS live, streams.lecture_id, formats.description AS format_description, formats.player_prio, formats.prio + FROM streams + JOIN lectures ON lectures.id = streams.lecture_id + JOIN formats ON formats.keywords = "hls" + WHERE streams.active AND (? OR streams.visible) AND lectures.course_id = ? + ''', ismod(), course['id']) + videos += genlive(livestreams) return render_template('course.html', course=course, lectures=lectures, videos=videos) @app.route('/faq') @@ -367,13 +388,20 @@ def faq(): def lecture(id): lecture = query('SELECT * FROM lectures WHERE id = ? AND (? OR visible)', id, ismod())[0] videos = query(''' - SELECT videos.*, (videos.downloadable AND courses.downloadable) as downloadable, formats.description AS format_description, formats.player_prio, formats.prio + SELECT videos.*, (videos.downloadable AND courses.downloadable) as downloadable, formats.description AS format_description, formats.player_prio, formats.prio, formats.mimetype FROM videos JOIN formats ON (videos.video_format = formats.id) JOIN courses ON (courses.id = ?) WHERE videos.lecture_id = ? AND (? OR videos.visible) ORDER BY formats.prio DESC ''', lecture['course_id'], lecture['id'], ismod()) + livestreams = query('''SELECT streams.handle AS live, streams.lecture_id, formats.description AS format_description, formats.player_prio, formats.prio, formats.mimetype + FROM streams + JOIN lectures ON lectures.id = streams.lecture_id + JOIN formats ON formats.keywords = "hls" + WHERE streams.active AND (? OR streams.visible) AND lectures.id = ? + ''', ismod(), id) + videos += genlive(livestreams) 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: @@ -531,7 +559,7 @@ def auth(): # For use with nginx auth_request cookie = int(request.cookies['tracking']) else: cookie = random.getrandbits(8*8-1) - if url.endswith('jpg') or ismod(): + if url.endswith('jpg') or ismod() or url.startswith('pub/hls/'): return "OK", 200 perms = query('''SELECT videos.path, videos.id AS vid, perm.* FROM videos diff --git a/templates/macros.html b/templates/macros.html index 87de4e31bdd5d04859a769a5d9d800ef8ff21905..7f7c80b4cc76d18a6d1eed5753a13d08e505609a 100644 --- a/templates/macros.html +++ b/templates/macros.html @@ -5,7 +5,7 @@ <div class="row"> <img class="col-xs-4" style="max-height: 120px; height: auto; width:170px" src="{{ config.VIDEOPREFIX }}/{{ lecture['titlefile'] }}" alt="Vorschaubild" onerror="this.src='{{url_for('static',filename='no-thumbnail.png')}}'; this.onerror=''; "> <div class="col-xs-4"> - <span><strong>{{ lecture.course.short }}</strong>{% if ismod() %} <i>({{lecture.course_id}})</i>{% endif %}</span><br> + <span>{% if lecture.live %}<span class="label label-danger">Live</span> {% endif %}<strong>{{ lecture.course.short }}</strong>{% if ismod() %} <i>({{lecture.course_id}})</i>{% endif %}</span><br> <span>{% if ismod() %}ID: {{lecture.id}}{% endif %}</span><br> <span>{{ lecture['time'] }}</span> {% if lecture['speaker'] %} @@ -24,6 +24,7 @@ <img style="width: 100%;" src="{{ config.VIDEOPREFIX }}/{{ lecture['titlefile'] }}" alt="Vorschaubild" onerror="this.src='{{url_for('static',filename='no-thumbnail.png')}}'; this.onerror=''; "> </li> <li> + {% if lecture.live %}<span class="label label-danger">Live</span> {% endif %} <strong>{{ lecture.course.short }}</strong> {{ lecture['time'] }} </li> {% if lecture['speaker'] %} @@ -47,7 +48,7 @@ {% macro player(lecture, videos) %} <video id="videoplayer" style="width: 100%" class="video-js vjs-default-skin vjs-big-play-centered" width="640" height="320" controls data-wasnotplayed="1" data-setup='{ "plugins" : {"hotkeys": {"seekStep": 15, "enableVolumeScroll": false, "alwaysCaptureHotkeys": true}, "videoJsResolutionSwitcher": { "ui": true, "default": "720p", "dynamicLabel": true } }, "customControlsOnMobile": true, "playbackRates": [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3, 3.25, 3.5, 3.75, 4] }'> {% for v in videos|sort(attribute='player_prio', reverse=True) %} - <source type="video/mp4" src="{{ config.VIDEOPREFIX }}/{{ v.path }}" label="{{ v.format_description }}"/> + <source type="{{ v.mimetype }}" src="{{ config.VIDEOPREFIX }}/{{ v.path }}" label="{{ v.format_description }}"/> {% endfor %} <track srclang="de" kind="chapters" src="{{ url_for('chapters',lectureid=lecture.id) }}" /> </video> @@ -171,7 +172,7 @@ $('#embedcodebtn').popover( </div> <span class="col-sm-3 col-xs-12"> <ul class="list-unstyled"> - <li>{{ moderator_editor(['lectures',lecture.id,'title'], lecture.title) }}</li> + <li>{% if videos|selectattr("live")|list|length %}<span class="label label-danger">Live</span> {% endif %}{{ moderator_editor(['lectures',lecture.id,'title'], lecture.title) }}</li> {% if lecture.speaker or ismod() %}<li>Gehalten von {{ moderator_editor(['lectures',lecture.id,'speaker'], lecture.speaker) }}</li>{% endif %} <li>{{ moderator_editor(['lectures',lecture.id,'time'], lecture.time) }} </li> <li>Dauer: {{ moderator_editor(['lectures',lecture.id,'duration'], lecture.duration) }} min</li>