ronantakizawa/codereview-bench huggingface dataset:
Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/goplay.py (Python)

Code before:
import base64
import binascii
import datetime as dt
import hashlib
import hmac
import json
import os
import urllib.parse

from .common import InfoExtractor
from ..utils import ExtractorError, int_or_none
from ..utils.traversal import get_first, traverse_obj


class GoPlayIE(InfoExtractor):
    _VALID_URL = r'https?://(www\.)?play\.tv/video/([^/?#]+/[^/?#]+/|)(?P<id>[^/#]+)'

    _NETRC_MACHINE = 'goplay'

    _TESTS = [{
        'url': 'https://www.play.tv/video/de-slimste-mens-ter-wereld/de-slimste-mens-ter-wereld-s22/de-slimste-mens-ter-wereld-s22-aflevering-1',
        'info_dict': {
            'id': '2baa4560-87a0-421b-bffc-359914e3c387',
            'ext': 'mp4',
            'title': 'De Slimste Mens ter Wereld - S22 - Aflevering 1',
            'description': r're:In aflevering 1 nemen Daan Alferink, Tess Elst en Xander De Rycke .{66}',
            'series': 'De Slimste Mens ter Wereld',
            'episode': 'Wordt aangekondigd',
            'season_number': 22,
            'episode_number': 1,
            'season': 'Season 22',
        },
        'params': {'skip_download': True},
        'skip': 'This video is only available for registered users',
    }, {
        'url': 'https://www.play.tv/video/1917',
        'info_dict': {
            'id': '40cac41d-8d29-4ef5-aa11-75047b9f0907',
            'ext': 'mp4',
            'title': '1917',

Review comment (refactor):
```suggestion
class GoPlayIE(InfoExtractor):
    IE_NAME = 'play.tv'
    IE_DESC = 'PLAY (formerly goplay.be)'
```

Code after:
import base64
import binascii
import datetime as dt
import hashlib
import hmac
import json
import os
import urllib.parse

from .common import InfoExtractor
from ..utils import ExtractorError, int_or_none
from ..utils.traversal import get_first, traverse_obj


class GoPlayIE(InfoExtractor):
    IE_NAME = 'play.tv'
    IE_DESC = 'PLAY (formerly goplay.be)'
    _VALID_URL = r'https?://(www\.)?play\.tv/video/([^/?#]+/[^/?#]+/|)(?P<id>[^/#]+)'

    _NETRC_MACHINE = 'goplay'

    _TESTS = [{
        'url': 'https://www.play.tv/video/de-slimste-mens-ter-wereld/de-slimste-mens-ter-wereld-s22/de-slimste-mens-ter-wereld-s22-aflevering-1',
        'info_dict': {
            'id': '2baa4560-87a0-421b-bffc-359914e3c387',
            'ext': 'mp4',
            'title': 'De Slimste Mens ter Wereld - S22 - Aflevering 1',
            'description': r're:In aflevering 1 nemen Daan Alferink, Tess Elst en Xander De Rycke .{66}',
            'series': 'De Slimste Mens ter Wereld',
            'episode': 'Wordt aangekondigd',
            'season_number': 22,
            'episode_number': 1,
            'season': 'Season 22',
        },
        'params': {'skip_download': True},
        'skip': 'This video is only available for registered users',
    }, {
        'url': 'https://www.play.tv/video/1917',
        'info_dict': {
            'id': '40cac41d-8d29-4ef5-aa11-75047b9f0907',
Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/mux.py (Python)

Code before:
from .common import InfoExtractor


class MuxStreamNewIE(InfoExtractor):
    IE_NAME = 'stream.new'
    _WORKING = True
    _VALID_URL = r'https?://stream.new/v/(?P<id>[A-Za-z0-9-]+)(/embed)?'
    _TESTS = [{
        'url': 'https://stream.new/v/36d8c00L1vEFwF01aF1cRKQs8nblgegMda8YR9CE3wejk/embed',
        'info_dict': {
            'id': '36d8c00L1vEFwF01aF1cRKQs8nblgegMda8YR9CE3wejk',
            'title': '36d8c00L1vEFwF01aF1cRKQs8nblgegMda8YR9CE3wejk',
            'formats': 'mincount:1',
            'ext': 'mp4',
            '_type': 'video',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)

        video_url = 'https://stream.mux.com/%s.m3u8' % video_id

        formats = self._extract_m3u8_formats(
            video_url, video_id, 'mp4',
            entry_protocol='m3u8_native', m3u8_id='hls', fatal=False)

        return {

Review comment (suggestion):
A trailing optional match is redundant
```suggestion
    _VALID_URL = r'https?://stream.new/v/(?P<id>[A-Za-z0-9-]+)'
```

Code after:
import re

from .common import InfoExtractor
from ..utils import (
    extract_attributes,
    filter_dict,
    parse_qs,
    smuggle_url,
    unsmuggle_url,
    update_url_query,
)
from ..utils.traversal import traverse_obj


class MuxIE(InfoExtractor):
    _VALID_URL = r'https?://(?:stream\.new/v|player\.mux\.com)/(?P<id>[A-Za-z0-9-]+)'
    _EMBED_REGEX = [r'<iframe\b[^>]+\bsrc=["\'](?P<url>(?:https?:)?//(?:stream\.new/v|player\.mux\.com)/(?P<id>[A-Za-z0-9-]+)[^"\']+)']
    _TESTS = [{
        'url': 'https://stream.new/v/OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j/embed',
        'info_dict': {
            'ext': 'mp4',
            'id': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
            'title': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
        },
    }, {
        'url': 'https://player.mux.com/OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
        'info_dict': {
            'ext': 'mp4',
            'id': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
            'title': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
        },
    }]
Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/mux.py (Python)

Code before:
from .common import InfoExtractor


class MuxStreamNewIE(InfoExtractor):
    IE_NAME = 'stream.new'
    _VALID_URL = r'https?://(?:stream\.new/v|player\.mux\.com)/(?P<id>[A-Za-z0-9-]+)'
    _EMBED_REGEX = [r'<iframe\b[^>]+\bsrc=["\'](?P<url>(?:https?:)?//(?:stream\.new/v|player\.mux\.com)/(?P<id>[A-Za-z0-9-]+))']
    _TESTS = [{
        'url': 'https://stream.new/v/36d8c00L1vEFwF01aF1cRKQs8nblgegMda8YR9CE3wejk/embed',
        'info_dict': {
            'id': '36d8c00L1vEFwF01aF1cRKQs8nblgegMda8YR9CE3wejk',
            'title': '36d8c00L1vEFwF01aF1cRKQs8nblgegMda8YR9CE3wejk',
            'formats': 'mincount:1',
            'ext': 'mp4',
            '_type': 'video',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)

        formats = self._extract_m3u8_formats(
            f'https://stream.mux.com/{video_id}.m3u8', video_id, 'mp4')

        return {
            'id': video_id,

Review comment (refactor):
Since this all the same infrastructure, might as well name it `MuxIE`
```suggestion
class MuxIE(InfoExtractor):
```

Code after:
import re

from .common import InfoExtractor
from ..utils import (
    extract_attributes,
    filter_dict,
    parse_qs,
    smuggle_url,
    unsmuggle_url,
    update_url_query,
)
from ..utils.traversal import traverse_obj


class MuxIE(InfoExtractor):
    _VALID_URL = r'https?://(?:stream\.new/v|player\.mux\.com)/(?P<id>[A-Za-z0-9-]+)'
    _EMBED_REGEX = [r'<iframe\b[^>]+\bsrc=["\'](?P<url>(?:https?:)?//(?:stream\.new/v|player\.mux\.com)/(?P<id>[A-Za-z0-9-]+)[^"\']+)']
    _TESTS = [{
        'url': 'https://stream.new/v/OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j/embed',
        'info_dict': {
            'ext': 'mp4',
            'id': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
            'title': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
        },
    }, {
        'url': 'https://player.mux.com/OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
        'info_dict': {
            'ext': 'mp4',
            'id': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
            'title': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/mux.py (Python)

Code before:
import re

from .common import InfoExtractor
from ..utils import extract_attributes, filter_dict, parse_qs, update_url_query
from ..utils.traversal import traverse_obj


class MuxIE(InfoExtractor):
    _VALID_URL = r'https?://(?:stream\.new/v|player\.mux\.com)/(?P<id>[A-Za-z0-9-]+)'
    _EMBED_REGEX = [r'<iframe\b[^>]+\bsrc=["\'](?P<url>(?:https?:)?//(?:stream\.new/v|player\.mux\.com)/(?P<id>[A-Za-z0-9-]+)[^"\']+)']
    _TESTS = [{
        'url': 'https://stream.new/v/OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j/embed',
        'info_dict': {
            'ext': 'mp4',
            'id': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
            'title': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
        },
    }, {
        'url': 'https://player.mux.com/OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
        'info_dict': {
            'ext': 'mp4',
            'id': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
            'title': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
        },
    }]
    _WEBPAGE_TESTS = [{
        # iframe embed
        'url': 'https://www.redbrickai.com/blog/2025-07-14-FAST-brush',
        'info_dict': {

Review comment (refactor):
```suggestion
from ..utils import (
    extract_attributes,
    filter_dict,
    parse_qs,
    smuggle_url,
    unsmuggle_url,
    update_url_query,
)
```

Code after:
import re

from .common import InfoExtractor
from ..utils import (
    extract_attributes,
    filter_dict,
    parse_qs,
    smuggle_url,
    unsmuggle_url,
    update_url_query,
)
from ..utils.traversal import traverse_obj


class MuxIE(InfoExtractor):
    _VALID_URL = r'https?://(?:stream\.new/v|player\.mux\.com)/(?P<id>[A-Za-z0-9-]+)'
    _EMBED_REGEX = [r'<iframe\b[^>]+\bsrc=["\'](?P<url>(?:https?:)?//(?:stream\.new/v|player\.mux\.com)/(?P<id>[A-Za-z0-9-]+)[^"\']+)']
    _TESTS = [{
        'url': 'https://stream.new/v/OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j/embed',
        'info_dict': {
            'ext': 'mp4',
            'id': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
            'title': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
        },
    }, {
        'url': 'https://player.mux.com/OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
        'info_dict': {
            'ext': 'mp4',
            'id': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/_extractors.py (Python)

Code before:
from .magentamusik import MagentaMusikIE
from .mailru import (
    MailRuIE,
    MailRuMusicIE,
    MailRuMusicSearchIE,
)
from .mainstreaming import MainStreamingIE
from .mangomolo import (
    MangomoloLiveIE,
    MangomoloVideoIE,
)
from .manoto import (
    ManotoTVIE,
    ManotoTVLiveIE,
    ManotoTVShowIE,
)
from .manyvids import ManyVidsIE
from .maoritv import MaoriTVIE
from .markiza import (
    MarkizaIE,
    MarkizaPageIE,
)
from .massengeschmacktv import MassengeschmackTVIE
from .masters import MastersIE
from .matchtv import MatchTVIE
from .mave import MaveChannelIE, MaveIE
from .mbn import MBNIE
from .mdr import MDRIE
from .medaltv import MedalTVIE
from .mediaite import MediaiteIE
from .mediaklikk import MediaKlikkIE
from .medialaan import MedialaanIE
from .mediaset import (
    MediasetIE,
    MediasetShowIE,
)
from .mediasite import (
    MediasiteCatalogIE,
    MediasiteIE,
    MediasiteNamedCatalogIE,
)
from .mediastream import (
    MediaStreamIE,
    WinSportsVideoIE,
)
from .mediaworksnz import MediaWorksNZVODIE
from .medici import MediciIE
from .megaphone import MegaphoneIE
from .megatvcom import (
    MegaTVComEmbedIE,
    MegaTVComIE,

Review comment (suggestion):
```suggestion
from .mave import (
    MaveChannelIE,
    MaveIE,
)
```

Code after:
from .magentamusik import MagentaMusikIE
from .mailru import (
    MailRuIE,
    MailRuMusicIE,
    MailRuMusicSearchIE,
)
from .mainstreaming import MainStreamingIE
from .mangomolo import (
    MangomoloLiveIE,
    MangomoloVideoIE,
)
from .manoto import (
    ManotoTVIE,
    ManotoTVLiveIE,
    ManotoTVShowIE,
)
from .manyvids import ManyVidsIE
from .maoritv import MaoriTVIE
from .markiza import (
    MarkizaIE,
    MarkizaPageIE,
)
from .massengeschmacktv import MassengeschmackTVIE
from .masters import MastersIE
from .matchtv import MatchTVIE
from .mave import (
    MaveChannelIE,
    MaveIE,
)
from .mbn import MBNIE
from .mdr import MDRIE
from .medaltv import MedalTVIE
from .mediaite import MediaiteIE
from .mediaklikk import MediaKlikkIE
from .medialaan import MedialaanIE
from .mediaset import (
    MediasetIE,
    MediasetShowIE,
)
from .mediasite import (
    MediasiteCatalogIE,
    MediasiteIE,
    MediasiteNamedCatalogIE,
)
from .mediastream import (
    MediaStreamIE,
    WinSportsVideoIE,
)
from .mediaworksnz import MediaWorksNZVODIE
from .medici import MediciIE
from .megaphone import MegaphoneIE
Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/mave.py (Python)

Code before:
    }]

    def _real_extract(self, url):
        channel_id = self._match_id(url)

        channel_meta = self._load_channel_meta(channel_id)

        entries = []
        page = 1

        while True:
            data = self._download_json(
                f'{self._API_BASE_URL}/{channel_id}/episodes?'
                f'view=all&page={page}&sort=newest&format=all',
                channel_id)

            episodes = data.get('episodes', [])

            if not episodes:
                break
            else:
                for episode_meta in data['episodes']:
                    entries.append(self._create_entry(
                        channel_id, channel_meta, episode_meta))

                page += 1

        return {
            '_type': 'playlist',
            'id': channel_id,
            'title': channel_meta['podcast']['title'],
            'description': channel_meta['podcast']['description'],
            'entries': entries[::-1],
        }


Review comment (refactor):
I would use an InAdvancePagedList here
```suggestion
    _PAGE_SIZE = 50

    def _entries(self, channel_id, channel_meta, page_num):
        page_data = self._download_json(
            f'{self._API_BASE_URL}/{channel_id}/episodes', channel_id, query={
                'view': 'all',
                'page': page_num + 1,
                'sort': 'newest',
                'format': 'all',
            }, note=f'Downloading page {page_num + 1}')
        for ep in traverse_obj(page_data, ('episodes', ..., {dict})):
            yield self._create_entry(channel_id, channel_meta, episode_meta=ep)

    def _real_extract(self, url):
        channel_id = self._match_id(url)

        channel_meta = self._load_channel_meta(channel_id)
```

Code after:
            'description': 'md5:4164d425d60a0d97abdce9d1f6f8e049',
        },
        'playlist_mincount': 80,
    }]
    _PAGE_SIZE = 50

    def _entries(self, channel_id, channel_meta, page_num):
        page_data = self._download_json(
            f'{self._API_BASE_URL}/{channel_id}/episodes', channel_id, query={
                'view': 'all',
                'page': page_num + 1,
                'sort': 'newest',
                'format': 'all',
            }, note=f'Downloading page {page_num + 1}')
        for ep in traverse_obj(page_data, ('episodes', lambda _, v: v['audio'] and v['id'])):
            yield self._create_entry(channel_id, channel_meta, ep)

    def _real_extract(self, url):
        channel_id = self._match_id(url)

        channel_meta = self._load_channel_meta(channel_id, channel_id)

        return {
            '_type': 'playlist',
            'id': channel_id,
            **traverse_obj(channel_meta, {
                'title': ('title', {str}),
                'description': ('description', {str}),
            }),
            'entries': InAdvancePagedList(
                functools.partial(self._entries, channel_id, channel_meta),
                math.ceil(channel_meta['episodes_count'] / self._PAGE_SIZE), self._PAGE_SIZE),
        }
Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/mave.py (Python)

Code before:
        entries = []
        page = 1

        while True:
            data = self._download_json(
                f'{self._API_BASE_URL}/{channel_id}/episodes?'
                f'view=all&page={page}&sort=newest&format=all',
                channel_id)

            episodes = data.get('episodes', [])

            if not episodes:
                break
            else:
                for episode_meta in data['episodes']:
                    entries.append(self._create_entry(
                        channel_id, channel_meta, episode_meta))

                page += 1

        return {
            '_type': 'playlist',
            'id': channel_id,
            'title': channel_meta['podcast']['title'],
            'description': channel_meta['podcast']['description'],
            'entries': entries[::-1],
        }


Review comment (refactor):
And also make the metadata extraction non-fatal
```suggestion
            **traverse_obj(channel_meta, {
                'title': ('podcast', 'title', {str}),
                'description': ('podcast', 'description', {str}),
            }),
            'entries': InAdvancePagedList(
                functools.partial(self._entries, channel_id, channel_meta),
                math.ceil(channel_meta['podcast']['episodes_count'] / self._PAGE_SIZE), self._PAGE_SIZE),
```

Code after:
        page_data = self._download_json(
            f'{self._API_BASE_URL}/{channel_id}/episodes', channel_id, query={
                'view': 'all',
                'page': page_num + 1,
                'sort': 'newest',
                'format': 'all',
            }, note=f'Downloading page {page_num + 1}')
        for ep in traverse_obj(page_data, ('episodes', lambda _, v: v['audio'] and v['id'])):
            yield self._create_entry(channel_id, channel_meta, ep)

    def _real_extract(self, url):
        channel_id = self._match_id(url)

        channel_meta = self._load_channel_meta(channel_id, channel_id)

        return {
            '_type': 'playlist',
            'id': channel_id,
            **traverse_obj(channel_meta, {
                'title': ('title', {str}),
                'description': ('description', {str}),
            }),
            'entries': InAdvancePagedList(
                functools.partial(self._entries, channel_id, channel_meta),
                math.ceil(channel_meta['episodes_count'] / self._PAGE_SIZE), self._PAGE_SIZE),
        }
Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/mave.py (Python)

Code before:
    }, {
        'url': 'https://geekcity.mave.digital/',
        'info_dict': {
            'id': 'geekcity',
            'title': 'Мужчины в трико',
            'description': 'md5:4164d425d60a0d97abdce9d1f6f8e049',
        },
        'playlist_mincount': 80,
    }]
    _PAGE_SIZE = 50

    def _entries(self, channel_id, channel_meta, page_num):
        page_data = self._download_json(
            f'{self._API_BASE_URL}/{channel_id}/episodes', channel_id, query={
                'view': 'all',
                'page': page_num + 1,
                'sort': 'newest',
                'format': 'all',
            }, note=f'Downloading page {page_num + 1}')
        for ep in traverse_obj(page_data, ('episodes', ..., {dict})):
            yield self._create_entry(channel_id, channel_meta, episode_meta=ep)

    def _real_extract(self, url):
        channel_id = self._match_id(url)

        channel_meta = self._load_channel_meta(channel_id)

        return {
            '_type': 'playlist',
            'id': channel_id,
            **traverse_obj(channel_meta, {
                'title': ('podcast', 'title', {str}),
                'description': ('podcast', 'description', {str}),
            }),
            'entries': InAdvancePagedList(
                functools.partial(self._entries, channel_id, channel_meta),
                math.ceil(
                    channel_meta['podcast']['episodes_count'] / self._PAGE_SIZE,
                ),
                self._PAGE_SIZE),
        }


Review comment (suggestion):
```suggestion
        channel_meta = self._load_channel_meta(channel_id, channel_id)
```

Code after:
        'playlist_mincount': 20,
    }, {
        'url': 'https://geekcity.mave.digital/',
        'info_dict': {
            'id': 'geekcity',
            'title': 'Мужчины в трико',
            'description': 'md5:4164d425d60a0d97abdce9d1f6f8e049',
        },
        'playlist_mincount': 80,
    }]
    _PAGE_SIZE = 50

    def _entries(self, channel_id, channel_meta, page_num):
        page_data = self._download_json(
            f'{self._API_BASE_URL}/{channel_id}/episodes', channel_id, query={
                'view': 'all',
                'page': page_num + 1,
                'sort': 'newest',
                'format': 'all',
            }, note=f'Downloading page {page_num + 1}')
        for ep in traverse_obj(page_data, ('episodes', lambda _, v: v['audio'] and v['id'])):
            yield self._create_entry(channel_id, channel_meta, ep)

    def _real_extract(self, url):
        channel_id = self._match_id(url)

        channel_meta = self._load_channel_meta(channel_id, channel_id)

        return {
            '_type': 'playlist',
            'id': channel_id,
            **traverse_obj(channel_meta, {
                'title': ('title', {str}),
                'description': ('description', {str}),
            }),
            'entries': InAdvancePagedList(
                functools.partial(self._entries, channel_id, channel_meta),
                math.ceil(channel_meta['episodes_count'] / self._PAGE_SIZE), self._PAGE_SIZE),
        }
Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/mave.py (Python)

Code before:
                'view': 'all',
                'page': page_num + 1,
                'sort': 'newest',
                'format': 'all',
            }, note=f'Downloading page {page_num + 1}')
        for ep in traverse_obj(page_data, ('episodes', ..., {dict})):
            yield self._create_entry(channel_id, channel_meta, episode_meta=ep)

    def _real_extract(self, url):
        channel_id = self._match_id(url)

        channel_meta = self._load_channel_meta(channel_id)

        return {
            '_type': 'playlist',
            'id': channel_id,
            **traverse_obj(channel_meta, {
                'title': ('podcast', 'title', {str}),
                'description': ('podcast', 'description', {str}),
            }),
            'entries': InAdvancePagedList(
                functools.partial(self._entries, channel_id, channel_meta),
                math.ceil(
                    channel_meta['podcast']['episodes_count'] / self._PAGE_SIZE,
                ),
                self._PAGE_SIZE),
        }


Review comment (suggestion):
```suggestion
                math.ceil(channel_meta['podcast']['episodes_count'] / self._PAGE_SIZE), self._PAGE_SIZE),
```

Code after:
            f'{self._API_BASE_URL}/{channel_id}/episodes', channel_id, query={
                'view': 'all',
                'page': page_num + 1,
                'sort': 'newest',
                'format': 'all',
            }, note=f'Downloading page {page_num + 1}')
        for ep in traverse_obj(page_data, ('episodes', lambda _, v: v['audio'] and v['id'])):
            yield self._create_entry(channel_id, channel_meta, ep)

    def _real_extract(self, url):
        channel_id = self._match_id(url)

        channel_meta = self._load_channel_meta(channel_id, channel_id)

        return {
            '_type': 'playlist',
            'id': channel_id,
            **traverse_obj(channel_meta, {
                'title': ('title', {str}),
                'description': ('description', {str}),
            }),
            'entries': InAdvancePagedList(
                functools.partial(self._entries, channel_id, channel_meta),
                math.ceil(channel_meta['episodes_count'] / self._PAGE_SIZE), self._PAGE_SIZE),
        }