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

Code before:
            'id': '11028566',
            'ext': 'mp4',
            'title': 'Best Tom Hanks Movies',
            'description': 'We look at the filmography of America\'s Dad, as we count down our list of the best Tom Hanks movies.',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 1468.592
        },
    }, {
        'url': 'https://www.rottentomatoes.com/m/toy_story_3/trailers/VycaVoBKhGuk',
        'info_dict': {
            'id': 'VycaVoBKhGuk',
            'ext': 'mp4',
            'title': 'Toy Story 3: Trailer 2',
            'description': '',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 149.941
        },
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        raw_video_info = self._html_search_regex(
            r'<script[^>]*id=["\']videos["\'][^>]*>([^<]+)</script>',
            webpage, 'video info', default=None)
        video_info = self._parse_json(raw_video_info, video_id, transform_source=js_to_json)

        return {
            'id': video_id,
            'title': traverse_obj(video_info, (0, 'title')),
            'description': traverse_obj(video_info, (0, 'description')),
            'thumbnail': traverse_obj(video_info, (0, 'image')),
            'formats': self._extract_m3u8_formats(
                traverse_obj(video_info, (0, 'file')), video_id, 'mp4', 'm3u8_native',
                m3u8_id=traverse_obj(video_info, (0, 'type')), note='Downloading m3u8 information',
                errnote='Unable to download m3u8 information'),
            'duration': parse_duration(traverse_obj(video_info, (0, 'durationInSeconds'),
                                                    default=traverse_obj(video_info, (0, 'duration'))))
        }


class RottenTomatoesPlaylistIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?rottentomatoes\.com/m/(?P<id>[^/]+)(?:/trailers)?/?(?:$|[#?])'

    _TESTS = [{
        'url': 'http://www.rottentomatoes.com/m/toy_story_3',
        'info_dict': {
            'id': 'toy_story_3',
            'title': 'Toy Story 3',
        },
        'playlist_mincount': 4,

Review comment (suggestion):
`js_to_json` is only needed if we are trying to parse a block of javascript that is JSON-like but not actually valid JSON, and needs to be transformed before parsing. This does not apply to what we find in this site's webpages, which is instead one big array of valid JSON. But since it is an array/list, we need to pass the `contains_pattern` arg to `_search_json` so it doesn't look for an object/dict:
```suggestion
        data = self._search_json(
            r'<script[^>]+\bid=["\'](?:heroV|v)ideos["\'][^>]*>', webpage,
            'data', playlist_id, contains_pattern=r'\[{(?s:.+)}\]')
```

Code after:
        'url': 'http://www.rottentomatoes.com/m/toy_story_3/trailers/11028566/',
        'info_dict': {
            'id': '11028566',
            'ext': 'mp4',
            'title': 'Toy Story 3',
            'description': 'From the creators of the beloved TOY STORY films, comes a story that will reunite the gang in a whole new way.'
        },
        'skip': 'No longer available',
    }, {
        'url': 'https://www.rottentomatoes.com/m/toy_story_3/trailers/VycaVoBKhGuk',
        'info_dict': {
            'id': 'VycaVoBKhGuk',
            'ext': 'mp4',
            'title': 'Toy Story 3: Trailer 2',
            'description': '',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 149.941
        },
    }, {
        'url': 'http://www.rottentomatoes.com/m/toy_story_3',
        'info_dict': {
            'id': 'toy_story_3',
            'title': 'Toy Story 3',
        },
        'playlist_mincount': 4,
    }, {
        'url': 'http://www.rottentomatoes.com/m/toy_story_3/trailers',
        'info_dict': {
            'id': 'toy_story_3-trailers',
        },
        'playlist_mincount': 5,
    }]

    def _extract_videos(self, data, display_id):
        for video in traverse_obj(data, (lambda _, v: v['publicId'] and v['file'] and v['type'] == 'hls')):
            yield {
                'formats': self._extract_m3u8_formats(
                    video['file'], display_id, 'mp4', m3u8_id='hls', fatal=False),
                **traverse_obj(video, {
                    'id': 'publicId',
                    'title': 'title',
                    'description': 'description',
                    'duration': ('durationInSeconds', {float_or_none}),
                    'thumbnail': ('image', {url_or_none}),
                }),
            }

    def _real_extract(self, url):
        playlist_id, trailers, video_id = self._match_valid_url(url).group('playlist', 'tr', 'id')
        playlist_id = join_nonempty(playlist_id, trailers)
        webpage = self._download_webpage(url, playlist_id)