Videoglancer 는 유튜브 URL을 입력하면 동영상을 1분 간격으로 캡쳐해서 pdf 로 만들어 주는 서비스입니다.
Videoglancer 를 어떻게 만들었는지 정리한 문서입니다.
다음과 같은 도구들을 사용했습니다.
npm install puppeteer
const browser = await puppeteer.launch({ headless: true }); // 브라우저를 실행
const page = await browser.newPage(); // 새로운 페이지를 연다
await page.setViewport({ width: 1920, height: 1080 }); // 화면의 크기를 설정한다.
await page.goto(`${youtubeURL}&t=5`, {
waitUntil: "networkidle2",
}); // 입력받은 youtube url 로 이동한다. 이동한 다음 컨텐츠가 충분히 로딩되도록, 네트워크 통신이 2개 아래로 줄어들때까지 대기.
// get video component
await page.waitForSelector(".html5-main-video"); // .html5-main-video 라는 클래스 명을 가진 DOM 이 나타날때까지 대기.
const videoEle = await page.$(".html5-main-video"); // .$ 메소드를 이용해 .html5-main-video 라는 DOM 을 선택.
// get video size
figWidth = await page.evaluate((video) => {
// evaluate 내부에서 실행되는건 브라우저상에서 실행되는거라, node 상으로 반영 안된다. 브라우저상에서 실행한 코드를 값으로 얻고 싶다면 return 을 이용해 값을 반환할 것.
return video.videoWidth;
}, videoEle);
figHeight = await page.evaluate((video) => {
return video.videoHeight;
}, videoEle);
const captureData = [];
captureData.push(
await videoEle.screenshot({
type: "jpeg",
encoding: "base64",
})
);
// make pdf file
const doc = new jsPDF("l", "pt", [figWidth, figHeight]); // "l"은 landscape, 좌우로 긴 레이아웃을 지정, pt는 pdf 크기의 단위를 point 로 지정, [width, height] 배열은 pdf의 크기를 지정.
for (let i = 0; i < captureData.length; i++) {
doc.addImage(captureData[i], "JPEG", 0, 0, figWidth, figHeight); //이미지 그리기
if (i == captureData.length - 1) {
} else {
doc.addPage();
}
}
// save it locally
doc.save(`glancer-${Date.now()}.pdf`);
aws lambda 상에서 pptr 을 사용하려면 pptr 라이브러리를 압축해서 lambda 레이어에 업로드 해야한다.
aws lambda 용 puppeteer 레이어 zip 파일을 아래에서 다운받아 사용했다.
API gateway 로 웹소켓 api 를 만들어 lambda 와 연동하는 것은 다음을 참고
https://velog.io/@budlebee/AWS-api-gateway-lambda-로-websocket-api-만들기
프론트는 React 를 사용했다.
const doSocket = useCallback(() => {
const socket = new WebSocket(wss://웹소캣 API 주소);
socket.onopen = (e) => {
socket.send(url);
};
socket.onclose = (event) => {
if (event.wasClean) {
console.log(`[close](code=${event.code} reason=${event.reason})`);
} else {
// 예시: 프로세스가 죽거나 네트워크에 장애가 있는 경우
// event.code가 1006이 됩니다.
console.log("[close] connection dead.");
}
};
socket.onmessage = (event) => {
const res = JSON.parse(event.data);
if (res.message.message) {
alert("Error. Check url. (we cannot support video under 1 minute)");
window.location.reload();
return;
}
if (onWait) {
setTimeout(() => {
if (onWait) {
socket.send(url);
} else {
return;
}
}, 3000);
}
if (res.message == "task on") {
onWait = false;
}
if (res.message && !res.message.message) {
setRes(res.message);
}
if (res.url) {
setDownloadURL(res.url);
setLoading(false);
socket.close();
}
};
}, [loading, url]);