diff --git a/comet/metadata/filter.py b/comet/metadata/filter.py index 5c1be70..4a7f15c 100644 --- a/comet/metadata/filter.py +++ b/comet/metadata/filter.py @@ -52,6 +52,13 @@ class DigitalReleaseFilter: release_date_str = None if media_type == "movie": release_date_str = await tmdb.get_upcoming_movie_release_date(tmdb_id) + if not release_date_str: + # Check watch providers as fallback + has_watch_providers = await tmdb.has_watch_providers(tmdb_id) + if has_watch_providers: + # Treat as released a long time ago + release_date_str = "1970-01-01" + elif media_type == "series": release_date_str = await tmdb.get_episode_air_date( tmdb_id, season, episode diff --git a/comet/metadata/tmdb.py b/comet/metadata/tmdb.py index ba79ad8..4757c5e 100644 --- a/comet/metadata/tmdb.py +++ b/comet/metadata/tmdb.py @@ -77,3 +77,16 @@ class TMDBApi: except Exception as e: logger.error(f"TMDB: Error converting IMDB ID {imdb_id}: {e}") return None + + async def has_watch_providers(self, tmdb_id: str): + try: + url = f"{self.base_url}/movie/{tmdb_id}/watch/providers" + async with self.session.get(url, headers=self.headers) as response: + if response.status != 200: + return None + + data = await response.json() + return bool(data.get("results")) + except Exception as e: + logger.error(f"TMDB: Error getting watch providers for {tmdb_id}: {e}") + return None diff --git a/comet/utils/network.py b/comet/utils/network.py index 8ae102e..2fc67c0 100644 --- a/comet/utils/network.py +++ b/comet/utils/network.py @@ -1,3 +1,5 @@ +import ipaddress + import aiohttp from fastapi import Request @@ -9,13 +11,50 @@ NO_CACHE_HEADERS = { "Expires": "0", } +IP_REQUEST_HEADERS = [ + "X-Client-Ip", + "Cf-Connecting-Ip", + "Do-Connecting-Ip", + "Fastly-Client-Ip", + "True-Client-Ip", + "X-Real-Ip", + "X-Cluster-Client-Ip", + "X-Forwarded", + "X-Forwarded-For", + "Forwarded-For", + "Forwarded", + "X-Appengine-User-Ip", + "Cf-Pseudo-IPv4", +] + + +def is_public_ip(ip: str): + try: + parsed_ip = ipaddress.ip_address(ip) + return not parsed_ip.is_private and not parsed_ip.is_loopback + except ValueError: + return False + def get_client_ip(request: Request): - return ( - request.headers["cf-connecting-ip"] - if "cf-connecting-ip" in request.headers - else request.client.host - ) + for header in IP_REQUEST_HEADERS: + header_value = request.headers.get(header) + if not header_value: + continue + + if header == "X-Forwarded-For": + for ip_part in header_value.split(","): + ip_part = ip_part.strip() + if is_public_ip(ip_part): + return ip_part + else: + if is_public_ip(header_value): + return header_value + + if request.client and request.client.host and is_public_ip(request.client.host): + return request.client.host + + return "" async def fetch_with_proxy_fallback(