From cc8cd781037917d53ebda642967daea65de55516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20F=C3=A4hrmann?= Date: Tue, 23 Dec 2025 19:08:30 +0100 Subject: [PATCH] [webtoons] download episode background music (#8733) add 'bgm' option --- docs/configuration.rst | 13 ++++++++- docs/gallery-dl.conf | 1 + gallery_dl/extractor/webtoons.py | 45 +++++++++++++++++++++++++++++++- test/results/webtoons.py | 45 ++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 2 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index afd7a0e1..d6d18edb 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -6745,13 +6745,24 @@ Note This requires 1 additional HTTP request per submission. +extractor.webtoons.bgm +---------------------- +Type + ``bool`` +Default + ``true`` +Description + Download an episode's `background music` if available. +Note + Requires |ytdl| + + extractor.webtoons.quality -------------------------- Type * ``integer`` * ``string`` * ``object`` (`ext` → `type`) - Default ``"original"`` Example diff --git a/docs/gallery-dl.conf b/docs/gallery-dl.conf index 93ce1aa8..ccbcbaff 100644 --- a/docs/gallery-dl.conf +++ b/docs/gallery-dl.conf @@ -943,6 +943,7 @@ { "sleep-request": "0.5-1.5", + "bgm" : true, "quality" : "original", "banners" : false, "thumbnails": false diff --git a/gallery_dl/extractor/webtoons.py b/gallery_dl/extractor/webtoons.py index a663319f..6bf4c5d7 100644 --- a/gallery_dl/extractor/webtoons.py +++ b/gallery_dl/extractor/webtoons.py @@ -130,10 +130,53 @@ class WebtoonsEpisodeExtractor(WebtoonsBase, GalleryExtractor): return results def assets(self, page): + assets = [] + if self.config("thumbnails", False): active = text.extr(page, 'class="on', '') url = _url(text.extr(active, 'data-url="', '"')) - return ({"url": url, "type": "thumbnail"},) + assets.append({"url": url, "type": "thumbnail"}) + + if self.config("bgm", True): + if bgm := text.extr(page, "episodeBgmList:", ",\n"): + self._asset_bgm(assets, util.json_loads(bgm)) + + return assets + + def _asset_bgm(self, assets, bgm_list): + import binascii + params = { + # "quality" : "MIDDLE", + "quality" : "HIGH", # no difference to 'MIDDLE' + "acceptCodecs": "AAC,MP3", + } + headers = { + "Accept" : "application/json", + "Content-Type" : "application/json", + "Origin" : self.root, + "Referer" : self.root + "/", + "Sec-Fetch-Dest": "empty", + "Sec-Fetch-Mode": "cors", + "Sec-Fetch-Site": "cross-site", + } + + for bgm in bgm_list: + url = (f"https://apis.naver.com/audiocweb/audiocplayogwweb/play" + f"/audio/{bgm['audioId']}/hls/token") + data = self.request_json( + url, params=params, headers=headers, interval=False) + token = data["result"]["playToken"] + data = util.json_loads(binascii.a2b_base64(token).decode()) + audio = data["audioInfo"] + + assets.append({ + **bgm, + **audio, + "type": "bgm", + "url" : "ytdl:" + audio["url"], + "_ytdl_manifest": audio["type"].lower(), + "extension": "mp3", + }) class WebtoonsComicExtractor(WebtoonsBase, Extractor): diff --git a/test/results/webtoons.py b/test/results/webtoons.py index ebd1a539..17a206f0 100644 --- a/test/results/webtoons.py +++ b/test/results/webtoons.py @@ -129,6 +129,51 @@ __tests__ = ( "author_name" : "spin.ani", }, +{ + "#url" : "https://www.webtoons.com/en/super-hero/unordinary/episode-20/viewer?title_no=679&episode_no=21", + "#comment" : "background music (#8733)", + "#class" : webtoons.WebtoonsEpisodeExtractor, + "#options" : {"bgm": True}, + "#range" : "1", + "#pattern" : r"ytdl:https://apis.naver.com/audioc/audiocplay/play/audio/4A10DDE1B92388DA164C48B0356AA442/hls/manifest\.m3u8\?apigw-routing-key=KR&codec=AAC&kbps=64&tt=\d+&tv=.+", + + "audioId" : "4A10DDE1B92388DA164C48B0356AA442", + "author_name" : "uru-chan", + "codec" : "AAC", + "comic" : "unordinary", + "comic_name" : "unOrdinary", + "count" : 63, + "cpContentId" : None, + "cpNo" : 5, + "description" : "Nobody paid much attention to John – just a normal teenager at a high school where the social elite happen to possess unthinkable powers and abilities. But John’s got a secret past that threatens to bring down the school’s whole social order – and much more. Fulfilling his destiny won’t be easy though, because there are battles, frenemies and deadly conspiracies around every corner.", + "duration" : 90.096692, + "encodingTargetYn": False, + "episode" : "21", + "episodeNo" : 21, + "episode_name" : "Episode 20", + "episode_no" : "21", + "expireTime" : int, + "extension" : "mp3", + "filePath" : "679_21/1475723621351drama7.mp3", + "genre" : "super-hero", + "kbps" : 64, + "lang" : "en", + "language" : "English", + "num" : 0, + "objectType" : "mp4a.40.2", + "originalFileSize": 0, + "playImageUrl" : "/20161006_271/1475724249957QlGUF_JPEG/1475724249934679214.jpg", + "region" : "KR", + "registerYmdt" : "2016-10-06 12:25:09", + "sortOrder" : 1, + "stopImageUrl" : "", + "title" : "unOrdinary - Episode 20", + "titleNo" : 679, + "title_no" : "679", + "type" : "bgm", + "username" : "62610", +}, + { "#url" : "https://www.webtoons.com/en/comedy/live-with-yourself/list?title_no=919", "#comment" : "english",