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

Code before:
                for f in current_formats:
                    # TODO: Convert language to code
                    f.update({'language': fmt['lang'], 'format_note': fmt['uncut']})
                formats.extend(current_formats)
        self._remove_duplicate_formats(formats)
        self._sort_formats(formats)

        return {
            'id': compat_str(episode['episodePk']),
            'display_id': display_id,
            'duration': duration,
            'title': episode['episodeTitle'],
            'description': episode.get('episodeSummary'),
            'episode': episode.get('episodeTitle'),
            'episode_number': int_or_none(episode.get('episodeId')),
            'episode_id': episode.get('slug'),
            'season': season.get('seasonTitle'),
            'season_number': int_or_none(season.get('seasonId')),
            'season_id': compat_str(season.get('seasonPk')),
            'series': show.get('showTitle'),
            'formats': formats,
            'thumbnails': thumbnails,
            'subtitles': subtitles,
        }

    def _get_subtitles(self, url, video_id, display_id):
        player_url = urljoin(url, '/player/' + video_id)
        player_page = self._download_webpage(player_url, display_id)
        text_tracks_json_string = self._search_regex(
            r'"textTracks": (\[{.+?}\])',
            player_page, 'subtitles data', default='')
        text_tracks = self._parse_json(
            text_tracks_json_string, display_id, js_to_json, fatal=False) or []
        subtitles = {}
        for text_track in text_tracks:
            url_element = {'url': text_track.get('src')}
            language = text_track.get('language')
            if text_track.get('type') == 'CC':
                language += '_CC'
            subtitles.setdefault(language, []).append(url_element)
        return subtitles


class FunimationShowIE(FunimationIE):
    IE_NAME = 'funimation:show'
    _VALID_URL = r'(?P<url>https?://(?:www\.)?funimation(?:\.com|now\.uk)/(?P<locale>[^/]+)?/?shows/(?P<id>[^/?#&]+))/?(?:[?#]|$)'

    _TESTS = [{
        'url': 'https://www.funimation.com/en/shows/sk8-the-infinity',
        'info_dict': {
            'id': 1315000,

Review comment (suggestion):
Github won't let me leave a comment on line 222 (one below this), so leaving it here.
Line 222 gave me an error due to `video_id `being an int
`ERROR: can only concatenate str (not "int") to str`

Code after:
                subtitles, experience_id, display_id=display_id, format_name=format_name,
                episode=episode if experience_id == initial_experience_id else episode_id)

            headers = {}
            if self._TOKEN:
                headers['Authorization'] = 'Token %s' % self._TOKEN
            page = self._download_json(
                'https://www.funimation.com/api/showexperience/%s/' % experience_id,
                display_id, headers=headers, expected_status=403, query={
                    'pinst_id': ''.join([random.choice(string.digits + string.ascii_letters) for _ in range(8)]),
                }, note=f'Downloading {format_name} JSON')
            sources = page.get('items') or []
            if not sources:
                error = try_get(page, lambda x: x['errors'][0], dict)
                if error:
                    self.report_warning('%s said: Error %s - %s' % (
                        self.IE_NAME, error.get('code'), error.get('detail') or error.get('title')))
                else:
                    self.report_warning('No sources found for format')

            current_formats = []
            for source in sources:
                source_url = source.get('src')
                source_type = source.get('videoType') or determine_ext(source_url)
                if source_type == 'm3u8':
                    current_formats.extend(self._extract_m3u8_formats(
                        source_url, display_id, 'mp4', m3u8_id='%s-%s' % (experience_id, 'hls'), fatal=False,
                        note=f'Downloading {format_name} m3u8 information'))
                else:
                    current_formats.append({
                        'format_id': '%s-%s' % (experience_id, source_type),
                        'url': source_url,
                    })
                for f in current_formats:
                    # TODO: Convert language to code
                    f.update({'language': lang, 'format_note': version})
                formats.extend(current_formats)
        self._remove_duplicate_formats(formats)
        self._sort_formats(formats)

        return {
            'id': initial_experience_id if only_initial_experience else episode_id,
            'display_id': display_id,
            'duration': duration,
            'title': episode['episodeTitle'],
            'description': episode.get('episodeSummary'),
            'episode': episode.get('episodeTitle'),
            'episode_number': int_or_none(episode.get('episodeId')),
            'episode_id': episode_id,
            'season': season.get('seasonTitle'),
            'season_number': int_or_none(season.get('seasonId')),