diff --git a/server.py b/server.py index d9c54865ee2d71080d9ef1032057425b88bf627d..dde9977c0cd31f880bf177c9086f4897a01e1342 100644 --- a/server.py +++ b/server.py @@ -386,7 +386,13 @@ def suggest_chapter(lectureid): time = request.values['time'] text = request.values['text'] assert(time and text) - time = int(time) + try: + x = datetime.strptime(time,'%H:%M:%S') + time= timedelta(hours=x.hour,minutes=x.minute,seconds=x.second).total_seconds() + time = int(time) + except ValueError: + flash('Falsches Zeitformat, "%H:%M:%S" wird erwartet. Z.B. "01:39:42" für eine Kapitel bei Stunde 1, Minute 39, Sekunde 42') + submitter = None if not ismod(): submitter = request.environ['REMOTE_ADDR'] @@ -396,6 +402,16 @@ def suggest_chapter(lectureid): return redirect(request.values['ref']) return 'OK', 200 +@app.route('/chapters/<int:lectureid>') +def chapters(lectureid): + chapters = query("SELECT * FROM chapters WHERE lecture_id = ? and visible ORDER BY time DESC", lectureid) + last = None + for c in chapters: + c['start'] = c['time'] + c['end'] = last['start'] if last else 9999 + last = c + return Response(render_template('chapters.srt',chapters=chapters), 200, {'Content-Type':'text/vtt'}) + @app.route('/sitemap.xml') def sitemap(): pages=[] diff --git a/templates/chapters.srt b/templates/chapters.srt new file mode 100644 index 0000000000000000000000000000000000000000..cf76270a6df2c250e5c50e2dee412851b6c181ea --- /dev/null +++ b/templates/chapters.srt @@ -0,0 +1,15 @@ +{% from 'macros.html' import vtttime %} +WEBVTT + + +{% for c in chapters|sort(attribute='start') %} +{{ loop.index }} +{# time format is 'minutes:seconds.ms' we expect full seconds #} +{{ vtttime(c['start']) }} --> {{ vtttime(c['end']) }} +{{ c['text'] }} + +{% else %} +00:00:00.000 --> 23:59:59.999 +Video + +{% endfor %} diff --git a/templates/index.html b/templates/index.html index c3ee03142f57c887fc24d85780b1b9ded3044812..089c9120768d8e3ddd17464cdf5642aca85a96d4 100644 --- a/templates/index.html +++ b/templates/index.html @@ -12,34 +12,40 @@ <div class="row"> <div class="col-xs-12"> {% for msg in get_announcements() %} - <div class="alert alert-{{levels.get(msg.level, ('info', ''))[0]}}" role="alert"> - <ul class="list-unstyled"> - <li>{{ valueeditor(('announcements',msg.id,'text'), msg.text|safe) }}</li> - <li class="pull-right">{{ valuedeletebtn(('announcements',msg.id,'deleted')) }}</li> - <li class="pull-right" style="padding-right: 5px;"> - {% if not msg.visible %} - <a href="{{ url_for('edit', prefix='announcements.'+msg.id|string+'.', ref=request.url, visible=1) }}" class="btn btn-primary">Veröffentlichen</a> - {% elif msg.time_expire and msg.time_expire < datetime.now() %} - <a href="#" class="btn btn-danger disabled">Abgelaufen</a> - {% elif msg.time_publish and msg.time_publish > datetime.now() %} - <a href="#" class="btn btn-default disabled">Geplant</a> - {% else %} - <a href="#" class="btn btn-success disabled">Öffentlich</a> - {% endif %} - </li> - <li class="dropdown pull-right" style="padding-right: 5px;"> - <span class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">{{levels.get(msg.level, ('', 'Unbekannt'))[1]}} <span class="caret"></span></span> - <ul class="dropdown-menu"> - {% for level, descr in levels.items() %} - <li><a href="{{ url_for('edit', prefix='announcements.'+(msg.id|string)+'.', ref=request.url, level=level) }}">{{ descr[1] }}</a></li> - {% endfor %} - </ul> - </li> - <li> - Aktiv von {{ valueeditor(('announcements',msg.id,'time_publish'), msg.time_publish) }} - bis {{ valueeditor(('announcements',msg.id,'time_expire'), msg.time_expire) }} - </li> - </ul> + <div class="alert alert-{{levels.get(msg.level, ('info', ''))[0]}} row" role="alert"> + <div class="col-xs-12" style="padding: 0px;"> + <div class="pull-right">{{ valuedeletebtn(('announcements',msg.id,'deleted')) }}</div> + <div>{{ valueeditor(('announcements',msg.id,'text'), msg.text) }}</div> + </div> + <div class="col-xs-12" style="padding: 0px;"> + <ul class="list-inline"> + <li> + {% if not msg.visible %} + <a href="{{ url_for('edit', prefix='announcements.'+msg.id|string+'.', ref=request.url, visible=1) }}" class="btn btn-primary">Veröffentlichen</a> + {% elif msg.time_expire and msg.time_expire < datetime.now() %} + <a href="#" class="btn btn-danger disabled">Abgelaufen</a> + {% elif msg.time_publish and msg.time_publish > datetime.now() %} + <a href="#" class="btn btn-default disabled">Geplant</a> + {% else %} + <a href="#" class="btn btn-success disabled">Öffentlich</a> + {% endif %} + </li> + <li class="dropdown"> + <span class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">{{levels.get(msg.level, ('', 'Unbekannt'))[1]}} <span class="caret"></span></span> + <ul class="dropdown-menu"> + {% for level, descr in levels.items() %} + <li><a href="{{ url_for('edit', prefix='announcements.'+(msg.id|string)+'.', ref=request.url, level=level) }}">{{ descr[1] }}</a></li> + {% endfor %} + </ul> + </li> + <li> + Aktiv von {{ valueeditor(('announcements',msg.id,'time_publish'), msg.time_publish) }} + </li> + <li> + bis {{ valueeditor(('announcements',msg.id,'time_expire'), msg.time_expire) }} + </li> + </ul> + </div> </div> {% endfor %} </div> diff --git a/templates/lecture.html b/templates/lecture.html index 8c10cea3938669e9094248ef580cb99b44043028..f135ca57b73f2d54d700e9194d2fff3743721e34 100644 --- a/templates/lecture.html +++ b/templates/lecture.html @@ -1,6 +1,10 @@ {% from 'macros.html' import player %} {% from 'macros.html' import video_download_btn %} {% from 'macros.html' import video_embed_btn %} +{% from 'macros.html' import vtttime %} +{% from 'macros.html' import valueeditor %} +{% from 'macros.html' import valuedeletebtn %} +{% from 'macros.html' import valuecheckbox %} {% set page_border = 0 -%} {% extends "base.html" %} @@ -16,6 +20,7 @@ </div> <div class="col-sm-6 col-xs-12"> <ul class="list-inline pull-right"> + <li><button class="btn btn-default" id="hintnewchapter">Kapitelmarker vorschlagen</button></li> <li>{{ video_embed_btn(lecture.id) }}</li> <li class="dropdown">{{ video_download_btn(videos) }}</li> </ul> @@ -25,8 +30,51 @@ <div class="col-xs-12" style="padding: 0px"> {{ player(lecture, videos) }} </div> + <div class="col-xs-12" style="padding-top: 10px;"> + <p>Kapitel:</p> + <table class="table table-hover"> + <tr> + <th>Index</th> + <th>Start</th> + <th>Kapitel</th> + <th>Sichtbar</th> + <th></th> + </tr> + {% for c in chapters|sort(attribute='time') %} + <tr onclick=" $('.player video')[0].setCurrentTime({{c['time']}})"> + <td>{{ loop.index }}</td> + <td>{{ vtttime(c['time']) }}</td> + <td>{{ valueeditor(['chapters',c.id,'text'],c['text']) }}</td> + <td>{{ valuecheckbox(['chapters',c.id,'visible'], c.visible) }}</td> + <td>{{ valuedeletebtn(['chapters',c.id,'deleted']) }}</td> + </tr> + {% endfor %} + </table> + </div> </div> </div> </div> +<script> +$(function() { + $( "#hintnewchapter" ).popover( + { + html:true, + title:'Kapitelmarkierung vorschlagen', + content: function() { + var zeropad = function (num, places) { + var zero = places - num.toString().length + 1; + return Array(+(zero > 0 && zero)).join("0") + num; + }; + var timestamp = $('.player video')[0].currentTime + var h = zeropad( Math.trunc(timestamp/3600),2); + var m = zeropad( Math.trunc((timestamp%3600)/60),2); + var s = zeropad( Math.trunc(timestamp%60),2); + var timeasstring = h+':'+m+':'+s + return '<form method="post" action="{{url_for('suggest_chapter', ref=request.url, lectureid=lecture.id)}}"><input placeholder="00:00.000" name="time" type="text" value="'+timeasstring+'"><br><input placeholder="Kapitel" name="text" type="texz"><br><input type="submit" value="Vorschlagen"></form>' + + } + }) +}); +</script> {% endblock %} diff --git a/templates/macros.html b/templates/macros.html index 38aca6b73d868bc645e6fdf34842b5ed18dbc3e9..23b57e0a9e2ed999daa7bb1bac33c9c194411083 100644 --- a/templates/macros.html +++ b/templates/macros.html @@ -49,6 +49,7 @@ {% for v in videos|sort(attribute='player_prio', reverse=True) %} <source type="video/mp4" src="{{ config.VIDEOPREFIX }}/{{ v.path }}" title="{{ v.format_description }}"/> {% endfor %} + <track srclang="en" kind="chapters" src="{{ url_for('chapters',lectureid=lecture.id) }}" /> </video> <script> $(function($) @@ -186,3 +187,5 @@ $('#embedcodebtn').popover( </button> {% endif %} {% endmacro %} + +{% macro vtttime(time) %}{{ '%02d:%02d:%02d.000'|format( time//3600, (time//60)%60, time%60) }}{% endmacro %}