mirror of
https://github.com/nirvana-7777/script.service.ultimate.git
synced 2026-09-18 15:12:12 +02:00
Fix taa_client
This commit is contained in:
@@ -234,7 +234,7 @@ class SessionManager:
|
||||
def load_scoped_token(self, provider: str, scope: str,
|
||||
country: Optional[str] = None) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Load token data for a specific scope
|
||||
Load token data for a specific scope (ENHANCED VERSION)
|
||||
|
||||
Args:
|
||||
provider: Provider name
|
||||
@@ -243,6 +243,11 @@ class SessionManager:
|
||||
|
||||
Returns:
|
||||
Token data dictionary or None
|
||||
|
||||
Enhanced:
|
||||
- Returns token even if access_token expired (for refresh attempts)
|
||||
- Properly handles yo_digital tokens with separate expiry times
|
||||
- Logs detailed expiration status
|
||||
"""
|
||||
country_str = f" (country: {country})" if country else ""
|
||||
|
||||
@@ -266,10 +271,28 @@ class SessionManager:
|
||||
logger.warning(f"Scope '{scope}' exists but doesn't contain valid token data")
|
||||
return None
|
||||
|
||||
# Check if token is expired
|
||||
if self._is_token_expired(token_data):
|
||||
# Check access token expiration
|
||||
access_token_expired = self._is_token_expired(token_data)
|
||||
|
||||
# For yo_digital, also check refresh token
|
||||
if scope == 'yo_digital' and 'refresh_token' in token_data:
|
||||
refresh_token_expired = self._is_refresh_token_expired(token_data)
|
||||
|
||||
if access_token_expired and not refresh_token_expired:
|
||||
logger.info(f"Access token expired for scope '{scope}' but refresh token still valid")
|
||||
return token_data # Return so caller can attempt refresh
|
||||
elif access_token_expired and refresh_token_expired:
|
||||
logger.warning(f"Both access and refresh tokens expired for scope '{scope}'")
|
||||
return None # Both expired, no point returning
|
||||
else:
|
||||
logger.info(f"Loaded valid token for {provider}{country_str}/{scope}")
|
||||
return token_data
|
||||
|
||||
# Standard token handling (tvhubs, taa, etc.)
|
||||
if access_token_expired:
|
||||
logger.info(f"Token for scope '{scope}' is expired")
|
||||
return token_data # Return anyway so refresh can be attempted
|
||||
# Still return it - caller might want to attempt refresh or re-auth
|
||||
return token_data
|
||||
|
||||
logger.info(f"Loaded valid token for {provider}{country_str}/{scope}")
|
||||
return token_data
|
||||
@@ -279,22 +302,103 @@ class SessionManager:
|
||||
"""
|
||||
Check if token is expired with buffer
|
||||
|
||||
Enhanced to support multiple expiration formats:
|
||||
1. Standard format: expires_in + issued_at (for access_token)
|
||||
2. yo_digital format: separate expiry for access_token and refresh_token
|
||||
|
||||
Args:
|
||||
token_data: Token data dictionary
|
||||
buffer_seconds: Seconds buffer before expiry (default 5 minutes)
|
||||
|
||||
Returns:
|
||||
True if expired, False otherwise
|
||||
|
||||
Note:
|
||||
For yo_digital tokens with both access_token and refresh_token,
|
||||
this checks the access_token expiration only (not refresh_token).
|
||||
"""
|
||||
if 'expires_in' not in token_data or 'issued_at' not in token_data:
|
||||
return False # Can't determine, assume valid
|
||||
|
||||
expires_in = token_data.get('expires_in', 0)
|
||||
issued_at = token_data.get('issued_at', 0)
|
||||
current_time = time.time()
|
||||
expires_at = issued_at + expires_in
|
||||
|
||||
return current_time >= (expires_at - buffer_seconds)
|
||||
# Format 1: Standard single token expiration
|
||||
# Used by: tvhubs tokens, taa tokens, SAM3 tokens
|
||||
if 'expires_in' in token_data and 'issued_at' in token_data:
|
||||
expires_in = token_data.get('expires_in', 0)
|
||||
issued_at = token_data.get('issued_at', 0)
|
||||
expires_at = issued_at + expires_in
|
||||
|
||||
is_expired = current_time >= (expires_at - buffer_seconds)
|
||||
|
||||
if is_expired:
|
||||
logger.debug(f"Token expired (standard format): "
|
||||
f"issued_at={issued_at}, expires_in={expires_in}, "
|
||||
f"expires_at={expires_at}, current={current_time}")
|
||||
|
||||
return is_expired
|
||||
|
||||
# Format 2: yo_digital separate expiration for access_token
|
||||
# yo_digital tokens have separate expiry for access and refresh tokens
|
||||
if 'access_token_expires_in' in token_data and 'access_token_issued_at' in token_data:
|
||||
expires_in = token_data.get('access_token_expires_in', 0)
|
||||
issued_at = token_data.get('access_token_issued_at', 0)
|
||||
expires_at = issued_at + expires_in
|
||||
|
||||
is_expired = current_time >= (expires_at - buffer_seconds)
|
||||
|
||||
if is_expired:
|
||||
logger.debug(f"Access token expired (yo_digital format): "
|
||||
f"issued_at={issued_at}, expires_in={expires_in}, "
|
||||
f"expires_at={expires_at}, current={current_time}")
|
||||
|
||||
return is_expired
|
||||
|
||||
# Format 3: Direct expiration timestamp (if some API returns 'exp' or 'expires_at')
|
||||
if 'expires_at' in token_data:
|
||||
expires_at = token_data.get('expires_at', 0)
|
||||
is_expired = current_time >= (expires_at - buffer_seconds)
|
||||
|
||||
if is_expired:
|
||||
logger.debug(f"Token expired (direct timestamp): "
|
||||
f"expires_at={expires_at}, current={current_time}")
|
||||
|
||||
return is_expired
|
||||
|
||||
# No expiration info available - assume valid
|
||||
logger.debug("No expiration info in token data - assuming valid")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _is_refresh_token_expired(token_data: Dict[str, Any], buffer_seconds: int = 300) -> bool:
|
||||
"""
|
||||
Check if refresh token is expired (for yo_digital tokens)
|
||||
|
||||
This is a separate check specifically for yo_digital refresh tokens
|
||||
which have their own expiration separate from access tokens.
|
||||
|
||||
Args:
|
||||
token_data: Token data dictionary
|
||||
buffer_seconds: Seconds buffer before expiry (default 5 minutes)
|
||||
|
||||
Returns:
|
||||
True if refresh token expired, False otherwise or if no refresh token
|
||||
"""
|
||||
# yo_digital format with separate refresh token expiry
|
||||
if 'refresh_token_expires_in' in token_data and 'refresh_token_issued_at' in token_data:
|
||||
current_time = time.time()
|
||||
expires_in = token_data.get('refresh_token_expires_in', 0)
|
||||
issued_at = token_data.get('refresh_token_issued_at', 0)
|
||||
expires_at = issued_at + expires_in
|
||||
|
||||
is_expired = current_time >= (expires_at - buffer_seconds)
|
||||
|
||||
if is_expired:
|
||||
logger.debug(f"Refresh token expired (yo_digital format): "
|
||||
f"issued_at={issued_at}, expires_in={expires_in}, "
|
||||
f"expires_at={expires_at}, current={current_time}")
|
||||
|
||||
return is_expired
|
||||
|
||||
# No refresh token or no expiration info
|
||||
return False
|
||||
|
||||
def save_token(self, provider: str, token: BaseAuthToken,
|
||||
country: Optional[str] = None) -> bool:
|
||||
|
||||
@@ -547,6 +547,16 @@ class SettingsManager:
|
||||
all_countries = self.session_manager.get_all_countries(provider_name)
|
||||
status['configured_countries'] = all_countries
|
||||
|
||||
scoped_tokens = {}
|
||||
available_scopes = self.list_scoped_tokens(provider_name, country)
|
||||
|
||||
for scope in available_scopes:
|
||||
scope_status = self.get_scoped_token_status(provider_name, scope, country)
|
||||
scoped_tokens[scope] = scope_status
|
||||
|
||||
if scoped_tokens:
|
||||
status['scoped_tokens'] = scoped_tokens
|
||||
|
||||
return status
|
||||
|
||||
def is_provider_ready(self, provider_name: str, country: Optional[str] = None) -> bool:
|
||||
@@ -1122,6 +1132,189 @@ class SettingsManager:
|
||||
logger.error(f"Error migrating {provider_name} to country structure: {e}")
|
||||
return False
|
||||
|
||||
# ============= Scoped Token Management (Pass-through to SessionManager) =============
|
||||
|
||||
def save_scoped_token(self, provider_name: str, scope: str, token_data: Dict[str, Any],
|
||||
country: Optional[str] = None) -> bool:
|
||||
"""
|
||||
Save authentication token for a specific scope
|
||||
|
||||
Pass-through method to SessionManager.save_scoped_token()
|
||||
|
||||
Args:
|
||||
provider_name: Name of the provider
|
||||
scope: Token scope (e.g., 'tvhubs', 'taa', 'yo_digital')
|
||||
token_data: Token data to save (should include access_token, expires_in, etc.)
|
||||
country: Optional country code
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
|
||||
Examples:
|
||||
# Save tvhubs token (access token only)
|
||||
settings_manager.save_scoped_token('magenta2', 'tvhubs', {
|
||||
'access_token': '...',
|
||||
'token_type': 'Bearer',
|
||||
'expires_in': 7200,
|
||||
'issued_at': 1234567890
|
||||
}, 'de')
|
||||
|
||||
# Save yo_digital token (access + refresh, each with own expiry)
|
||||
settings_manager.save_scoped_token('magenta2', 'yo_digital', {
|
||||
'access_token': '...',
|
||||
'access_token_expires_in': 3600,
|
||||
'access_token_issued_at': 1234567890,
|
||||
'refresh_token': '...',
|
||||
'refresh_token_expires_in': 86400,
|
||||
'refresh_token_issued_at': 1234567890,
|
||||
'token_type': 'Bearer'
|
||||
}, 'de')
|
||||
"""
|
||||
return self.session_manager.save_scoped_token(provider_name, scope, token_data, country)
|
||||
|
||||
def load_scoped_token(self, provider_name: str, scope: str,
|
||||
country: Optional[str] = None) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Load token data for a specific scope
|
||||
|
||||
Pass-through method to SessionManager.load_scoped_token()
|
||||
|
||||
Args:
|
||||
provider_name: Name of the provider
|
||||
scope: Token scope (e.g., 'tvhubs', 'taa', 'yo_digital')
|
||||
country: Optional country code
|
||||
|
||||
Returns:
|
||||
Token data dictionary or None if not found or expired
|
||||
|
||||
Note:
|
||||
This method checks token expiration automatically.
|
||||
For yo_digital tokens, it checks both access_token and refresh_token expiry.
|
||||
"""
|
||||
return self.session_manager.load_scoped_token(provider_name, scope, country)
|
||||
|
||||
def clear_scoped_token(self, provider_name: str, scope: str,
|
||||
country: Optional[str] = None) -> bool:
|
||||
"""
|
||||
Clear token for a specific scope
|
||||
|
||||
Pass-through method to SessionManager.clear_scoped_token()
|
||||
|
||||
Args:
|
||||
provider_name: Name of the provider
|
||||
scope: Token scope to clear
|
||||
country: Optional country code
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
|
||||
Example:
|
||||
# Clear only the tvhubs token, keep taa and yo_digital
|
||||
settings_manager.clear_scoped_token('magenta2', 'tvhubs', 'de')
|
||||
"""
|
||||
return self.session_manager.clear_scoped_token(provider_name, scope, country)
|
||||
|
||||
def list_scoped_tokens(self, provider_name: str, country: Optional[str] = None) -> List[str]:
|
||||
"""
|
||||
List all available token scopes for a provider
|
||||
|
||||
Args:
|
||||
provider_name: Name of the provider
|
||||
country: Optional country code
|
||||
|
||||
Returns:
|
||||
List of scope names (e.g., ['tvhubs', 'taa', 'yo_digital'])
|
||||
|
||||
Example:
|
||||
scopes = settings_manager.list_scoped_tokens('magenta2', 'de')
|
||||
# Returns: ['tvhubs', 'taa', 'yo_digital']
|
||||
"""
|
||||
session_data = self.session_manager.load_session(provider_name, country)
|
||||
if not session_data:
|
||||
return []
|
||||
|
||||
# Filter out non-scope keys (device_id, refresh_token at provider level)
|
||||
non_scope_keys = {'device_id', 'refresh_token'}
|
||||
scopes = [
|
||||
key for key in session_data.keys()
|
||||
if isinstance(session_data[key], dict) and key not in non_scope_keys
|
||||
]
|
||||
|
||||
return scopes
|
||||
|
||||
def get_scoped_token_status(self, provider_name: str, scope: str,
|
||||
country: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
Get detailed status information for a scoped token
|
||||
|
||||
Args:
|
||||
provider_name: Name of the provider
|
||||
scope: Token scope
|
||||
country: Optional country code
|
||||
|
||||
Returns:
|
||||
Dictionary with token status information
|
||||
|
||||
Example:
|
||||
status = settings_manager.get_scoped_token_status('magenta2', 'yo_digital', 'de')
|
||||
# Returns:
|
||||
# {
|
||||
# 'exists': True,
|
||||
# 'access_token_valid': True,
|
||||
# 'access_token_expires_at': 1234567890,
|
||||
# 'refresh_token_valid': True,
|
||||
# 'refresh_token_expires_at': 1234654290,
|
||||
# 'scope': 'yo_digital'
|
||||
# }
|
||||
"""
|
||||
token_data = self.load_scoped_token(provider_name, scope, country)
|
||||
|
||||
if not token_data:
|
||||
return {
|
||||
'exists': False,
|
||||
'scope': scope,
|
||||
'provider_name': provider_name,
|
||||
'country': country
|
||||
}
|
||||
|
||||
status = {
|
||||
'exists': True,
|
||||
'scope': scope,
|
||||
'provider_name': provider_name,
|
||||
'country': country,
|
||||
'token_type': token_data.get('token_type', 'Bearer')
|
||||
}
|
||||
|
||||
# Check access token expiration
|
||||
if 'access_token' in token_data:
|
||||
status['has_access_token'] = True
|
||||
|
||||
# Standard expiration (single expires_in and issued_at)
|
||||
if 'expires_in' in token_data and 'issued_at' in token_data:
|
||||
expires_at = token_data['issued_at'] + token_data['expires_in']
|
||||
status['access_token_expires_at'] = expires_at
|
||||
status['access_token_valid'] = time.time() < expires_at
|
||||
|
||||
# yo_digital style (separate access_token_expires_in)
|
||||
elif 'access_token_expires_in' in token_data and 'access_token_issued_at' in token_data:
|
||||
expires_at = token_data['access_token_issued_at'] + token_data['access_token_expires_in']
|
||||
status['access_token_expires_at'] = expires_at
|
||||
status['access_token_valid'] = time.time() < expires_at
|
||||
else:
|
||||
status['access_token_valid'] = None # Cannot determine
|
||||
|
||||
# Check refresh token expiration (for yo_digital)
|
||||
if 'refresh_token' in token_data:
|
||||
status['has_refresh_token'] = True
|
||||
|
||||
if 'refresh_token_expires_in' in token_data and 'refresh_token_issued_at' in token_data:
|
||||
expires_at = token_data['refresh_token_issued_at'] + token_data['refresh_token_expires_in']
|
||||
status['refresh_token_expires_at'] = expires_at
|
||||
status['refresh_token_valid'] = time.time() < expires_at
|
||||
else:
|
||||
status['refresh_token_valid'] = None # Cannot determine
|
||||
|
||||
return status
|
||||
|
||||
# For imports that expect the old interface
|
||||
UnifiedSettingsManager = SettingsManager # Backward compatibility alias
|
||||
|
||||
Reference in New Issue
Block a user