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

Code before:

    def _get_user_token(self, invalidate=False):
        if not invalidate and self._USERTOKEN:
            return self._USERTOKEN

        username, password = self._get_login_info()
        IwaraBaseIE._USERTOKEN = username and self.cache.load(self._NETRC_MACHINE, username)
        if not IwaraBaseIE._USERTOKEN or invalidate:
            IwaraBaseIE._USERTOKEN = self._download_json(
                'https://api.iwara.tv/user/login', None, note='Logging in',
                data=json.dumps({
                    '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,

Review comment (suggestion):
```suggestion
        if not invalidate and self._MEDIATOKEN:
            return self._MEDIATOKEN
        elif invalidate:
            IwaraBaseIE._MEDIATOKEN = None
```
or else an expired(?) token will still be returned if the exception below is caught

Code after:
    _NETRC_MACHINE = 'iwara'
    _USERTOKEN = None
    _MEDIATOKEN = None

    def _is_token_expired(self, token, token_type):
        # User token TTL == ~3 weeks, Media token TTL == ~1 hour
        if (try_call(lambda: jwt_decode_hs256(token)['exp']) or 0) <= int(time.time() - 120):
            self.to_screen(f'{token_type} token has expired')
            return True

    def _get_user_token(self):
        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):