async function run() { const urlInput = document.getElementById('url').value; const log = document.getElementById('log'); const btn = document.getElementById('btn'); const videoId = urlInput.split('v=')[1]?.split('&')[0] || urlInput.split('/').pop(); if (!videoId || videoId.length !== 11) return alert('주소 확인!'); btn.disabled = true; log.innerText = "데이터 정밀 분석 중..."; try { const pageRes = await fetch(`/functions/proxy?url=${encodeURIComponent('https://www.youtube.com/watch?v=' + videoId)}`); const html = await pageRes.text(); // 방법 1: playerResponse에서 찾기 let captionData = null; const playerResponseMatch = html.match(/var ytInitialPlayerResponse\s*=\s*({.+?});/); if (playerResponseMatch) { const playerResponse = JSON.parse(playerResponseMatch[1]); captionData = playerResponse.captions?.playerCaptionsTracklistRenderer?.captionTracks; } // 방법 2: 만약 방법 1이 실패하면 전체 텍스트에서 긁어오기 if (!captionData) { const regex = /"captionTracks":\s*(\[.+?\])/; const match = html.match(regex); if (match) captionData = JSON.parse(match[1]); } if (!captionData || captionData.length === 0) { throw new Error("유튜브가 자막 데이터를 숨겼거나 봇으로 감지했습니다."); } // 한국어(ko) -> 영어(en) -> 첫 번째 자막 순으로 선택 const track = captionData.find(t => t.languageCode === 'ko') || captionData.find(t => t.languageCode === 'en') || captionData[0]; log.innerText = `${track.name.simpleText} 자막 가져오는 중...`; const subRes = await fetch(`/functions/proxy?url=${encodeURIComponent(track.baseUrl + '&fmt=srt')}`); const srtContent = await subRes.text(); const blob = new Blob([srtContent], { type: 'text/plain' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = `subtitle_${videoId}.srt`; a.click(); log.innerText = "성공!"; } catch (e) { log.innerText = "에러: " + e.message; console.error("상세 에러:", e); } finally { btn.disabled = false; } }