ytdl-org / youtube-dl

Command-line program to download videos from YouTube.com and other video sites
http://ytdl-org.github.io/youtube-dl/
The Unlicense
129.82k stars 9.79k forks source link

Add now.naver.com support #29088

Open Infinitay opened 3 years ago

Infinitay commented 3 years ago

Checklist

Description

Add support for now.naver.com. Naver's NOW is a livestreaming platform with content typically being radio shows. Currently youtube-dl does not support the site.

There is an existing tool that helped download the streams along with the respect chat log, but the tool does not work anymore. I suspect this is because they changed their URL format and now encrypt it judging by the value of streamUrl when looking at a show's content.

Example

Show: https://now.naver.com/player/10238 Show ID: 10238 Content URL: https://now.naver.com/api/nnow/v1/stream/10238/content

Parsed Stream URL (videoStreamUrl): U2FsdGVkX19UaxuMl4PrWL/xRTH6zG+SiMwzjFgnr7U4HCv53otYAU0WhvKd/O/0ljegddjhvIPwrCcJcudRNv6Oh9/6MBMelGIOjtwzNjW2OkmGZiI2ISGwBGzIsS9mkILFqd7eOs4AnPOS934VgbdhRQSdIYpsM1YZviDkFMVPGgPCUCIvRAIH8ldOG/LscpEkwceW+AAZVoK7uYJfQ/scxAkVgpGlla/5S8Qvj/Gbb8gYQWSjUqrZzOzufb+a3izG/n2P/k80tGOSwRyv+uui8C1qWrQoxlZ0qDpoNUkUy/pgV9TsTZ33I9pntMKIBxTTaGAT2Q1UxGClvE9SgdhZQ+YAUHU2iB4xE+JUOSjwGQR/QIWBQxw2GLS+qgJRR9WdkbOJpSrVP162bQf8eKTuds8tQmAovjCVMSjX0BQpqbfhhLCgoVLtPfXv1r34YhlS+IyjEHBJgCUYqhUmgaPXLpKFeLYmFVTLDAKOrUOrCXbm2qpQWcw4r6WfSlh5gBo5s78myJ6+/yWQ7bKx8CgleytmPMW9XHFN48IE0AbvrIdyHc4An54rBGLACkL00YV/bfj7Vsr81+nvgOrtVOX2ypA6ycX3BNWQwEj+dX88LJdBzYizmyTej//Cz3BT6BoHnpd5ZWmBiaTKv0nnZ4oueipJh/YlSPYGwVxQ0hRwrisBGEV8Z1LtvcKFGcOPETWOCUOku83Fcdr1PWHDwJVxoIp1FrVT+kq0gA1mYDfEY+1EYZGdtjAk4nZiqjZaR/YKvUJQ7GcLpfUC4JTsbhzfrWREA9vHtS5SMyrcauw=

Response ```javascript { "contentList": [ { "cpId": "vibe", "contentId": "10238", "groupId": "707", "video": true, "streamUrl": "U2FsdGVkX19UaxuMl4PrWL/xRTH6zG+SiMwzjFgnr7U4HCv53otYAU0WhvKd/O/0ljegddjhvIPwrCcJcudRNv6Oh9/6MBMelGIOjtwzNjW2OkmGZiI2ISGwBGzIsS9mkILFqd7eOs4AnPOS934VgbdhRQSdIYpsM1YZviDkFMVPGgPCUCIvRAIH8ldOG/LscpEkwceW+AAZVoK7uYJfQ/scxAkVgpGlla/5S8Qvj/Gbb8gYQWSjUqrZzOzufb+a3izG/n2P/k80tGOSwRyv+uui8C1qWrQoxlZ0qDpoNUkUy/pgV9TsTZ33I9pntMKIBxTTaGAT2Q1UxGClvE9SgdhZQ+YAUHU2iB4xE+JUOSjwGQR/QIWBQxw2GLS+qgJRR9WdkbOJpSrVP162bQf8eKTuds8tQmAovjCVMSjX0BQpqbfhhLCgoVLtPfXv1r34YhlS+IyjEHBJgCUYqhUmgaPXLpKFeLYmFVTLDAKOrUOrCXbm2qpQWcw4r6WfSlh5gBo5s78myJ6+/yWQ7bKx8CgleytmPMW9XHFN48IE0AbvrIdyHc4An54rBGLACkL00YV/bfj7Vsr81+nvgOrtVOX2ypA6ycX3BNWQwEj+dX88LJdBzYizmyTej//Cz3BT6BoHnpd5ZWmBiaTKv0nnZ4oueipJh/YlSPYGwVxQ0hRwrisBGEV8Z1LtvcKFGcOPETWOCUOku83Fcdr1PWHDwJVxoIp1FrVT+kq0gA1mYDfEY+1EYZGdtjAk4nZiqjZaR/YKvUJQ7GcLpfUC4JTsbhzfrWREA9vHtS5SMyrcauw=", "videoStreamUrl": "U2FsdGVkX18Y4EVoeuDFi4ZfhbTeR4+znlkbU8XCT2Wn4V4OQoJRaUyTnoCI8lulNI/+CiPWzZOVS2EpREDaopg61Ij+HWCzzOZ4PalbbAaRm9PLYV0Qbqqqt/hrA5fAemQPNrN5P4C81HyuWeFXHYC3HeWh4pw8xrvL6XO2/PlAkLxKOherj5aK0OiwAGjTWmNY+ORyPDEgiBOv8tzZLhMyJ/oyJR9uMj3k0qgnPu2t29lyvGT2z20wKI4qKgXuB6ugIP9jD7uAP3C0t3sTEltdw8RsiGr0B4pyT54/44l5g8+aQhZPg6jgsmzLqAg9gWwMS3fqRBcUmWlnnTjoWIsvdLUso2HLkcZoeuEjCvkqdBxt1+tXxvsE/NAek+lTIQMDXbN6+TfLOBtUSkuIBQXxZssJi8pdWsIhj5tvCXqsFmg27GMRv+wuozurJxtP66KN4NKrjbXUbly47m8aOhkdWntZIw9kHGOB+aaduSg7aURNCkt0t0C4uM7o9Ly9DiD11gWArUgRnJDK2h5vdwHb6P1vcBt8LLlo4i5+usWha0O/7go12UD/xn+nHCW/SjM0mS2/qXeoAyMq+kHj8fKn5gn1HEaafnHsuWvtykCWw8BZqEC9PJxs07dGWK3y6L7TQZBm4j80vUuIcQtv96PeNZt5HWB2zaCwPrq/FUqNzQ6pJ96e+O++P0xnsss8d9dxMyfsp8IxzGyWmZBofA==", "landscape": false, "videoConfig": { "orientation": "portrait" }, "zeroratable": true, "start": "2021-05-20T02:59:33+09:00", "count": "", "again": false, "realtime": true, "bi": { "image": { "url": "https://nnow-phinf.pstatic.net/20210517_226/16212149733795jJRL_PNG/ef3346.png", "alt": "#OUTNOW 헤이즈 로고" }, "symbol": { "url": "https://nnow-phinf.pstatic.net/20210517_227/16212150259990xM1X_PNG/09d23f.png", "alt": "#OUTNOW 헤이즈 심볼" }, "color": "#FFFFFF", "jingle": "https://apis.naver.com/naverapps-now/nowapi/media/jingles/20210510/now_ver2_-5db_X__tnwjd_2qmIySX_xRwnwBp.mp3?msgpad=1621513894834&md=J14324GR2IsDYx4K6cbnGqH8cfM%3D" }, "title": { "text": "#OUTNOW\r\n모두가 기다린 그 컴백\r\n헤이즈의 첫 신곡 라이브!", "link": "http://" }, "description": { "text": "본: 5/20(목) 밤 8시\r\n재: 본방송 직후, 5/21(금) - 5/23(일) 밤 8시\r\n\r\n11개월을 기다린 헤이즈 컴백!\r\n\r\n7번째 EP [HAPPEN]으로 컴백하는 헤이즈(Heize)의 \r\n신곡 첫 라이브를 만날 수 있는 \r\n헤이즈 #OUTNOW!\r\n\r\n헤이즈의 감성이 담긴 곡 이야기는 물론 \r\n뮤직비디오와 신곡 무대까지 \r\n모~두 만날 수 있는 그 시간!\r\n\r\n5월 20일 저녁 8시, \r\n헤이즈 #OUTNOW 에서\r\n생방송 보쇼로 만나요✨|호스트: 헤이즈\r\n제작: 피치미디어, 네이버\r\n\r\n▶ 공식채널\r\nInstagram: https://www.instagram.com/now.kr/\r\nTwitter: https://twitter.com/kr_now\r\n네이버포스트: https://post.naver.com/now_kr", "link": "http://now.naver.com/10238", "scheme": "naversearchapp://playnow?contentId=10238&type=full&shouldPlayJingle=true&version=36", "clova": { "synonym": [ "#OUTNOW 헤이즈" ], "host": [ "헤이즈", "Heize" ], "hostid": [ "314811" ], "guest": [], "guestid": [], "mood": [], "short_term": false } }, "playlist": { "image": "https://music-phinf.pstatic.net/20210517_94/1621215537744t6tom_PNG/ce20hez_05%C7%C3%B7%B9%C0%CC%B8%AE%BD%BA%C6%AE%C4%BF%B9%F6.png", "title": "#OUTNOW 헤이즈", "subtitle": "3일 전 업데이트 · 총 5곡, 13분", "url": "https://vibe.naver.com/ios/universal/playlistEnd?playlistId=now_outnowheize_0001", "profile": "https://now-vibe.onelink.me/Hs2c/df76732d" }, "image": { "url": "https://nnow-phinf.pstatic.net/20210517_4/1621214973143f7hQY_PNG/d663e5.png", "alt": "#OUTNOW 헤이즈 포스터 배경" }, "home": { "title": { "text": "#OUTNOW 헤이즈", "subtext": "헤이즈", "link": "http://" }, "link": "http://홈링크, @wiki 1-1-영역2, @wiki 1-6-웹뷰", "image": { "url": "https://nnow-phinf.pstatic.net/20210517_282/1621214973606W0A8S_PNG/87a46f.png", "alt": "#OUTNOW 헤이즈 편성 포스터" }, "icon": { "url": "https://nnow-phinf.pstatic.net/20210517_54/1621214973734eeBgA_PNG/2ede4d.png", "alt": "#OUTNOW 헤이즈 프로필" } } } ] } ```
Infinitay commented 3 years ago

Some more details, the key is apparently an AES encryption. The code for the decoding is found within the app.js file that is loaded when you visit a show's page. The following is a snippet from the file, but I redacted the current key from the app.js file just in case.

, Lt = "REDACTED_AES_DECRYPT_KEY";
function At(t) {
    var e = It["a"].AES.decrypt(t, Lt)
      , n = e.toString(It["a"].enc.Utf8);
    return n.toString()
}

The AES decryption key appears to be static, but perhaps dynamically pulling the key would make this module more consistent and functional.