Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/tarangplus.py (Python)

Code before:
        'url': 'https://tarangplus.in/kids-shows/chhota-jaga/chhota-jaga-ep-33-jamidar-ra-khajana-adaya',
        'only_matching': True,
    }, {
        'url': 'https://tarangplus.in/movies/swayambara',
        'only_matching': True,
    }]

    def decrypt(self, data, key):
        if not Cryptodome.AES:
            raise ExtractorError('pycryptodomex not found. Please install', expected=True)
        iv = binascii.unhexlify('00000000000000000000000000000000')
        cipher = Cryptodome.AES.new(base64.b64decode(key), Cryptodome.AES.MODE_CBC, iv)
        return cipher.decrypt(base64.b64decode(data)).decode('utf-8')

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        hidden_inputs_data = self._hidden_inputs(webpage)
        json_ld_data = self._search_json_ld(webpage, display_id)
        del json_ld_data['url']
        iframe_div = get_element_html_by_class('item_details_page', webpage)
        iframe_elem = get_element_text_and_html_by_tag('iframe', iframe_div)[1]
        iframe_src = extract_attributes(iframe_elem)['src']
        url_data = re.search(r'contenturl=(?P<data>[^|]+).*key=(?P<key>[^|]+)', iframe_src)
        m3u8_url = self.decrypt(url_data.group('data'), url_data.group('key'))
        return {
            'display_id': display_id,
            **json_ld_data,
            **traverse_obj(hidden_inputs_data, {
                'id': ('content_id', {str}),
                'media_type': ('theme_type', {str}),
                'categories': ('genre', {str}, filter, all, filter),
            }),
            'formats': self._extract_m3u8_formats(m3u8_url, display_id),
        }


class TarangPlusPlaylistIE(TarangPlusBaseIE):
    IE_NAME = 'tarangplus:playlist'
    _VALID_URL = r'https?://(?:www\.)?tarangplus\.in/[^#?/]+/(?P<id>[^#?/]+)/episodes$'
    _TESTS = [{
        'url': 'https://tarangplus.in/tarangaplus-originals/balijatra/episodes',
        'info_dict': {
            'id': 'balijatra',
            'title': 'Balijatra',
        },
        'playlist_mincount': 7,
    }, {
        'url': 'https://tarangplus.in/tarang-serials/bada-bohu/episodes',
        'info_dict': {
            'id': 'bada-bohu',

Review comment (refactor):
I would write it like this... it gives a nice clean error ("Unable to extract `content`") and we can easily access the other info in the parsed `contenturl`, e.g. by adding the `id` fallback

Of note, the parsed metadata also contains `title` and `is_premium` fields

```suggestion
        content = traverse_obj(webpage, (
            {find_element(tag='iframe', attr='src', value=r'.+[?&]contenturl=.+', html=True, regex=True)},
            {extract_attributes}, 'src', {parse_qs}, 'contenturl', -1, {require('content')}))
        encrypted_data, *attrs = content.split('|')
        metadata = {k: v for (k, _, v) in (attr.partition('=') for attr in attrs)}
        m3u8_url = self.decrypt(encrypted_data, metadata['key'])

        return {
            'id': metadata.get('content_id') or display_id,
```

Code after:
        'only_matching': True,
    }, {
        'url': 'https://tarangplus.in/kids-shows/chhota-jaga/chhota-jaga-ep-33-jamidar-ra-khajana-adaya',
        'only_matching': True,
    }, {
        'url': 'https://tarangplus.in/movies/swayambara',
        'only_matching': True,
    }]

    def decrypt(self, data, key):
        if not Cryptodome.AES:
            raise ExtractorError('pycryptodomex not found. Please install', expected=True)
        iv = binascii.unhexlify('00000000000000000000000000000000')
        cipher = Cryptodome.AES.new(base64.b64decode(key), Cryptodome.AES.MODE_CBC, iv)
        return cipher.decrypt(base64.b64decode(data)).decode('utf-8')

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        hidden_inputs_data = self._hidden_inputs(webpage)
        json_ld_data = self._search_json_ld(webpage, display_id)
        json_ld_data.pop('url', None)

        iframe_url = traverse_obj(webpage, (
            {find_element(tag='iframe', attr='src', value=r'.+[?&]contenturl=.+', html=True, regex=True)},
            {extract_attributes}, 'src', {require('iframe URL')}))
        # Can't use parse_qs here since it would decode the encrypted base64 `+` chars to spaces
        content = self._search_regex(r'[?&]contenturl=(.+)', iframe_url, 'content')
        encrypted_data, _, attrs = content.partition('|')
        metadata = {
            m.group('k'): m.group('v')
            for m in re.finditer(r'(?:^|\|)(?P<k>[a-z_]+)=(?P<v>(?:(?!\|[a-z_]+=).)+)', attrs)
        }
        m3u8_url = self.decrypt(encrypted_data, metadata['key'])

        return {
            'id': display_id,  # Fallback
            'display_id': display_id,
            **json_ld_data,
            **traverse_obj(metadata, {
                'id': ('content_id', {str}),
                'title': ('title', {str}),
                'thumbnail': ('image', {str}),
            }),
            **traverse_obj(hidden_inputs_data, {
                'id': ('content_id', {str}),
                'media_type': ('theme_type', {str}),
                'categories': ('genre', {str}, filter, all, filter),
            }),
            'formats': self._extract_m3u8_formats(m3u8_url, display_id),
        }