Skip to content

Instantly share code, notes, and snippets.

@kyle-seongwoo-jun
Created November 15, 2020 13:28
Show Gist options
  • Save kyle-seongwoo-jun/38d703355bb0db53c0a86ff860c6a8a2 to your computer and use it in GitHub Desktop.
Save kyle-seongwoo-jun/38d703355bb0db53c0a86ff860c6a8a2 to your computer and use it in GitHub Desktop.
async function initYoutubeCaptions() {
const video = document.querySelector('video')
const captions = await getYoutubeCaptions()
addCaptions(video, captions)
}
function selectYoutubeCaption(language) {
const video = document.querySelector('video')
Array.prototype.forEach.call(video.textTracks, track => {
if (track.mode == "disabled") return
track.mode = track.language == language ? "showing" : "hidden"
})
video.webkitClosedCaptionsVisible = true
}
function availableLanguages() {
const video = document.querySelector('video')
const languages = Array.prototype.filter.call(video.textTracks, track => track.mode != "disabled")
.map(track => track.language)
return languages
}
async function getYoutubeCaptions() {
const youtubeArgs = ytplayer.config.args
if (youtubeArgs === undefined) {
console.warn('No Youtube player configuarion found.')
return []
}
const videoId = new URLSearchParams(window.location.search).get('v')
if (videoId !== youtubeArgs.video_id) {
console.warn('video id is not matched. Please refresh and try again.')
console.debug('v:', videoId, 'video_id:', youtubeArgs.video_id)
return []
}
const { captionTracks } = JSON.parse(youtubeArgs.player_response).captions.playerCaptionsTracklistRenderer
if (captionTracks.length === 0) {
console.warn('No captions found.')
return []
}
const captions = await Promise.all(
captionTracks.map(async caption => {
// request caption as xml
const fetchUrl = caption.baseUrl
const xmlString = await fetch(fetchUrl).then(res => res.text())
const cues = parseXml(xmlString)
return {
label: caption.name.simpleText,
language: caption.languageCode,
cues,
}
})
)
return captions
}
let AVOID_CONCURRENT_CAPTIONS = true
function parseXml(xmlString) {
// parse xml
const xmlParsed = new window.DOMParser().parseFromString(xmlString, 'text/xml')
const textElements = xmlParsed.querySelector('transcript').childNodes
var tempNode = document.createElement('div')
const cues = Array.prototype.map.call(textElements, (text, index) => {
const start = Number(text.getAttribute('start'))
let end = start + Number(text.getAttribute('dur'))
if (AVOID_CONCURRENT_CAPTIONS && index + 1 < textElements.length) {
// youtube captions overlap so this gets rid of that
const nextStart = Number(textElements[index + 1].getAttribute('start'))
end = Math.min(end, nextStart)
}
// remove font tags from caption content
const content = text.textContent.replace(/<[^>]+>/g, '')
tempNode.innerHTML = content
// create cue
return new VTTCue(start, end, tempNode.textContent)
})
return cues
}
function addCaptions(video, captions) {
// disable all of tracks
Array.prototype.forEach.call(video.textTracks, track => track.mode = "disabled")
// add tracks
captions.forEach(caption => {
const track = video.addTextTrack('subtitles', caption.label, caption.language)
caption.cues.forEach(cue => track.addCue(cue))
})
}
// await initYoutubeCaptions()
// availableLanguages()
// selectYoutubeCaption('ko')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment