Repository: yt-dlp/yt-dlp

Issue:
idolplus.com Support
### DO NOT REMOVE OR SKIP THE ISSUE TEMPLATE

- [X] I understand that I will be **blocked** if I remove or skip any mandatory\* field

### Checklist

- [X] I'm reporting a new site support request
- [X] I've verified that I'm running yt-dlp version **2023.01.06** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
- [X] I've checked that all provided URLs are playable in a browser with the same IP and same login details
- [X] I've checked that none of provided URLs [violate any copyrights](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-website-primarily-used-for-piracy) or contain any [DRM](https://en.wikipedia.org/wiki/Digital_rights_management) to the best of my knowledge
- [X] I've searched the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues **including closed ones**. DO NOT post duplicates
- [X] I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
- [X] I've read about [sharing account credentials](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#are-you-willing-to-share-account-details-if-needed) and am willing to share it if required

### Region

Any country, it can restrict some countries depending on the content on it

### Example URLs

- Single video: https://idolplus.com/zs/contents&albumId=M012326213PPV00
- Single video: https://idolplus.page.link/4gaw

### Provide a description that is worded well enough to be understood

This website is from Korea, it is a website that publishes videos about entertainment programs, music festivals and concerts.

In addition, it not only uploads video (VOD) but it also Live stream. However, it does not broadcast live 24/7, but it only broadcasts when there is an event

There will be a live stream in 2 days, I think it will come in handy as you work on it:
- https://idolplus.com/zs/concert/M012323014PPV00
- https://idolplus.com/zs/concert/M012323039PPV00

### Provide verbose output that clearly demonstrates the problem

- [X] Run **your** yt-dlp command with **-vU** flag added (`yt-dlp -vU <your command line>`)
- [X] Copy the WHOLE output (starting with `[debug] Command-line config`) and insert it below

### Complete Verbose Output

```shell
[debug] Command-line config: ['-vU', 'https://idolplus.page.link/4gaw']
[debug] Encodings: locale cp1252, fs utf-8, pref cp1252, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version 2023.01.06 [6becd25] (win_exe)
[debug] Python 3.8.10 (CPython AMD64 64bit) - Windows-10-10.0.19044-SP0 (OpenSSL 1.1.1k  25 Mar 2021)
[debug] exe versions: ffmpeg 4.2.1, ffprobe 4.2.1
[debug] Optional libraries: Cryptodome-3.16.0, brotli-1.0.9, certifi-2022.12.07, mutagen-1.46.0, sqlite3-2.6.0, websockets-10.4
[debug] Proxy map: {}
[debug] Loaded 1760 extractors
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Latest version: 2023.01.06, Current version: 2023.01.06
yt-dlp is up to date (2023.01.06)
[generic] Extracting URL: https://idolplus.page.link/4gaw
[generic] 4gaw: Downloading webpage
[redirect] Following redirect to https://idolplus.com/web/vod/share/content?params=b7025f006b7521d0374b5be3b4b18756b557c154ae3231c4bc8d46eea11be3c0f806a6d8ef8e4147c81618b1db5be762b45515e07b2216217036a853db7895a2f9d489c693d6667d3cfc6adc6ec987dc4e5d7534b066cab53389e01f77f1cd5a48657211fea53bee90d203dd8fbdc9664ce9a43366d92753a624420b6d6c87b8c1bc00ce8c29e08c23d01711a1c95ead&landingPath=9db791767f90a0367c019748e51d12609fe062c92941a835374aec1e1e2f151edbe11424ab25ac035ca65a06663c6701f8ac6d1250357b32e15a63e63998d71d
[generic] Extracting URL: https://idolplus.com/web/vod/share/content?params=b7025f006b7521d0374b5be3b4b18756b557c154ae3231c4bc8d46eea11be3c0f806a6d8ef8e4147c81618b1db5be762b45515e07b2216217036a853db7895a2f9d489c693d6667d3cfc6adc6ec987dc4e5d7534b066cab53389e01f77f1cd5a48657211fea53bee90d203dd8fbdc9664ce9a43366d92753a624420b6d6c87b8c1bc00ce8c29e08c23d01711a1c95ead&landingPath=9db791767f90a0367c019748e51d12609fe062c92941a835374aec1e1e2f151edbe11424ab25ac035ca65a06663c6701f8ac6d1250357b32e15a63e63998d71d
[generic] content?params=b7025f006b7521d0374b5be3b4b18756b557c154ae3231c4bc8d46eea11be3c0f806a6d8ef8e4147c81618b1db5be762b45515e07b2216217036a853db7895a2f9d489c693d6667d3cfc6adc6ec987dc4e5d7534b066cab53389e01f77f1cd5a48657211fea53bee90d203dd8fbdc9664ce9a43366d92753a624420b6d6c87b8c1bc00ce8c29e08c23d01711a1c95ead&landingPath=9db791767f90a0367c019748e51d12609fe062c92941a835374aec1e1e2f151edbe11424ab25ac035ca65a06663c6701f8ac6d1250357b32e15a63e63998d71d: Downloading webpage
WARNING: [generic] Falling back on generic information extractor
[generic] content?params=b7025f006b7521d0374b5be3b4b18756b557c154ae3231c4bc8d46eea11be3c0f806a6d8ef8e4147c81618b1db5be762b45515e07b2216217036a853db7895a2f9d489c693d6667d3cfc6adc6ec987dc4e5d7534b066cab53389e01f77f1cd5a48657211fea53bee90d203dd8fbdc9664ce9a43366d92753a624420b6d6c87b8c1bc00ce8c29e08c23d01711a1c95ead&landingPath=9db791767f90a0367c019748e51d12609fe062c92941a835374aec1e1e2f151edbe11424ab25ac035ca65a06663c6701f8ac6d1250357b32e15a63e63998d71d: Extracting information
[debug] Looking for embeds
ERROR: Unsupported URL: https://idolplus.com/web/vod/share/content?params=b7025f006b7521d0374b5be3b4b18756b557c154ae3231c4bc8d46eea11be3c0f806a6d8ef8e4147c81618b1db5be762b45515e07b2216217036a853db7895a2f9d489c693d6667d3cfc6adc6ec987dc4e5d7534b066cab53389e01f77f1cd5a48657211fea53bee90d203dd8fbdc9664ce9a43366d92753a624420b6d6c87b8c1bc00ce8c29e08c23d01711a1c95ead&landingPath=9db791767f90a0367c019748e51d12609fe062c92941a835374aec1e1e2f151edbe11424ab25ac035ca65a06663c6701f8ac6d1250357b32e15a63e63998d71d
Traceback (most recent call last):
  File "yt_dlp\YoutubeDL.py", line 1502, in wrapper
  File "yt_dlp\YoutubeDL.py", line 1578, in __extract_info
  File "yt_dlp\extractor\common.py", line 680, in extract
  File "yt_dlp\extractor\generic.py", line 2523, in _real_extract
yt_dlp.utils.UnsupportedError: Unsupported URL: https://idolplus.com/web/vod/share/content?params=b7025f006b7521d0374b5be3b4b18756b557c154ae3231c4bc8d46eea11be3c0f806a6d8ef8e4147c81618b1db5be762b45515e07b2216217036a853db7895a2f9d489c693d6667d3cfc6adc6ec987dc4e5d7534b066cab53389e01f77f1cd5a48657211fea53bee90d203dd8fbdc9664ce9a43366d92753a624420b6d6c87b8c1bc00ce8c29e08c23d01711a1c95ead&landingPath=9db791767f90a0367c019748e51d12609fe062c92941a835374aec1e1e2f151edbe11424ab25ac035ca65a06663c6701f8ac6d1250357b32e15a63e63998d71d
```



Discussion:

Resolving patch:
diff --git a/yt_dlp/extractor/_extractors.py b/yt_dlp/extractor/_extractors.py
--- a/yt_dlp/extractor/_extractors.py
+++ b/yt_dlp/extractor/_extractors.py
@@ -763,6 +763,7 @@
     IchinanaLiveIE,
     IchinanaLiveClipIE,
 )
+from .idolplus import IdolPlusIE
 from .ign import (
     IGNIE,
     IGNVideoIE,
diff --git a/yt_dlp/extractor/idolplus.py b/yt_dlp/extractor/idolplus.py
new file mode 100644
--- /dev/null
+++ b/yt_dlp/extractor/idolplus.py
@@ -0,0 +1,115 @@
+from .common import InfoExtractor
+from ..utils import traverse_obj, try_call, url_or_none
+
+
+class IdolPlusIE(InfoExtractor):
+    _VALID_URL = r'https?://(?:www\.)?idolplus\.com/z[us]/(?:concert/|contents/?\?(?:[^#]+&)?albumId=)(?P<id>\w+)'
+    _TESTS = [{
+        'url': 'https://idolplus.com/zs/contents?albumId=M012077298PPV00',
+        'md5': '2ace3f4661c943a2f7e79f0b88cea1e7',
+        'info_dict': {
+            'id': 'M012077298PPV00',
+            'ext': 'mp4',
+            'title': '[MultiCam] Aegyo on Top of Aegyo (IZ*ONE EATING TRIP)',
+            'release_date': '20200707',
+            'formats': 'count:65',
+        },
+        'params': {'format': '532-KIM_MINJU'},
+    }, {
+        'url': 'https://idolplus.com/zs/contents?albumId=M01232H058PPV00&catId=E9TX5',
+        'info_dict': {
+            'id': 'M01232H058PPV00',
+            'ext': 'mp4',
+            'title': 'YENA (CIRCLE CHART MUSIC AWARDS 2022 RED CARPET)',
+            'release_date': '20230218',
+            'formats': 'count:5',
+        },
+        'params': {'skip_download': 'm3u8'},
+    }, {
+        # live stream
+        'url': 'https://idolplus.com/zu/contents?albumId=M012323174PPV00',
+        'info_dict': {
+            'id': 'M012323174PPV00',
+            'ext': 'mp4',
+            'title': 'Hanteo Music Awards 2022 DAY2',
+            'release_date': '20230211',
+            'formats': 'count:5',
+        },
+        'params': {'skip_download': 'm3u8'},
+    }, {
+        'url': 'https://idolplus.com/zs/concert/M012323039PPV00',
+        'info_dict': {
+            'id': 'M012323039PPV00',
+            'ext': 'mp4',
+            'title': 'CIRCLE CHART MUSIC AWARDS 2022',
+            'release_date': '20230218',
+            'formats': 'count:5',
+        },
+        'params': {'skip_download': 'm3u8'},
+    }]
+
+    def _real_extract(self, url):
+        video_id = self._match_id(url)
+        data_list = traverse_obj(self._download_json(
+            'https://idolplus.com/api/zs/viewdata/ruleset/build', video_id,
+            headers={'App_type': 'web', 'Country_Code': 'KR'}, query={
+                'rulesetId': 'contents',
+                'albumId': video_id,
+                'distribute': 'PRD',
+                'loggedIn': 'false',
+                'region': 'zs',
+                'countryGroup': '00010',
+                'lang': 'en',
+                'saId': '999999999998',
+            }), ('data', 'viewData', ...))
+
+        player_data = {}
+        while data_list:
+            player_data = data_list.pop()
+            if traverse_obj(player_data, 'type') == 'player':
+                break
+            elif traverse_obj(player_data, ('dataList', ...)):
+                data_list += player_data['dataList']
+
+        formats = self._extract_m3u8_formats(traverse_obj(player_data, (
+            'vodPlayerList', 'vodProfile', 0, 'vodServer', 0, 'video_url', {url_or_none})), video_id)
+
+        subtitles = {}
+        for caption in traverse_obj(player_data, ('vodPlayerList', 'caption')) or []:
+            subtitles.setdefault(caption.get('lang') or 'und', []).append({
+                'url': caption.get('smi_url'),
+                'ext': 'vtt',
+            })
+
+        # Add member multicams as alternative formats
+        if (traverse_obj(player_data, ('detail', 'has_cuesheet')) == 'Y'
+                and traverse_obj(player_data, ('detail', 'is_omni_member')) == 'Y'):
+            cuesheet = traverse_obj(self._download_json(
+                'https://idolplus.com/gapi/contents/v1.0/content/cuesheet', video_id,
+                'Downloading JSON metadata for member multicams',
+                headers={'App_type': 'web', 'Country_Code': 'KR'}, query={
+                    'ALBUM_ID': video_id,
+                    'COUNTRY_GRP': '00010',
+                    'LANG': 'en',
+                    'SA_ID': '999999999998',
+                    'COUNTRY_CODE': 'KR',
+                }), ('data', 'cuesheet_item', 0))
+
+            for member in traverse_obj(cuesheet, ('members', ...)):
+                index = try_call(lambda: int(member['omni_view_index']) - 1)
+                member_video_url = traverse_obj(cuesheet, ('omni_view', index, 'cdn_url', 0, 'url', {url_or_none}))
+                if not member_video_url:
+                    continue
+                member_formats = self._extract_m3u8_formats(
+                    member_video_url, video_id, note=f'Downloading m3u8 for multicam {member["name"]}')
+                for mf in member_formats:
+                    mf['format_id'] = f'{mf["format_id"]}-{member["name"].replace(" ", "_")}'
+                formats.extend(member_formats)
+
+        return {
+            'id': video_id,
+            'title': traverse_obj(player_data, ('detail', 'albumName')),
+            'formats': formats,
+            'subtitles': subtitles,
+            'release_date': traverse_obj(player_data, ('detail', 'broadcastDate')),
+        }


Test patch: