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

Code before:
        'info_dict': {
            'id': 'theguardianswomensfootballweekly',
            'title': "The Guardian's Women's Football Weekly",
            'description': 'md5:e2cc021311e582d29935a73614a43f51'
        },
        'playlist_mincount': 69
    }, {
        'url': 'https://www.theguardian.com/news/series/todayinfocus?page=2',
        'info_dict': {
            'id': 'todayinfocus',
            'title': 'Today in Focus | Page 2 of 66 | News | The Guardian',
            'description': 'md5:0f097764fc0d359e0b6eb537be0387e2'
        },
        'playlist_mincount': 1261
    }, {
        'url': 'https://www.theguardian.com/news/series/the-audio-long-read',
        'info_dict': {
            'id': 'the-audio-long-read',
            'title': 'The Audio Long Read',
            'description': 'md5:5462994a27527309562b25b6defc4ef3'
        },
        'playlist_mincount': 996
    }]

    def _entries(self, url, page, playlist_id):
        for page in itertools.count(int_or_none(page) or 1):
            webpage, urlh = self._download_webpage_handle(
                url, playlist_id, f'Downloading page {page}', query={'page': page})
            if '?' not in urlh.url:
                break

            episodes = get_elements_html_by_class('fc-item--type-media', webpage)
            for url_path in traverse_obj(episodes, (..., {extract_attributes}, 'data-id')):
                episode_url = urljoin('https://www.theguardian.com', url_path)
                if TheGuardianPodcastIE.suitable(episode_url):
                    yield episode_url

    def _real_extract(self, url):
        podcast_id, page = self._match_valid_url(url).group('id', 'page')

        webpage = self._download_webpage(url, podcast_id)

        title = (clean_html(get_element_by_class(
            'index-page-header__title', webpage)) or self._generic_title(url, webpage))
        description = self._og_search_description(webpage) or self._html_search_meta(
            'description', webpage)

        return self.playlist_from_matches(
            self._entries(url, page, podcast_id), podcast_id, title, description=description,
            ie=TheGuardianPodcastIE)


Review comment (refactor):
I think we should ignore the query passed with the URL. Sorry if that wasn't clear from my previous review comment. I was only trying to say we can't append to the URL with an f-string because of a potential query in the input URL

The most consistent behavior is to always extract all pages of the playlist, regardless of input url query. The user can pass `--playlist-items` to specify if needed

Code after:
        'info_dict': {
            'id': 'theguardianswomensfootballweekly',
            'title': "The Guardian's Women's Football Weekly",
            'description': 'md5:e2cc021311e582d29935a73614a43f51'
        },
        'playlist_mincount': 69
    }, {
        'url': 'https://www.theguardian.com/news/series/todayinfocus?page=2',
        'info_dict': {
            'id': 'todayinfocus',
            'title': 'Today in Focus',
            'description': 'md5:0f097764fc0d359e0b6eb537be0387e2'
        },
        'playlist_mincount': 1261
    }, {
        'url': 'https://www.theguardian.com/news/series/the-audio-long-read',
        'info_dict': {
            'id': 'the-audio-long-read',
            'title': 'The Audio Long Read',
            'description': 'md5:5462994a27527309562b25b6defc4ef3'
        },
        'playlist_mincount': 996
    }]

    def _entries(self, url, playlist_id):
        for page in itertools.count(1):
            webpage, urlh = self._download_webpage_handle(
                url, playlist_id, f'Downloading page {page}', query={'page': page})
            if 'page' not in parse_qs(urlh.url):
                break

            episodes = get_elements_html_by_class('fc-item--type-media', webpage)
            for url_path in traverse_obj(episodes, (..., {extract_attributes}, 'data-id')):
                yield url_path

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

        webpage = self._download_webpage(url, podcast_id)

        title = clean_html(get_element_by_class(
            'index-page-header__title', webpage) or get_element_by_class('flagship-audio__title', webpage))
        description = self._og_search_description(webpage) or self._html_search_meta(
            'description', webpage)

        return self.playlist_from_matches(
            self._entries(url, podcast_id), podcast_id, title, description=description,
            ie=TheGuardianPodcastIE, getter=lambda x: urljoin('https://www.theguardian.com', x))