194 lines
7.2 KiB
Plaintext
194 lines
7.2 KiB
Plaintext
<p>
|
|
Long videos are easier to navigate with chapter markers, so I've been
|
|
slowly adding chapter markers to the Q&A sessions for EmacsConf 2021.
|
|
I wrote an IkiWiki template and some Javascript code so that adding
|
|
chapter markers to the <a href="https://emacsconf.org/">EmacsConf wiki</a> should be just a matter of as
|
|
adding something like this:
|
|
</p>
|
|
|
|
<pre class="example" id="orgc9f48d3">[[!template id="chapters" vidid="mainVideo" data="""
|
|
00:00 Introduction
|
|
00:11 Upcoming Emacs 28 release
|
|
00:24 Org mode 9.5
|
|
00:57 Magit major release
|
|
01:18 Completion
|
|
01:51 Embark
|
|
02:12 tree-sitter
|
|
02:44 Collaborative editing
|
|
03:03 Graphical experiments
|
|
03:41 Community
|
|
04:00 libera.chat
|
|
"""]]
|
|
</pre>
|
|
|
|
<p>
|
|
That way, updating the talk pages with chapter descriptions should be
|
|
less reliant on my Emacs Lisp functions for generating HTML, so it's
|
|
more likely to be something other people can do.
|
|
</p>
|
|
|
|
<p>
|
|
If you happen to be interested in Emacs and you're planning to watch
|
|
the talks or Q&A sessions anyway, you can help add chapter markers to
|
|
videos that don't have them yet. You can either edit the wiki yourself
|
|
or e-mail me chapter timestamps at . You can also
|
|
help out by cross-referencing the chapter timestamps with the
|
|
discussion session on the page, so that people reading the questions
|
|
can see where to find the answers. If you're feeling extra-helpful,
|
|
you could even write down the answers for easy reference.
|
|
</p>
|
|
|
|
<p>
|
|
Here are a few pages that have long Q&A sessions. I've linked to the
|
|
autogenerated captions in the Discussion sections.
|
|
</p>
|
|
|
|
<ul class="org-ul">
|
|
<li><a href="https://emacsconf.org/2021/talks/bindat/">https://emacsconf.org/2021/talks/bindat/</a></li>
|
|
<li><a href="https://emacsconf.org/2021/talks/faster/">https://emacsconf.org/2021/talks/faster/</a></li>
|
|
<li><a href="https://emacsconf.org/2021/talks/janitor/">https://emacsconf.org/2021/talks/janitor/</a></li>
|
|
<li><a href="https://emacsconf.org/2021/talks/maintainers/">https://emacsconf.org/2021/talks/maintainers/</a></li>
|
|
</ul>
|
|
|
|
<p>
|
|
You can call dibs by editing
|
|
<a href="https://etherpad.wikimedia.org/p/emacsconf-2021-volunteers">https://etherpad.wikimedia.org/p/emacsconf-2021-volunteers</a> .
|
|
</p>
|
|
|
|
<p>
|
|
Little steps towards making things easier to find! =)
|
|
</p>
|
|
|
|
<div class="outline-2" id="outline-container-org474ae9f">
|
|
<h2 id="org474ae9f">Behind the scenes</h2>
|
|
<div class="outline-text-2" id="text-org474ae9f">
|
|
<p>
|
|
I used the auto-generated captions from YouTube as a starting point,
|
|
since I could skim them easily. I found that the .ass format was
|
|
easier to speed-read than the .vtt format, so I used ffmpeg to convert
|
|
them. Then I used <code>emacsconf-subed-mark-chapter</code> from <a href="https://git.emacsconf.org/emacsconf-el/tree/emacsconf-subed.el">emacsconf-subed</a> to
|
|
capture the timestamps as a <code>.vtt</code> file.
|
|
</p>
|
|
|
|
<p>
|
|
This is what part of the autogenerated captions looks like:
|
|
</p>
|
|
|
|
<pre class="example" id="orgb81b7f0">...
|
|
Dialogue: 0,0:01:16.11,0:01:18.11,Default,,0,0,0,,First of all, in your opinion, what is
|
|
Dialogue: 0,0:01:18.11,0:01:20.11,Default,,0,0,0,,Emacs' achilles heel? it's obviously a
|
|
Dialogue: 0,0:01:20.11,0:01:22.35,Default,,0,0,0,,powerful tool but no tool is perfect
|
|
...
|
|
</pre>
|
|
|
|
<p>
|
|
and this is part of the chapters file I made:
|
|
</p>
|
|
|
|
<pre class="example" id="org500ffbf">00:00:26.319 --> 00:03:09.598
|
|
In your opinion, what is Emacs' Achilles heel?
|
|
|
|
00:03:09.599 --> 00:05:06.959
|
|
What is your opinion about the documentation of Emacs in other languages?
|
|
...
|
|
</pre>
|
|
|
|
<p>
|
|
I converted the timestamps to a simple text format handy for including in video descriptions and on the wiki.
|
|
</p>
|
|
|
|
<pre class="example" id="orgb16b397">[[!template id="chapters" vidid="qanda" data="""
|
|
00:00 Thanks
|
|
00:26 In your opinion, what is Emacs' Achilles heel?
|
|
03:09 What is your opinion about the documentation of Emacs in other languages?
|
|
...
|
|
]]
|
|
</pre>
|
|
|
|
<p>
|
|
A number of Emacs users browse the web without Javascript, so I wanted the chapter information to be available even then. Putting all the data into a pre tag seems like the easiest way to do it with an ikiwiki template. Here's the template I used:
|
|
</p>
|
|
|
|
<pre class="example" id="org5ca5c4f"><pre class="chapters" data-target="<TMPL_VAR vidid>">
|
|
<TMPL_VAR data>
|
|
</pre>
|
|
</pre>
|
|
|
|
<p>
|
|
I also modified the IkiWiki <code>htmlscrubber.pm</code> plugin to allow the attributes I wanted, like <code>data-target</code> and <code>data-start</code>.
|
|
</p>
|
|
|
|
<p>
|
|
If Javascript was enabled, I wanted people to be able to click on the chapters in order to jump to the right spot in the video. I split the content into lines, parsed out the timestamps, and replaced the pre tag with the list of links. I also added the chapters as a hidden track in the video so that I could use the <code>cuechange</code> event to highlight the current chapter. This is what I added to the <code>page.tmpl</code>:
|
|
</p>
|
|
|
|
<div class="org-src-container">
|
|
<pre class="src src-js2"><script>
|
|
// @license magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt txt CC0-1.0
|
|
// Copyright (c) 2021 Sacha Chua - CC0 Public Domain
|
|
function displayChapters(elem) {
|
|
var i;
|
|
var chapter;
|
|
var list = document.createElement('ol');
|
|
list.setAttribute('class', 'chapters');
|
|
var link;
|
|
var target = elem.getAttribute('data-target');
|
|
var video = document.getElementById(target);
|
|
var track;
|
|
if (video) {
|
|
track = video.addTextTrack('chapters');
|
|
track.mode = 'hidden';
|
|
}
|
|
var chapters = elem.textContent.split(/[ \t]*\n+[ \t]*/).forEach(function(line) {
|
|
var m = (line.match(/^(([0-9]+:)?[0-9]+:[0-9]+)[ \t]+(.*)/));
|
|
if (m) {
|
|
var start = m[1];
|
|
var text = m[3];
|
|
chapter = document.createElement('li');
|
|
link = document.createElement('a');
|
|
link.setAttribute('href', '#');
|
|
link.setAttribute('data-video', target);
|
|
link.setAttribute('data-start', start);
|
|
link.setAttribute('data-start-s', parseSeconds(start));
|
|
link.appendChild(document.createTextNode(m[1] + ' ' + text));
|
|
link.onclick = handleSubtitleClick;
|
|
chapter.appendChild(link);
|
|
list.appendChild(chapter);
|
|
if (track) {
|
|
var time = parseSeconds(start);
|
|
if (track.cues.length > 0) {
|
|
track.cues[track.cues.length - 1].endTime = time - 1;
|
|
}
|
|
track.addCue(new VTTCue(time, time, text));
|
|
}
|
|
}
|
|
})
|
|
if (track && track.cues.length > 0) {
|
|
video.addEventListener('durationchange', function() {
|
|
track.cues[track.cues.length - 1].endTime = video.duration;
|
|
});
|
|
track.addEventListener('cuechange', function() {
|
|
if (!this.activeCues[0]) return;
|
|
if (list.querySelector('.current')) {
|
|
list.querySelector('.current').className = '';
|
|
}
|
|
var chapter;
|
|
if (chapter = list.querySelector('a[data-start-s="' + this.activeCues[0].startTime + '"]')) {
|
|
chapter.parentNode.className = 'current';
|
|
}
|
|
});
|
|
}
|
|
elem.parentNode.replaceChild(list, elem);
|
|
}
|
|
|
|
document.querySelectorAll('pre.chapters').forEach(displayChapters);
|
|
|
|
// @license-end
|
|
</script>
|
|
</pre>
|
|
</div>
|
|
|
|
<p>
|
|
<code>handleSubtitleClick</code> is also part of the JS on that page. It sets the current time of the video and scrolls so that the video is in view.</p>
|
|
</div>
|
|
</div> |