emacs/var/elfeed/db/data/18/18a577e535252a8a395ee746703e10861149012a
2022-01-03 12:49:32 -06:00

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&amp;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&amp;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&amp;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 --&gt; 00:03:09.598
In your opinion, what is Emacs' Achilles heel?
00:03:09.599 --&gt; 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">&lt;pre class="chapters" data-target="&lt;TMPL_VAR vidid&gt;"&gt;
&lt;TMPL_VAR data&gt;
&lt;/pre&gt;
</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">&lt;script&gt;
// @license magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&amp;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 &gt; 0) {
track.cues[track.cues.length - 1].endTime = time - 1;
}
track.addCue(new VTTCue(time, time, text));
}
}
})
if (track &amp;&amp; track.cues.length &gt; 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
&lt;/script&gt;
</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>