Videoglancer: 유튜브 영상을 PDF 로 바꾸기

버들비·2021년 6월 30일
0

Videoglancer?

https://videoglancer.com

Videoglancer 는 유튜브 URL을 입력하면 동영상을 1분 간격으로 캡쳐해서 pdf 로 만들어 주는 서비스입니다.

Videoglancer 를 어떻게 만들었는지 정리한 문서입니다.

도구들

다음과 같은 도구들을 사용했습니다.

  • API gateway : 웹소켓 호출을 관리.
  • AWS lambda : 유튜브 영상을 캡쳐하고 pdf 를 생성한 뒤 s3 에 업로드.
  • API s3 : lambda가 생성한 pdf 파일을 저장.
  • nodejs : 자바스크립트 서버 런타임.
  • puppeteer : chromium 기반의 브라우저 자동화 도구.
  • jsPDF : pdf 를 만들어주는 라이브러리

작동법

  1. 사용자가 유튜브 url 을 입력하면 API gateway와 웹소켓 연결
  2. API gateway 는 lambda 함수를 호출. 람다 함수는 url을 받아서 다른 람다함수에게 전해준다.
  3. url 을 전달받은 람다함수는 유튜브를 캡쳐하고 pdf 를 만든다. \rightarrow 이 작업이 1~2분 정도 걸리기에, 일반적인 http 통신을 쓰지 않고 websocket 을 사용한다.
  4. 람다함수가 작업을 끝내면 결과물을 s3에 저장하고, 다운로드 url 을 클라이언트 쪽으로 send.

1. puppeteer 로 유튜브 크롤링하기

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",
        })
      );

캡쳐 데이터를 pdf 로 만들기

// 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`);

2. lambda 함수로 배포하기

aws lambda 상에서 pptr 을 사용하려면 pptr 라이브러리를 압축해서 lambda 레이어에 업로드 해야한다.

aws lambda 용 puppeteer 레이어 zip 파일을 아래에서 다운받아 사용했다.

https://github.com/alixaxel/chrome-aws-lambda

API gateway 로 웹소켓 api 를 만들어 lambda 와 연동하는 것은 다음을 참고

https://velog.io/@budlebee/AWS-api-gateway-lambda-로-websocket-api-만들기

3. 클라이언트에서 웹소켓 호출

프론트는 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]);

0개의 댓글