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

Code before:
            for p in re.findall(r'list\[\]=/playlist/([^/]+)/', unescapeHTML(mobj.group('url'))):
                yield '//dailymotion.com/playlist/%s' % p


class DailymotionSearchIE(DailymotionPlaylistBaseIE):
    IE_NAME = 'dailymotion:search'
    _VALID_URL = r'(?:https?://)?(?:www\.)?dailymotion\.[a-z]{2,3}/search/(?P<id>[^/]+)/videos'
    _PAGE_SIZE = 20
    _TESTS = [{
        'url': 'http://www.dailymotion.com/search/king of turtles/videos',
        'info_dict': {
            'id': 'king of turtles',
            'title': 'king of turtles',
        },
        'playlist_mincount': 90,
    }]

    def _call_search_api(self, term, page, note):
        search_query = 'query SEARCH_QUERY( $query: String! $page: Int $limit: Int ) { search { videos( query: $query first: $limit page: $page ) { edges { node { xid } } } } } '

        if not self._HEADERS.get('Authorization'):
            self._HEADERS['Authorization'] = f'Bearer {self._get_token(term)}'
        resp = self._download_json(
            'https://graphql.api.dailymotion.com/', None, note, data=json.dumps({
                'operationName': 'SEARCH_QUERY',
                'query': search_query,
                'variables': {
                    'limit': 20,
                    'page': page,
                    'query': term,
                }
            }).encode(), headers=self._HEADERS)
        obj = traverse_obj(resp, ('data', 'search', {dict}))
        if not obj:
            raise ExtractorError(
                traverse_obj(resp, ('errors', 0, 'message', {str})) or 'Could not fetch search data')

        return obj

    def _fetch_page(self, term, page):
        page += 1
        response = self._call_search_api(term, page, f'Searching "{term}" page {page}')
        for xid in traverse_obj(response, ('videos', 'edges', ..., 'node', 'xid')):
            yield self.url_result(f'https://www.dailymotion.com/video/{xid}', DailymotionIE, xid)

    def _real_extract(self, url):
        term = urllib.parse.unquote_plus(self._match_id(url))
        return self.playlist_result(
            OnDemandPagedList(functools.partial(self._fetch_page, term), self._PAGE_SIZE), term, term)



Review comment (security):
```suggestion
_SEARCH_QUERY = 'query SEARCH_QUERY( $query: String! $page: Int $limit: Int ) { search { videos( query: $query first: $limit page: $page ) { edges { node { xid } } } } } '

    def _call_search_api(self, term, page, note):
        if not self._HEADERS.get('Authorization'):
            self._HEADERS['Authorization'] = f'Bearer {self._get_token(term)}'
        resp = self._download_json(
            'https://graphql.api.dailymotion.com/', None, note, data=json.dumps({
                'operationName': 'SEARCH_QUERY',
                'query': self._SEARCH_QUERY,
```

Code after:
                r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//(?:www\.)?dailymotion\.[a-z]{2,3}/widget/jukebox\?.+?)\1',
                webpage):
            for p in re.findall(r'list\[\]=/playlist/([^/]+)/', unescapeHTML(mobj.group('url'))):
                yield '//dailymotion.com/playlist/%s' % p


class DailymotionSearchIE(DailymotionPlaylistBaseIE):
    IE_NAME = 'dailymotion:search'
    _VALID_URL = r'https?://(?:www\.)?dailymotion\.[a-z]{2,3}/search/(?P<id>[^/?#]+)/videos'
    _PAGE_SIZE = 20
    _TESTS = [{
        'url': 'http://www.dailymotion.com/search/king of turtles/videos',
        'info_dict': {
            'id': 'king of turtles',
            'title': 'king of turtles',
        },
        'playlist_mincount': 90,
    }]
    _SEARCH_QUERY = 'query SEARCH_QUERY( $query: String! $page: Int $limit: Int ) { search { videos( query: $query first: $limit page: $page ) { edges { node { xid } } } } } '

    def _call_search_api(self, term, page, note):
        if not self._HEADERS.get('Authorization'):
            self._HEADERS['Authorization'] = f'Bearer {self._get_token(term)}'
        resp = self._download_json(
            'https://graphql.api.dailymotion.com/', None, note, data=json.dumps({
                'operationName': 'SEARCH_QUERY',
                'query': self._SEARCH_QUERY,
                'variables': {
                    'limit': 20,
                    'page': page,
                    'query': term,
                }
            }).encode(), headers=self._HEADERS)
        obj = traverse_obj(resp, ('data', 'search', {dict}))
        if not obj:
            raise ExtractorError(
                traverse_obj(resp, ('errors', 0, 'message', {str})) or 'Could not fetch search data')

        return obj

    def _fetch_page(self, term, page):
        page += 1
        response = self._call_search_api(term, page, f'Searching "{term}" page {page}')
        for xid in traverse_obj(response, ('videos', 'edges', ..., 'node', 'xid')):
            yield self.url_result(f'https://www.dailymotion.com/video/{xid}', DailymotionIE, xid)

    def _real_extract(self, url):
        term = urllib.parse.unquote_plus(self._match_id(url))
        return self.playlist_result(
            OnDemandPagedList(functools.partial(self._fetch_page, term), self._PAGE_SIZE), term, term)