Files
script.service.ultimate/lib/streaming_providers/base/models/subscription.py
T

119 lines
3.9 KiB
Python
Raw Normal View History

2026-08-12 09:45:25 +02:00
# streaming_providers/base/models/subscription.py
2025-12-22 14:32:36 +01:00
from dataclasses import dataclass, field
from datetime import datetime
2026-08-12 09:45:25 +02:00
from typing import Dict, List, Optional, Set, Any # <-- Added Any
from .pricing import Pricing
2025-12-22 14:32:36 +01:00
@dataclass
class SubscriptionPackage:
2026-08-12 09:45:25 +02:00
"""Represents a subscription package (a purchasable bouquet/tier)."""
2025-12-22 14:32:36 +01:00
package_id: str
name: str
2026-08-12 09:45:25 +02:00
tier: Optional[str] = None
bouquet: Optional[str] = None
pricing: Optional[Pricing] = None
2025-12-22 14:32:36 +01:00
2026-08-12 09:45:25 +02:00
description: Optional[str] = None
2025-12-22 14:32:36 +01:00
channel_ids: List[str] = field(default_factory=list)
2026-08-12 09:45:25 +02:00
# Fix is here:
metadata: Dict[str, Any] = field(default_factory=dict)
"""Provider-specific metadata"""
2025-12-22 14:32:36 +01:00
@property
def channel_count(self) -> int:
return len(self.channel_ids)
@dataclass
class UserSubscription:
"""User's subscription status for a provider"""
provider: str
country: str
active: bool = False
2026-08-12 09:45:25 +02:00
"""Whether the account subscription is currently active"""
2025-12-22 14:32:36 +01:00
packages: List[SubscriptionPackage] = field(default_factory=list)
2026-08-12 09:45:25 +02:00
"""All subscription packages the user has purchased"""
2025-12-22 14:32:36 +01:00
valid_from: Optional[datetime] = None
valid_until: Optional[datetime] = None
status_message: Optional[str] = None
billing_status: Optional[str] = None
def __post_init__(self):
self._update_derived_fields()
def _update_derived_fields(self):
2026-08-12 09:45:25 +02:00
"""Pre-calculate sets for fast entitlement checks"""
self.accessible_channel_ids: Set[str] = set()
self.active_tiers: Set[str] = set()
self.active_bouquets: Set[str] = set()
2025-12-22 14:32:36 +01:00
2026-08-12 09:45:25 +02:00
if not self.active or not self._is_currently_valid():
return
2025-12-22 14:32:36 +01:00
2026-08-12 09:45:25 +02:00
for package in self.packages:
self.accessible_channel_ids.update(package.channel_ids)
if package.tier:
self.active_tiers.add(package.tier)
if package.bouquet:
self.active_bouquets.add(package.bouquet)
def _is_currently_valid(self) -> bool:
"""Check if subscription is within valid date range"""
now = datetime.now()
if self.valid_from and now < self.valid_from:
return False
if self.valid_until and now > self.valid_until:
return False
return True
# --- Entitlement Checks ---
def has_tier(self, tier: str) -> bool:
"""Check if user has a specific tier"""
return self.active and tier in self.active_tiers
def has_bouquet(self, bouquet: str) -> bool:
"""Check if user has a specific bouquet"""
return self.active and bouquet in self.active_bouquets
2025-12-22 14:32:36 +01:00
2026-08-12 09:45:25 +02:00
def can_access_channel(self, channel_id: str) -> bool:
"""Check if user can access a specific channel"""
return self.active and channel_id in self.accessible_channel_ids
2025-12-22 14:32:36 +01:00
2026-08-12 09:45:25 +02:00
# --- Serialization ---
2025-12-22 14:32:36 +01:00
def to_dict(self) -> Dict:
return {
2026-01-06 16:41:03 +01:00
"provider": self.provider,
"country": self.country,
"active": self.active,
2026-08-12 09:45:25 +02:00
"has_packages": len(self.packages) > 0,
"package_count": len(self.packages),
"accessible_channel_count": len(self.accessible_channel_ids),
"active_tiers": list(self.active_tiers),
"active_bouquets": list(self.active_bouquets),
2026-01-06 16:41:03 +01:00
"valid_from": self.valid_from.isoformat() if self.valid_from else None,
"valid_until": self.valid_until.isoformat() if self.valid_until else None,
"status_message": self.status_message,
"billing_status": self.billing_status,
"packages": [
2025-12-22 14:32:36 +01:00
{
2026-01-06 16:41:03 +01:00
"package_id": pkg.package_id,
"name": pkg.name,
2026-08-12 09:45:25 +02:00
"tier": pkg.tier,
"bouquet": pkg.bouquet,
"pricing": pkg.pricing.to_dict() if pkg.pricing else None,
2026-01-06 16:41:03 +01:00
"channel_count": pkg.channel_count,
2025-12-22 14:32:36 +01:00
}
for pkg in self.packages
2026-01-06 16:41:03 +01:00
],
2026-08-12 09:45:25 +02:00
}