Repository navigation
Expand file tree
/
Copy pathtranscript.md
More file actions
89 lines (81 loc) · 2.98 KB
/
Copy pathtranscript.md
File metadata and controls
89 lines (81 loc) · 2.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<!--
# Jekyll include for including a transcript on a page, and invoking the
# YouTube Player API for the video.
#
# Disclaimer: Portions of this code were generated with help from ChatGPT before
# being edited for this site's purposes. Such changes and documentation are
# written by Simon Cook.
#
# This code is released under the MIT licence.
#
# Note on usage: If embedding manually, replace {{ include.ytvideo }} on line
# 31 with the correct video ID, and line 16 with the transcript.
-->
<div id="player"></div>
### Transcript
{% vtt_transcript include.transcript %}
<script>
// Find all transcript lines on the page, and register them to move the
// play head to their corresponding time.
const transcriptLines = document.querySelectorAll(".transcript-line");
transcriptLines.forEach(el => {
el.onclick = () => player.seekTo(parseFloat(el.dataset.start));
});
// YouTube API setup - create the video, and trigger the transcript syncing
// every 250ms
let player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
videoId: '{{ include.ytvideo }}',
events: {
onReady: () => setInterval(syncTranscript, 250)
}
});
}
// Smoothly scroll the container element so that the relevant element is in
// focus
function scrollToLine(el) {
const container = document.getElementById("transcript-box");
const containerTop = container.scrollTop;
const containerBottom = containerTop + container.clientHeight;
const elTop = el.offsetTop - container.offsetTop;
const elBottom = elTop + el.offsetHeight;
if (elTop < containerTop) {
// Line is above view — scroll up such that is the top most element
container.scrollTo({
top: elTop,
behavior: "smooth"
});
} else if (elBottom > containerBottom) {
// Line is below view — scroll down so that it is the last visible element
container.scrollTo({
top: elBottom - container.clientHeight,
behavior: "smooth"
});
}
}
// Local store of the last line we considered active - we only scroll the UI
// when this line changes.
let lastActive = Infinity;
// Search all transcript lines for the correct one to assume as active, give
// this the 'active' class if it is, and if it has changed trigger a scroll
// of the container.
function syncTranscript() {
const currentTime = player.getCurrentTime();
transcriptLines.forEach((line, i) => {
const start = parseFloat(line.dataset.start);
const next = transcriptLines[i + 1] ?
parseFloat(transcriptLines[i + 1].dataset.start) : Infinity;
const isActive = currentTime >= start && currentTime < next;
line.classList.toggle("active", isActive);
if (isActive && i != lastActive) {
scrollToLine(line);
lastActive = i;
}
});
}
// Load YouTube API
const tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
document.body.appendChild(tag);
</script>