yt-dlp/yt-dlp · error · ExtractorError

This livestream has ended

Error message

This livestream has ended

What it means

Soop live extractor: the access-token API (player_live_api.php with bno/stream_type/type/quality/pwd) returned CHANNEL without an AID and RESULT == 0 -- the broadcast number is valid but the stream is over. Expected error; the usual follow-up is a replay VOD that appears later.

Source

Thrown at yt_dlp/extractor/afreecatv.py:399

        if channel_info.get('BPWD') == 'Y' and password is None:
            raise ExtractorError(
                'This livestream is protected by a password, use the --video-password option',
                expected=True)

        token_info = traverse_obj(self._download_json(
            self._LIVE_API_URL, broadcast_no, 'Downloading access token for stream',
            'Unable to download access token for stream', data=urlencode_postdata(filter_dict({
                'bno': broadcast_no,
                'stream_type': 'common',
                'type': 'aid',
                'quality': 'master',
                'pwd': password,
            }))), ('CHANNEL', {dict})) or {}
        aid = token_info.get('AID')
        if not aid:
            result = token_info.get('RESULT')
            if result == 0:
                raise ExtractorError('This livestream has ended', expected=True)
            elif result == -6:
                self.raise_login_required('This livestream is for subscribers only', method='password')
            raise ExtractorError('Unable to extract access token')

        formats = self._extract_formats(channel_info, broadcast_no, aid)

        station_info = traverse_obj(self._download_json(
            'https://st.sooplive.com/api/get_station_status.php', broadcast_no,
            'Downloading channel metadata', 'Unable to download channel metadata',
            query={'szBjId': broadcaster_id}, fatal=False), {dict}) or {}

        return {
            'id': broadcast_no,
            'title': channel_info.get('TITLE') or station_info.get('station_title'),
            'uploader': channel_info.get('BJNICK') or station_info.get('station_name'),
            'uploader_id': broadcaster_id,
            'timestamp': parse_iso8601(station_info.get('broad_start'), delimiter=' ', timezone=dt.timedelta(hours=9)),
            'formats': formats,

View on GitHub (pinned to 81ecd58b13)

Solutions

  1. Re-check the channel -- if the broadcaster restarted, a new BNO applies (re-run the original channel URL)
  2. Wait for the replay VOD to be published on vod.sooplive.com and extract that instead
  3. Treat as terminal for live extraction; do not retry the same BNO
Defensive patterns

Strategy: fallback

Try / catch

from yt_dlp import YoutubeDL
from yt_dlp.utils import DownloadError

try:
    info = YoutubeDL().extract_info(live_url)          # play.sooplive.com/<bid>
except DownloadError as e:
    if 'This livestream has ended' in str(e):
        # fall back: replay VODs surface on vod.sooplive.com; re-check later
        info = try_replay_vod(broadcaster_id, delay_hours=1)
    else:
        raise

Prevention

When it happens

Trigger: Joining a sooplive livestream in the minutes after it ended: the channel step still resolved a BNO, but the token step no longer issues an AID for it.

Common situations: Race at stream end; polling scripts hammering an ended broadcast; replays not yet uploaded so nothing to fall back to.

Related errors


AI-assisted analysis of yt-dlp/yt-dlp@81ecd58b13 (2026-08-22). Data as JSON: /api/errors/b4fc47fe64444313. Report an issue: GitHub.