YouTube downloader - 실전편(1)

trevor.ph·2021년 7월 17일
4

의식의흐름대로

목록 보기
3/4

[0] TL;TR

YouTube와 Naver TV의 동영상을 간단한 script를 통해 다운로드 받아본다.

[1] 계기

youtube-dl 은 python기반의 CLI프로그램으로 다음과 같이 원하는 Youtube 영상의 링크를 입력하면 바로 영상을 다운로드 받을수 있다.
youtube-dl "https://www.youtube.com/watch?v=asdbasdf"
YouTube 광고를 시청할 필요도 없이 .mp4 파일로 금방 다운로드가 된다. YouTube 뿐만 아니라 Naver TV 아프리카TV 등 여러 사이트를 지원한다. 그냥 갖다 써도 행복하겠지만, "이거 어떻게 한거지?" 라는 궁금증이 든다. 이번 포스팅은 Youtube 영상과 더불어 Naver TV 영상을 DIY로 다운로드 해보고자 했던 삽질과, 그 결과물에 대한 것이다.

[2] Youtube

Debug Elements


YouTube의 video 영역을 debugger로 찍어봤을 때의 모습인데, src 가 좀 이상하게 생겼다. src attribute가 이를테면 https://youtube.com/movie.mp4 이런 식으로 영상의 소스 url을 바로 담고 있을거라 생각헀는데 blob으로 시작하는 이상한 url 이다. 브라우저의 새탭을 열어 blob경로를 입력한 후 접속을 했더니,

Not allowed to load local resource:blob:https://www.youtube.com/18eddd0a-abca-4ae4-be79-a340a6bb24e2

라고 으르렁대며 오류가 발생한다. blob이 뭔가 수상한데 이에 대해서는 다음 포스팅에서 다루기로 하고 일단 다른 방법을 모색해보도록 한다.

Debug Network

debugger의 Network탭을 켜보면 다음과 같다.

위의 캡처에서 videoplayback?로 시작하는 request가 반복적으로 호출되고, response의 용량이 상당한 것을 보아 왠지 이게 영상 소스가 아닐까 하는 생각이 든다. response header를 까보면 content-type: video/mp4인 것으로 보아 영상 소스가 맞다. request를 추적해보기 위해 소스코드를 따라가본다. base.js의 4199번째 줄을 먼저 살펴보자.


xhr이 있는 것을 보아 여기서 video에 대한 request를 하고 있고, 42227줄의 onDone이 콜백함수로 보인다. 따라가보자


찾았다!! onDone콜백함수에서 response를 처리하는데, 여기다 breakpoint를 걸고 this.xhr.response를 콘솔에 쳐보자. ArrayBuffer의 길이가 엄청난 것을 보아 동영상에 대한 binary data인 것으로 보인다. 또한 url의 index가 바뀌어가며 반복적으로 호출되고 있는 것으로 보아 동영상을 fragment로 나누어 다운로드 받고 있는 듯하다. request의 url을 복사하여 새 탭에 붙여넣기하면 videoplayback.webm라는 이름의 파일로 다운로드가 되는데, 이를 열어보면 영상이 몇 초 나오다 끝나버리고 음성은 나오지도 않는다. 즉 이건 영상만 있는 파일로 YouTube에서는 영상과 음성을 따로 serving하기 때문이다(간혹 영상에 따라 mp4로 주는 경우도 있음). 따라서 처음부터 끝까지 모든 조각을 다운받아 이걸 하나로 합친 다음, ffmpeg로 영상과 음성을 합쳐 인코딩 하는 과정이 필요하다. 하지만 절차가 복잡하므로 일단 만만한 Naver TV를 먼저 시도해본다.

[3] Naver TV

Youtube와 같은 방법으로 debugger의 network탭에서 request/response 중심으로 디버깅을 해보자
Naver TV는 video 플에이어로 prismplayer라는 것을 이용하고 있다.

content_512000_{index}_ts로 시작하는 request가 용량을 보아하니 video인것 같고 아까 Youtube와는 다르게 .ts라는 확장자를 가지고 있다. 위와 같은 방법으로 다운로드시 재생이 가능하다(.ts는 MPEG stream으로 음성과 영상이 하나로 합쳐져 온전히 재생가능한 파일이다). ts파일이 1번부터 i번까지 있는데, 모든 ts파일을 다운받아 순서대로 한 파일로 합치면 전체 video가 된다. 여기서 팁이 있는데, 우클릭하면 다음과 같이 fetch, cURL등 여러가지 포맷으로 request를 copy 할수 있다.

우리는 terminal에서 다운로드 받을 것이므로 cURL을 선택하고, 0번부터 9번까지 한번에 다운로드 받기 위해 0.ts 대신 [0-9].ts로 수정하여 shell script를 실행하면 동영상 파일을 다운로드 할 수 있다. 아래 스크립트 실행시 output.ts라는 통 파일이 완성된다.

# SAMPLE CODE(NOT WORK)
curl 'https://naver-mbc-h.smartmediarep.com/{길어서 생략}/content_5120000_[0-9].ts?{이후 생략}' \
  -H 'authority: naver-mbc-h.smartmediarep.com' \
  -H {헤더 길어서 생략} \
  -H 'accept-language: en-US,en;q=0.9,ko;q=0.8' \
  --compressed \
  >> output.ts

[4] YouTube download

video source

Naver TV에서 했던 방법으로 network탭에서 동영상 소스에 대한 request를 따보면 다음과 같다.

아까와는 다르게 매우 복잡하다. video 통파일이 아닌 조각이므로 여기 어딘가에 index에 대한 정보가 있을것 같은데, 표시된 부분을 보면 range=0-1640846 이라는 query string parameter가 있다. 1640846 숫자를 video 마지막에 해당하는 index로 수정해주면 전체 video를 다운로드 할수 있을 것이다. 마지막 index는 어떻게 따냐고? 일단은 동영상 끝부분으로 이동했을 때 나오는 request를 컨닝해본다. 이 video는 약 9분짜리 영상으로 컨닝한 결과 마지막 index가 66921103 이다. 위의 range를 range=0-66921103로 대체하여 cURL 스크립트를 작성하면 다음곽 같다.

# NOT WORK
# video only
curl 'https://r3---sn-3u-bh2ey.googlevideo.com/videoplayback?expire=1626545945&ei=range=0-164084{.. 이후 중략}' \
  -H 'authority: r3---sn-3u-bh2ey.googlevideo.com' \
   {... header 중략 }
  -H 'accept-language: en-US,en;q=0.9,ko-KR;q=0.8,ko;q=0.7' \
  --compressed \
>> movie.webm 

다운로드한 파일의 확장자는 webm으로 하는데 이는 response header를 까봤을 때 content-typevideo/webm이기 때문이다. 이 파일을 Chrome에서 오픈해보면 영상은 처음부터 끝까지 통으로 잘 나오고 당연히 소리는 안나온다. 이제 소리를 구하러 가보자.

audio source

다시 debugger의 network탭을 열어 음성에 해당하는 request를 따서 마저 다운로드 받는다. 잘 보면 영상 request와의 차이점이 있는데, Response Header를 보면 content-type: audio/webm라고 적혀있다. cURL 코드를 딴 후, 다음과 같이 range를 잘 수정해서 다운로드 해보자. 이번에도 파일의 확장자는 webm이다.

curl 'https://r3---sn-3u-bh2ey.googlevideo.com/videoplayback?expire=1626545945&range=0-8544830&rn=2{... url 중략 }&rbuf=0' \
  -H 'authority: r3---sn-3u-bh2ey.googlevideo.com' \
  { ... header 중략}
  -H 'accept-language: en-US,en;q=0.9,ko-KR;q=0.8,ko;q=0.7' \
  --compressed \
>> audio.webm

이 파일을 크롬에서 재생해보면 음성만 처음부터 끝까지 잘 나온다

ffmpeg

자 이제 영상파일과 음성파일을 확보했으니 이 두 파일을 하나로 합쳐주면 된다. ffmpeg를 설치하고 다음과 같이 터미널에 쳐준다.
./ffmpeg -i video.webm -i audio.webm -c:v copy -c:a copy output.mp4
output.mp4를 재생하면 음성과 영상이 정상적으로 나온다.

[5] 생각할 거리

위의 케이스를 토대로 Youtube와 Naver TV에서 video를 보여주는 방식을 추정하면 다음과 같다.

  • 서버에서는 test.mp4 와 같이 동영상 파일을 통째로 제공해 주지 않는다.
  • 브라우저에서 start~end에 해당하는 fragment를 서버에 요청하고 ArrayBuffer의 형태로 response를 받아온다.
  • 이후 브라우저에서는 ArrayBuffer를 재생가능한 형태로 가공을 하고, 이를 <video> Element에서 재생시킨다

이번 포스팅에서는 영상 다운로드에 대한 실전으로 디버깅 과정과 간단한 terminal 코드를 알아보았고, 다음 포스팅에서는 이러한 꼼수를 부리며 알게된 키워드인 BLOB과 HLS에 대해서 알아본다.

[6] 참고

https://kibua20.tistory.com/130
https://kibua20.tistory.com/79

profile
It's just a shot away

0개의 댓글