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

Code before:
                    'email': username,
                    'password': password
                }).encode('utf-8'),
                headers={
                    'Content-Type': 'application/json'
                })['token']

            self.cache.store(self._NETRC_MACHINE, username, IwaraBaseIE._USERTOKEN)

        return self._USERTOKEN

    def _get_media_token(self, invalidate=False):
        if not invalidate and self._MEDIATOKEN:
            return self._MEDIATOKEN

        try:
            IwaraBaseIE._MEDIATOKEN = self._download_json(
                'https://api.iwara.tv/user/token', None, note='Fetching media token',
                data=b'',  # Need to have some data here, even if it's empty
                headers={
                    'Authorization': f'Bearer {self._get_user_token()}',
                    'Content-Type': 'application/json'
                })['accessToken']
        except ExtractorError as e:
            if isinstance(e.cause, urllib.error.HTTPError) and e.cause.code == 403:
                self.write_debug('User Token Expired')
        return self._MEDIATOKEN


class IwaraIE(IwaraBaseIE):
    IE_NAME = 'iwara'
    _VALID_URL = r'https?://(?:www\.|ecchi\.)?iwara\.tv/videos?/(?P<id>[a-zA-Z0-9]+)'
    _TESTS = [{
        # this video cannot be played because of migration
        'only_matching': True,
        'url': 'https://www.iwara.tv/video/k2ayoueezfkx6gvq',
        'info_dict': {
            'id': 'k2ayoueezfkx6gvq',
            'ext': 'mp4',
            'age_limit': 18,
            'title': 'Defeat of Irybelda - アイリベルダの敗北',
            'description': 'md5:70278abebe706647a8b4cb04cf23e0d3',
            'uploader': 'Inwerwm',
            'uploader_id': 'inwerwm',
            'tags': 'count:1',
            'like_count': 6133,
            'view_count': 1050343,
            'comment_count': 1,
            'timestamp': 1677843869,
            'modified_timestamp': 1679056362,
        },

Review comment (refactor):
make sure to `import urllib.error`
```suggestion
        except ExtractorError as e:
            if not isinstance(e.cause, urllib.error.HTTPError) and e.cause.code == 403:
                raise
            self.to_screen('User token has expired')
```

Code after:
        username, password = self._get_login_info()
        if not username or not password:
            return

        user_token = IwaraBaseIE._USERTOKEN or self.cache.load(self._NETRC_MACHINE, username)
        if not user_token or self._is_token_expired(user_token, 'User'):
            response = self._download_json(
                'https://api.iwara.tv/user/login', None, note='Logging in',
                headers={'Content-Type': 'application/json'}, data=json.dumps({
                    'email': username,
                    'password': password
                }).encode(), expected_status=lambda x: True)
            user_token = traverse_obj(response, ('token', {str}))
            if not user_token:
                error = traverse_obj(response, ('message', {str}))
                if 'invalidLogin' in error:
                    raise ExtractorError('Invalid login credentials', expected=True)
                else:
                    raise ExtractorError(f'Iwara API said: {error or "nothing"}')

            self.cache.store(self._NETRC_MACHINE, username, user_token)

        IwaraBaseIE._USERTOKEN = user_token

    def _get_media_token(self):
        self._get_user_token()
        if not IwaraBaseIE._USERTOKEN:
            return  # user has not passed credentials

        if not IwaraBaseIE._MEDIATOKEN or self._is_token_expired(IwaraBaseIE._MEDIATOKEN, 'Media'):
            IwaraBaseIE._MEDIATOKEN = self._download_json(
                'https://api.iwara.tv/user/token', None, note='Fetching media token',
                data=b'', headers={
                    'Authorization': f'Bearer {IwaraBaseIE._USERTOKEN}',
                    'Content-Type': 'application/json'
                })['accessToken']

        return {'Authorization': f'Bearer {IwaraBaseIE._MEDIATOKEN}'}

    def _perform_login(self, username, password):
        self._get_media_token()


class IwaraIE(IwaraBaseIE):
    IE_NAME = 'iwara'
    _VALID_URL = r'https?://(?:www\.|ecchi\.)?iwara\.tv/videos?/(?P<id>[a-zA-Z0-9]+)'
    _TESTS = [{
        'url': 'https://www.iwara.tv/video/k2ayoueezfkx6gvq',
        'info_dict': {
            'id': 'k2ayoueezfkx6gvq',
            'ext': 'mp4',