유튜브 음악 영상에서 고음질 ogg 파일 추출하기

지금 사용 중인 유튜브 요금제가 유튜브 프리미엄도 아니고 휴대전화 요금제도 데이터 무제한이 아니라서 외출 시 감상할 목적으로 유튜브 음악 영상에서 ogg 파일을 추출해서 휴대전화에 보관해 두고 듣는 방법을 찾아봤다.

유튜브 영상 다운로드는 yt-dlp 프로그램을 이용하면 된다. https://github.com/yt-dlp/yt-dlp에서 프로그램을 내려받고 다음과 같이 실행하면

yt-dlp.exe -F <유튜브 영상 URL>

다음과 같이 대상 유튜브 영상의 각종 영상/음성 관련 정보가 나온다.

$ yt-dlp.exe -F https://www.youtube.com/watch?v=<video ID>
[youtube] Extracting URL: https://www.youtube.com/watch?v=<video ID>
[youtube] <video ID>: Downloading webpage
[youtube] <video ID>: Downloading ios player API JSON
[youtube] <video ID>: Downloading android player API JSON
WARNING: [youtube] Skipping player responses from android clients (got player responses for video "aQvGIIdgFDM" instead of "<video ID>")
[youtube] <video ID>: Downloading player edea0cc6
[youtube] <video ID>: Downloading m3u8 information
[info] Available formats for <video ID>:
ID  EXT   RESOLUTION FPS CH │   FILESIZE  TBR PROTO │ VCODEC         VBR ACODEC      ABR ASR MORE INFO
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
sb3 mhtml 48x27        0    │                 mhtml │ images                                 storyboard
sb2 mhtml 80x45        0    │                 mhtml │ images                                 storyboard
sb1 mhtml 160x90       0    │                 mhtml │ images                                 storyboard
sb0 mhtml 320x180      0    │                 mhtml │ images                                 storyboard
233 mp4   audio only        │                 m3u8  │ audio only         unknown             [ko] Default
234 mp4   audio only        │                 m3u8  │ audio only         unknown             [ko] Default
139 m4a   audio only      2 │   22.15MiB  49k https │ audio only         mp4a.40.5   49k 22k [ko] low, m4a_dash
249 webm  audio only      2 │   24.01MiB  53k https │ audio only         opus        53k 48k [ko] low, webm_dash
250 webm  audio only      2 │   31.75MiB  70k https │ audio only         opus        70k 48k [ko] low, webm_dash
140 m4a   audio only      2 │   58.79MiB 129k https │ audio only         mp4a.40.2  129k 44k [ko] medium, m4a_dash
251 webm  audio only      2 │   61.90MiB 136k https │ audio only         opus       136k 48k [ko] medium, webm_dash
...

여기서 ACODEC이 ‘opus’로 표시된 데이터가 ogg 데이터일 텐데, 이 데이터를 내려받아 ogg 파일로 저장하면 된다. 특정 데이터를 받으려면 첫 번째 칼럼인 ID를 이용하면 되고, 여기서는 ID가 251인 파일을 다음 명령어로 내려받는다.

yt-dlp.exe -f 251 <유튜브 영상 URL>

그런데 위와 같은 경우 확장자(EXT)가 webm이니 webm 파일로 내려받게 되므로 이 파일에서 오디오 데이터만 추출하려면 ffmpeg를 이용하면 된다. ffmpeg가 설치돼 있다는 가정하에 아래 명령어를 실행하면 ogg 파일이 추출된다.

ffmpeg -i "input.webm" -vn -acodec copy "output.ogg"

그럼 변환된 ogg 파일을 플레이어에 넣고 재생하면 된다.

추가로 유튜브 영상이 여러 곡으로 구성된 경우라면 변환된 ogg 파일을 각 곡별 재생 구간으로 잘라서 추출하면 된다.

ffmpeg -i output.ogg -acodec copy -ss "시작구간<00:00:00>" -to "종료구간<00:99:99>" "<파일명>.ogg"

그럼 각 파일별로 노래를 쪼개서 저장할 수 있으니 재생할 때 편하다.

현재 디렉터리에 있는 PDF에서 첫 페이지만 고화질 이미지로 저장하기

PDF 파일에서 첫 페이지는 보통 표지인데, 표지만 고화질로 저장하고 싶을 때가 있다. PDF 리더의 이미지 내보내기 기능을 이용해 봤는데 화질이 그리 좋지 않기도 하고 파일이 많을 때는 번거로운 작업이기도 하다.

먼저 작업에 PyMuPDF 라이브러리가 필요하므로 아래와 같이 설치한다.

python -m pip install --upgrade pip
python -m pip install --upgrade pymupdf

그러고 나서 아래와 같은 스크립트를 이용하면 glob으로 현재 디렉터리 내 PDF 파일을 모두 나열한 후 파일을 하나씩 열어서 문서의 첫 페지이만 이미지 파일로 저장하는 작업을 수행한다.

import glob, sys, fitz, os

zoom_x = 3.0
zoom_y = 3.0

mat = fitz.Matrix(zoom_x, zoom_y)

path = './'
all_files = glob.glob(path + "*.pdf")

for filename in all_files:
    doc = fitz.open(filename)
    page = doc[0]
    pix = page.get_pixmap(matrix=mat)

    filename_only = os.path.splitext(filename)[0]
    print('Saving cover for {}'.format(filename_only))

    pix.save(filename=f'{filename_only}_cover.png')

참고로 zoom_x, zoom_y의 값을 올려서 확대 배율을 높이면 그만큼 고화질 이미지로 저장된다.

requests에서 GET 요청 시 동일한 이름의 파라미터 전달하기

파이썬 requests 라이브러리를 사용할 때 간혹 요청 대상 사이트의 URL 설계에 따라 같은 이름의 URL 파라미터명을 사용하는 경우가 있다. 즉, 아래와 같은 URL인데, item이라는 이름으로 여러 값을 전달하도록 돼 있는 경우다.

https://example.com?action=buy&item=사과&item=바나나&item=딸기

그런데 무심코 해당 부분을 다음과 같이 뽑아서 요청을 보냈더니 이름이 같아서 값 하나만 전송되는 결과가 발생했다.

data = {
    'action': 'buy',
    'item': '사과',
    'item': '바나나',
    'item': '딸기',
}

resp = session.get(url, params=data)

그래서 구글링해보니 위와 같은 경우에는 다음과 같이 리스트 타입으로 지정하면 처음 의도한 바대로 전송된다.

data = {
    'action': 'buy',
    'item': ['사과', '바나나', '딸기'],
}

resp = session.get(url, params=data)