Skip to content

Instantly share code, notes, and snippets.

@sounisi5011
Last active January 13, 2021 15:06
Show Gist options
  • Save sounisi5011/0cdf426d24128176d0acbbeda1b77a2e to your computer and use it in GitHub Desktop.
Save sounisi5011/0cdf426d24128176d0acbbeda1b77a2e to your computer and use it in GitHub Desktop.
YouTubeのチャプターの簡易デバッグツール

YouTubeのチャプターの簡易デバッグツール

YouTubeのチャプター機能の結果をシミュレートし、わかりやすく表示するツール。

動作ページ

機能

  • 各チャプターの時間を表示
  • 10秒未満のチャプターはエラー
  • 100以上のチャプターはエラー(2021年1月5日 3時47分の時点で確認した仕様)
#!/bin/bash
echo '$' npx standard --fix
npx standard --fix
echo '$' npx prettier --write '*.css'
npx prettier --write '*.css'
<!DOCTYPE html>
<html lang=ja>
<meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1">
<meta name=format-detection content="telephone=no,email=no,address=no">
<title>YouTubeのチャプターの簡易デバッグツール</title>
<link rel=stylesheet href="./main.css">
<h1>YouTubeのチャプターの簡易デバッグツール</h1>
<main>
<p><textarea id=input></textarea></p>
<ol id=output></ol>
</main>
<footer>
<h2>Gist</h2>
<p><a href="https://gist.github.com/sounisi5011/0cdf426d24128176d0acbbeda1b77a2e">gist.github.com<wbr>/sounisi5011<wbr>/0cdf426d24128176d0acbbeda1b77a2e</a>
</footer>
<script src="https://unpkg.com/html-escaper@3.0.0/min.js" integrity="sha384-5//czxK1xsldAV41C5VrZbhEJLPVlOxJg4vPh9flJbmIkiB6BL89PhjudfyNQw1Z" crossorigin=anonymous defer></script>
<script src="./main.js" defer></script>
#input {
box-sizing: border-box;
width: 100%;
height: 10em;
font-family: Roboto, Noto, sans-serif;
}
#output li {
padding: 0.15em 0;
}
#output li:nth-child(2n) {
background-color: lightgray;
}
#output .time,
#output .diff {
margin-right: 0.25em;
border: solid 1px black;
border-radius: 0.3em;
padding: 0.1em 0.25em;
font-family: monospace;
vertical-align: text-bottom;
}
#output .time {
color: cyan;
background-color: gray;
}
#output .diff {
color: lime;
background-color: gray;
}
#output .warn {
background-color: orangered;
}
const SESSION_STORAGE_KEY = 'gist.github.com/sounisi5011/0cdf426d24128176d0acbbeda1b77a2e'
const inputElem = document.getElementById('input')
const outputElem = document.getElementById('output')
inputElem.addEventListener('input', inputListener)
init()
// ----- ----- ----- ----- ----- //
function last (array) {
return array[array.length - 1]
}
function sec2time (sec) {
if (!Number.isFinite(sec)) return String(sec)
const sign = sec < 0 ? '-' : ''
const absSec = Math.abs(sec)
const s = absSec % 60
const m = Math.trunc(absSec / 60) % 60
const h = Math.trunc(absSec / 60 ** 2)
return `${sign}${h}:${String(m).padStart(2, 0)}:${String(s).padStart(2, 0)}`
}
function init () {
if (inputElem.value === '') {
const savedData = sessionStorage.getItem(SESSION_STORAGE_KEY)
if (typeof savedData === 'string') {
inputElem.value = savedData
}
}
inputListener()
}
function inputListener () {
const inputText = inputElem.value
const chapterList = inputText
.split(/(?:\r\n?|\n)+/)
.map(line => {
const match = /^\s*(?:(\d+):)?(\d+):(\d+)\s+(.+)$/s.exec(line)
if (!match) return null
const [, hour = '0', min, sec, title] = match
const time = (Number(hour) * 60 + Number(min)) * 60 + Number(sec)
return { time, title }
})
.filter(Boolean)
.reduce(
(chapterList, chapterItem) =>
(chapterList.length < 1 && chapterItem.time === 0) ||
(chapterList.length >= 1 && last(chapterList).time < chapterItem.time)
? [...chapterList, chapterItem]
: chapterList,
[]
)
const outputHTML = chapterList
.map(({ time, title }, index) => {
const nextTime = chapterList[index + 1]?.time ?? Infinity
const diffTime = nextTime - time
return [
`<li class="${index >= 100 ? 'warn' : ''}">`,
`<span class=time>${sec2time(time)}</span>`,
`<span class=${diffTime < 10 ? '"diff warn"' : 'diff'}>${sec2time(diffTime)}</span>`,
`<span class=title>${html.escape(title)}</span>`,
'</li>'
].join('')
})
.join('')
outputElem.innerHTML = outputHTML
try {
sessionStorage.setItem(SESSION_STORAGE_KEY, inputText)
} finally {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment