Files
script.service.ultimate/lib/streaming_providers/base/auth/credential_manager.py
T

697 lines
28 KiB
Python
Raw Normal View History

2025-10-29 20:23:50 +01:00
# streaming_providers/base/auth/credential_manager.py
import time
2026-01-06 16:41:03 +01:00
from typing import Any, Dict, List, Optional
2025-10-29 20:23:50 +01:00
# Import centralized logger and VFS
from ..utils.logger import logger
2026-01-16 12:24:41 +01:00
from .credentials import BaseCredentials, ClientCredentials, UserPasswordCredentials
2025-10-29 20:23:50 +01:00
class CredentialManager:
"""
Manages loading and saving credentials from/to persistent storage
Now supports country-specific credentials
"""
def __init__(self, config_dir: Optional[str] = None):
# Initialize VFS with config directory support
from ..utils.vfs import VFS
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
self.vfs = VFS(config_dir=config_dir)
# Credentials file is always in the root of the VFS base path
2026-01-06 16:41:03 +01:00
self.credentials_file = "credentials.json"
2025-10-29 20:23:50 +01:00
# Ensure base directory exists
2026-01-06 16:41:03 +01:00
self.vfs.mkdirs("")
2026-01-16 12:24:41 +01:00
logger.debug(f"CredentialManager initialized with VFS base: {self.vfs.base_path}")
2025-10-29 20:23:50 +01:00
@staticmethod
def _get_credential_path(provider: str, country: Optional[str] = None) -> tuple:
"""
Determine the path to credential data based on country
Args:
provider: Provider name
country: Optional country code (e.g., 'de', 'at', 'ch')
Returns:
Tuple of (keys_path, is_nested) where keys_path is list of keys to navigate
"""
if country:
return [provider, country], True
else:
return [provider], False
2026-01-06 16:41:03 +01:00
def load_credentials(
self, provider: str, country: Optional[str] = None
) -> Optional[BaseCredentials]:
2025-10-29 20:23:50 +01:00
"""
2025-12-20 19:20:41 +01:00
Load credentials for a specific provider and optional country.
Now with fallback: tries with country first, then without country
for backward compatibility with credentials stored without country nesting.
2025-10-29 20:23:50 +01:00
Args:
provider: Provider name (e.g., 'rtlplus', 'joyn')
country: Optional country code (e.g., 'de', 'at', 'ch')
Returns:
BaseCredentials instance or None if not found
"""
country_str = f" (country: {country})" if country else ""
2025-12-20 19:20:41 +01:00
2025-10-29 20:23:50 +01:00
try:
2026-01-16 12:24:41 +01:00
logger.debug(f"CredentialManager: Loading credentials for '{provider}{country_str}'")
2025-10-29 20:23:50 +01:00
# Check if credentials file exists
if not self.vfs.exists(self.credentials_file):
2026-01-06 16:41:03 +01:00
logger.debug(
f"CredentialManager: Credentials file does not exist: {self.credentials_file}"
)
2025-10-29 20:23:50 +01:00
return None
2026-01-16 12:24:41 +01:00
logger.debug(f"CredentialManager: Reading credentials file: {self.credentials_file}")
2025-10-29 20:23:50 +01:00
data = self.vfs.read_json(self.credentials_file)
if not data:
2026-01-16 12:24:41 +01:00
logger.debug(f"CredentialManager: Credentials file is empty or invalid JSON")
2025-10-29 20:23:50 +01:00
return None
2026-01-16 12:24:41 +01:00
logger.debug(f"CredentialManager: Available providers in file: {list(data.keys())}")
2025-10-29 20:23:50 +01:00
2025-12-20 19:20:41 +01:00
# Try with country first (new format: {"provider": {"country": {...}}})
if country:
keys_path, _ = self._get_credential_path(provider, country)
provider_data = data
found = True
for key in keys_path:
if not isinstance(provider_data, dict) or key not in provider_data:
found = False
break
provider_data = provider_data[key]
if found and provider_data:
logger.debug(
2026-01-06 16:41:03 +01:00
f"CredentialManager: Found credentials with country nesting for '{provider}{country_str}'"
)
2026-01-16 12:24:41 +01:00
return self._create_credential_from_data(provider, country, provider_data)
2025-12-20 19:20:41 +01:00
# Fallback: try without country (legacy format: {"provider": {...}})
2026-01-06 16:41:03 +01:00
logger.debug(
f"CredentialManager: No country-nested credentials found, trying legacy format"
)
2025-12-20 19:20:41 +01:00
keys_path, _ = self._get_credential_path(provider, None)
provider_data = data
for key in keys_path:
if not isinstance(provider_data, dict) or key not in provider_data:
2026-01-06 16:41:03 +01:00
logger.debug(
f"CredentialManager: No credentials found at path: {' -> '.join(keys_path)}"
)
2025-12-20 19:20:41 +01:00
return None
provider_data = provider_data[key]
if provider_data:
2026-01-06 16:41:03 +01:00
logger.debug(
f"CredentialManager: Found credentials in legacy format for '{provider}{country_str}'"
)
2026-01-16 12:24:41 +01:00
return self._create_credential_from_data(provider, country, provider_data)
2025-12-20 19:20:41 +01:00
return None
# No country specified - direct lookup
keys_path, _ = self._get_credential_path(provider, None)
2025-10-29 20:23:50 +01:00
provider_data = data
for key in keys_path:
if not isinstance(provider_data, dict) or key not in provider_data:
2026-01-06 16:41:03 +01:00
logger.debug(
f"CredentialManager: No data found at path: {' -> '.join(keys_path)}"
)
2025-10-29 20:23:50 +01:00
return None
provider_data = provider_data[key]
if not provider_data:
2026-01-16 12:24:41 +01:00
logger.debug(f"CredentialManager: No data found for provider '{provider}'")
2025-10-29 20:23:50 +01:00
return None
2025-12-20 19:20:41 +01:00
return self._create_credential_from_data(provider, None, provider_data)
except Exception as e:
2026-01-06 16:41:03 +01:00
logger.error(
f"CredentialManager: Error loading credentials for '{provider}{country_str}': {e}"
)
2025-12-20 19:20:41 +01:00
return None
2026-01-06 16:41:03 +01:00
def _create_credential_from_data(
self, provider: str, country: Optional[str], provider_data: dict
) -> Optional[BaseCredentials]:
2025-12-20 19:20:41 +01:00
"""
Helper method to create credential object from loaded data.
Args:
provider: Provider name
country: Optional country code (for logging)
provider_data: Dictionary containing credential data
2025-10-29 20:23:50 +01:00
2025-12-20 19:20:41 +01:00
Returns:
BaseCredentials instance or None
"""
country_str = f" (country: {country})" if country else ""
2025-10-29 20:23:50 +01:00
2026-01-06 16:41:03 +01:00
logger.debug(
f"CredentialManager: Found data for '{provider}{country_str}': {list(provider_data.keys())}"
)
2025-10-29 20:23:50 +01:00
2026-01-06 16:41:03 +01:00
credential_type = provider_data.get("type")
logger.debug(
f"CredentialManager: Credential type for '{provider}{country_str}': {credential_type}"
)
2025-10-29 20:23:50 +01:00
2026-01-06 16:41:03 +01:00
if credential_type == "user_password":
username = provider_data.get("username", "")
password_present = "password" in provider_data
client_id = provider_data.get("client_id")
2025-10-29 20:23:50 +01:00
2025-12-20 19:20:41 +01:00
logger.debug(
2026-01-06 16:41:03 +01:00
f"CredentialManager: Creating UserPasswordCredentials for '{provider}{country_str}'"
)
logger.debug(
f"CredentialManager: Username: '{username}', Password present: {password_present}, Client ID: {client_id}"
)
2025-10-29 20:23:50 +01:00
2025-12-20 19:20:41 +01:00
creds = UserPasswordCredentials(
username=username,
2026-01-06 16:41:03 +01:00
password=self._decode_password(provider_data.get("password", "")),
2025-12-20 19:20:41 +01:00
client_id=client_id,
2026-01-06 16:41:03 +01:00
grant_type=provider_data.get("grant_type", "password"),
2025-12-20 19:20:41 +01:00
)
2025-10-29 20:23:50 +01:00
2025-12-20 19:20:41 +01:00
logger.debug(
2026-01-06 16:41:03 +01:00
f"CredentialManager: Successfully created UserPasswordCredentials for '{provider}{country_str}'"
)
2025-12-20 19:20:41 +01:00
return creds
2025-10-29 20:23:50 +01:00
2026-01-06 16:41:03 +01:00
elif credential_type == "client_credentials":
client_id = provider_data.get("client_id", "")
client_secret_present = "client_secret" in provider_data
2025-10-29 20:23:50 +01:00
2025-12-20 19:20:41 +01:00
logger.debug(
2026-01-06 16:41:03 +01:00
f"CredentialManager: Creating ClientCredentials for '{provider}{country_str}'"
)
logger.debug(
f"CredentialManager: Client ID: '{client_id}', Client secret present: {client_secret_present}"
)
2025-10-29 20:23:50 +01:00
2025-12-20 19:20:41 +01:00
creds = ClientCredentials(
client_id=client_id,
2026-01-16 12:24:41 +01:00
client_secret=self._decode_password(provider_data.get("client_secret", "")),
2026-01-06 16:41:03 +01:00
grant_type=provider_data.get("grant_type", "client_credentials"),
2025-12-20 19:20:41 +01:00
)
2025-10-29 20:23:50 +01:00
2026-01-06 16:41:03 +01:00
logger.debug(
f"CredentialManager: Successfully created ClientCredentials for '{provider}{country_str}'"
)
2025-12-20 19:20:41 +01:00
return creds
else:
logger.error(
2026-01-06 16:41:03 +01:00
f"CredentialManager: Unknown credential type for '{provider}{country_str}': {credential_type}"
)
2025-10-29 20:23:50 +01:00
return None
2026-01-06 16:41:03 +01:00
def save_credentials(
self, provider: str, credentials: BaseCredentials, country: Optional[str] = None
) -> bool:
2025-10-29 20:23:50 +01:00
"""
Save credentials for a specific provider and optional country
Args:
provider: Provider name
credentials: Credentials to save
country: Optional country code
Returns:
True if successful, False otherwise
"""
country_str = f" (country: {country})" if country else ""
try:
2026-01-06 16:41:03 +01:00
logger.debug(
f"Saving {credentials.credential_type} credentials for {provider}{country_str}"
)
2025-10-29 20:23:50 +01:00
# Load existing data
data = {}
if self.vfs.exists(self.credentials_file):
data = self.vfs.read_json(self.credentials_file) or {}
# Prepare provider data based on credential type
if isinstance(credentials, UserPasswordCredentials):
provider_data = {
2026-01-06 16:41:03 +01:00
"type": "user_password",
"username": credentials.username,
"password": self._encode_password(credentials.password),
"grant_type": credentials.grant_type,
2025-10-29 20:23:50 +01:00
}
if credentials.client_id:
2026-01-06 16:41:03 +01:00
provider_data["client_id"] = credentials.client_id
2025-10-29 20:23:50 +01:00
elif isinstance(credentials, ClientCredentials):
provider_data = {
2026-01-06 16:41:03 +01:00
"type": "client_credentials",
"client_id": credentials.client_id,
"client_secret": self._encode_password(credentials.client_secret),
"grant_type": credentials.grant_type,
2025-10-29 20:23:50 +01:00
}
else:
logger.error(f"Unsupported credential type: {type(credentials)}")
return False
# Navigate and create nested structure if needed
keys_path, is_nested = self._get_credential_path(provider, country)
# Build nested structure
current = data
for i, key in enumerate(keys_path[:-1]):
if key not in current:
current[key] = {}
elif not isinstance(current[key], dict):
logger.warning(f"Overwriting non-dict value at {key}")
current[key] = {}
current = current[key]
# Set the final value
current[keys_path[-1]] = provider_data
# Save to file using VFS
success = self.vfs.write_json(self.credentials_file, data)
if success:
2026-01-16 12:24:41 +01:00
logger.info(f"Successfully saved credentials for {provider}{country_str}")
2025-10-29 20:23:50 +01:00
# Verify by reading back
verify_data = self.vfs.read_json(self.credentials_file)
if verify_data:
verify_current = verify_data
found = True
for key in keys_path:
2026-01-16 12:24:41 +01:00
if not isinstance(verify_current, dict) or key not in verify_current:
2025-10-29 20:23:50 +01:00
found = False
break
verify_current = verify_current[key]
if found:
2026-01-06 16:41:03 +01:00
logger.debug(
f"Verification successful: {provider}{country_str} data found in saved file"
)
2025-10-29 20:23:50 +01:00
else:
2026-01-06 16:41:03 +01:00
logger.error(
f"Verification failed: {provider}{country_str} data NOT found in saved file"
)
2025-10-29 20:23:50 +01:00
return False
return True
else:
logger.error(f"Failed to save credentials for {provider}{country_str}")
return False
except Exception as e:
country_str = f" (country: {country})" if country else ""
logger.error(f"Error saving credentials for {provider}{country_str}: {e}")
return False
def delete_credentials(self, provider: str, country: Optional[str] = None) -> bool:
"""
Delete credentials for a specific provider and optional country
Args:
provider: Provider name
country: Optional country code (if None, deletes entire provider or all countries)
Returns:
True if successful, False otherwise
"""
country_str = f" (country: {country})" if country else ""
try:
if not self.vfs.exists(self.credentials_file):
2026-01-06 16:41:03 +01:00
logger.debug(
f"No credentials file exists, nothing to delete for {provider}{country_str}"
)
2025-10-29 20:23:50 +01:00
return True
data = self.vfs.read_json(self.credentials_file)
if not data:
2026-01-06 16:41:03 +01:00
logger.debug(
f"Credentials file is empty, nothing to delete for {provider}{country_str}"
)
2025-10-29 20:23:50 +01:00
return True
if country:
# Delete specific country data
2026-01-06 16:41:03 +01:00
if (
provider in data
and isinstance(data[provider], dict)
and country in data[provider]
):
2025-10-29 20:23:50 +01:00
del data[provider][country]
2026-01-16 12:24:41 +01:00
logger.info(f"Deleted stored credentials for {provider}{country_str}")
2025-10-29 20:23:50 +01:00
# If provider dict is now empty, remove it entirely
if not data[provider]:
del data[provider]
2026-01-16 12:24:41 +01:00
logger.debug(f"Provider {provider} had no more countries, removed entirely")
2025-10-29 20:23:50 +01:00
return self.vfs.write_json(self.credentials_file, data)
else:
2026-01-06 16:41:03 +01:00
logger.debug(
f"No stored credentials found to delete for {provider}{country_str}"
)
2025-10-29 20:23:50 +01:00
else:
# Delete entire provider data (all countries)
if provider in data:
del data[provider]
logger.info(f"Deleted all stored credentials for {provider}")
return self.vfs.write_json(self.credentials_file, data)
else:
2026-01-16 12:24:41 +01:00
logger.debug(f"No stored credentials found to delete for {provider}")
2025-10-29 20:23:50 +01:00
return True
except Exception as e:
country_str = f" (country: {country})" if country else ""
logger.error(f"Error deleting credentials for {provider}{country_str}: {e}")
return False
def list_providers(self) -> List[str]:
"""
List all providers with stored credentials
Returns:
List of provider names
"""
try:
if not self.vfs.exists(self.credentials_file):
2026-01-16 12:24:41 +01:00
logger.debug("No credentials file exists, returning empty provider list")
2025-10-29 20:23:50 +01:00
return []
data = self.vfs.read_json(self.credentials_file)
if not data:
logger.debug("Credentials file is empty, returning empty provider list")
return []
providers = list(data.keys())
logger.debug(f"Found stored credentials for providers: {providers}")
return providers
except Exception as e:
logger.error(f"Error listing providers: {e}")
return []
def get_all_countries(self, provider: str) -> List[str]:
"""
Get all countries that have credentials for a provider
Args:
provider: Provider name
Returns:
List of country codes
"""
try:
if not self.vfs.exists(self.credentials_file):
return []
data = self.vfs.read_json(self.credentials_file)
if not data or provider not in data:
return []
provider_data = data[provider]
# Check if this is a nested (country-aware) structure
if isinstance(provider_data, dict):
# Check if any keys look like country codes (2-3 char strings)
# and their values are dicts (credential data)
countries = []
for key, value in provider_data.items():
2026-01-06 16:41:03 +01:00
if isinstance(value, dict) and len(key) <= 3 and "type" in value:
2025-10-29 20:23:50 +01:00
countries.append(key)
return countries
return []
except Exception as e:
logger.error(f"Error getting countries for {provider}: {e}")
return []
def has_credentials(self, provider: str, country: Optional[str] = None) -> bool:
"""
Check if credentials exist for a provider and optional country
Args:
provider: Provider name
country: Optional country code
Returns:
True if credentials exist, False otherwise
"""
credentials = self.load_credentials(provider, country)
return credentials is not None and credentials.validate()
@staticmethod
def _encode_password(password: str) -> str:
"""No encoding - store as plaintext (TEMPORARY INSECURE SOLUTION)."""
2026-01-06 16:41:03 +01:00
logger.warning(
"Storing password in plaintext - this is insecure and should be replaced with proper encryption"
)
2025-10-29 20:23:50 +01:00
return password
@staticmethod
def _decode_password(encoded_password: str) -> str:
"""No decoding needed - passwords are stored in plaintext."""
return encoded_password
2026-01-06 16:41:03 +01:00
def export_config(
self, provider: Optional[str] = None, country: Optional[str] = None
) -> Dict[str, Any]:
2025-10-29 20:23:50 +01:00
"""
Export credentials to a portable format
Args:
provider: Optional provider name to export (if None, exports all)
country: Optional country code (only used if provider is specified)
Returns:
Dictionary with credentials data
"""
export_data = {
2026-01-06 16:41:03 +01:00
"version": "1.1", # Bumped for country support
"exported_at": time.time(),
"credentials": {},
2025-10-29 20:23:50 +01:00
}
if provider:
# Export specific provider
if country:
# Export specific country
credentials = self.load_credentials(provider, country)
if credentials:
2026-01-06 16:41:03 +01:00
export_data["credentials"][provider] = {
2025-10-29 20:23:50 +01:00
country: self._credential_to_dict(credentials)
}
else:
# Export all countries for provider or non-country data
countries = self.get_all_countries(provider)
if countries:
# Provider has country-specific credentials
provider_data = {}
for ctry in countries:
credentials = self.load_credentials(provider, ctry)
if credentials:
provider_data[ctry] = self._credential_to_dict(credentials)
if provider_data:
2026-01-06 16:41:03 +01:00
export_data["credentials"][provider] = provider_data
2025-10-29 20:23:50 +01:00
else:
# Non-country provider
credentials = self.load_credentials(provider)
if credentials:
2026-01-16 12:24:41 +01:00
export_data["credentials"][provider] = self._credential_to_dict(credentials)
2025-10-29 20:23:50 +01:00
else:
# Export all providers
for prov in self.list_providers():
countries = self.get_all_countries(prov)
if countries:
# Provider has country-specific credentials
provider_data = {}
for ctry in countries:
credentials = self.load_credentials(prov, ctry)
if credentials:
provider_data[ctry] = self._credential_to_dict(credentials)
if provider_data:
2026-01-06 16:41:03 +01:00
export_data["credentials"][prov] = provider_data
2025-10-29 20:23:50 +01:00
else:
# Non-country provider
credentials = self.load_credentials(prov)
if credentials:
2026-01-16 12:24:41 +01:00
export_data["credentials"][prov] = self._credential_to_dict(credentials)
2025-10-29 20:23:50 +01:00
return export_data
@staticmethod
def _credential_to_dict(credentials: BaseCredentials) -> Dict[str, Any]:
"""Convert credentials object to dictionary for export"""
if isinstance(credentials, UserPasswordCredentials):
return {
2026-01-06 16:41:03 +01:00
"type": "user_password",
"username": credentials.username,
"password": credentials.password,
"client_id": credentials.client_id,
2025-10-29 20:23:50 +01:00
}
elif isinstance(credentials, ClientCredentials):
return {
2026-01-06 16:41:03 +01:00
"type": "client_credentials",
"client_id": credentials.client_id,
"client_secret": credentials.client_secret,
2025-10-29 20:23:50 +01:00
}
else:
return {}
def import_config(self, config_data: Dict[str, Any]) -> Dict[str, bool]:
"""
Import credentials from exported configuration
Args:
config_data: Configuration data from export_config()
Returns:
Dictionary mapping provider names (or provider_country) to import success status
"""
results = {}
# Validate config data
2026-01-06 16:41:03 +01:00
if not isinstance(config_data, dict) or "credentials" not in config_data:
2025-10-29 20:23:50 +01:00
logger.error("Invalid credential config data format")
return results
2026-01-06 16:41:03 +01:00
credentials_data = config_data.get("credentials", {})
2025-10-29 20:23:50 +01:00
for provider, provider_data in credentials_data.items():
try:
# Check if this is country-aware structure
2026-01-06 16:41:03 +01:00
if isinstance(provider_data, dict) and not provider_data.get("type"):
2025-10-29 20:23:50 +01:00
# Nested structure with countries
for country, cred_data in provider_data.items():
if not isinstance(cred_data, dict):
continue
credentials = self._dict_to_credential(cred_data)
if credentials:
2026-01-16 12:24:41 +01:00
success = self.save_credentials(provider, credentials, country)
2025-10-29 20:23:50 +01:00
results[f"{provider}_{country}"] = success
if success:
2026-01-06 16:41:03 +01:00
logger.info(
f"Successfully imported credentials for {provider} ({country})"
)
2025-10-29 20:23:50 +01:00
else:
2026-01-06 16:41:03 +01:00
logger.error(
f"Failed to import credentials for {provider} ({country})"
)
2025-10-29 20:23:50 +01:00
else:
# Flat structure (no country)
credentials = self._dict_to_credential(provider_data)
if credentials:
success = self.save_credentials(provider, credentials)
results[provider] = success
if success:
2026-01-16 12:24:41 +01:00
logger.info(f"Successfully imported credentials for {provider}")
2025-10-29 20:23:50 +01:00
else:
logger.error(f"Failed to import credentials for {provider}")
except Exception as e:
logger.error(f"Error importing credentials for {provider}: {e}")
results[provider] = False
return results
@staticmethod
def _dict_to_credential(cred_data: Dict[str, Any]) -> Optional[BaseCredentials]:
"""Convert dictionary to credentials object"""
try:
2026-01-06 16:41:03 +01:00
if cred_data.get("type") == "user_password":
2025-10-29 20:23:50 +01:00
return UserPasswordCredentials(
2026-01-06 16:41:03 +01:00
username=cred_data.get("username", ""),
password=cred_data.get("password", ""),
client_id=cred_data.get("client_id"),
2025-10-29 20:23:50 +01:00
)
2026-01-06 16:41:03 +01:00
elif cred_data.get("type") == "client_credentials":
2025-10-29 20:23:50 +01:00
return ClientCredentials(
2026-01-06 16:41:03 +01:00
client_id=cred_data.get("client_id", ""),
client_secret=cred_data.get("client_secret", ""),
2025-10-29 20:23:50 +01:00
)
else:
logger.error(f"Unknown credential type: {cred_data.get('type')}")
return None
except Exception as e:
logger.error(f"Error creating credential from dict: {e}")
return None
def debug_credentials_file(self) -> None:
"""Debug method to log the current state of the credentials file"""
try:
logger.info(f"=== CREDENTIALS FILE DEBUG INFO ===")
credentials_file_path = self.vfs.join_path(self.credentials_file)
logger.info(f"Credentials file path: {credentials_file_path}")
2026-01-16 12:24:41 +01:00
logger.info(f"Credentials file exists: {self.vfs.exists(self.credentials_file)}")
2025-10-29 20:23:50 +01:00
if self.vfs.exists(self.credentials_file):
file_size = self.vfs.get_size(self.credentials_file)
logger.info(f"Credentials file size: {file_size} bytes")
if file_size and file_size > 0:
data = self.vfs.read_json(self.credentials_file)
if data:
2026-01-06 16:41:03 +01:00
logger.info(
f"Credentials file contains {len(data)} providers: {list(data.keys())}"
)
2025-10-29 20:23:50 +01:00
for provider, provider_data in data.items():
if isinstance(provider_data, dict):
# Check if nested (has countries)
has_countries = any(
2026-01-06 16:41:03 +01:00
isinstance(v, dict) and len(k) <= 3 and "type" in v
2025-10-29 20:23:50 +01:00
for k, v in provider_data.items()
)
if has_countries:
logger.info(f" {provider} (country-aware):")
for country, cred_data in provider_data.items():
2026-01-16 12:24:41 +01:00
if isinstance(cred_data, dict) and "type" in cred_data:
2026-01-06 16:41:03 +01:00
cred_type = cred_data.get("type")
username = cred_data.get("username", "N/A")
logger.info(
f" {country}: type={cred_type}, username={username}"
)
2025-10-29 20:23:50 +01:00
else:
# Flat structure (no country)
2026-01-06 16:41:03 +01:00
cred_type = provider_data.get("type", "unknown")
username = provider_data.get("username", "N/A")
logger.info(
f" {provider} (no country): type={cred_type}, username={username}"
)
2025-10-29 20:23:50 +01:00
else:
2026-01-16 12:24:41 +01:00
logger.error("Credentials file contains invalid JSON or is empty")
2025-10-29 20:23:50 +01:00
else:
logger.info("Credentials file is empty")
else:
logger.info("Credentials file does not exist yet")
logger.info(f"=== END CREDENTIALS FILE DEBUG ===")
except Exception as e:
2026-01-06 16:41:03 +01:00
logger.error(f"Error during credentials file debug: {e}")