import httpx import logging from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from typing import List, Optional, Dict, Any from pydantic import BaseModel # --- Configuration --- VEGA_API_BASE = "https://sanch1tx-vega-providers.hf.space" MODFLIX_JSON_URL = "https://raw.githubusercontent.com/himanshu8443/providers/main/modflix.json" # --- Provider ID Mappings (Critical Fix) --- # Map 'name' or incorrect 'value' from JSON to the actual folder name on the server PROVIDER_ID_MAP = { "vegamovies": "vega", "moviesmod": "mod", "uhdmovies": "uhd", "moviesdrive": "drive", "luxmovies": "luxMovies", # Case sensitive? matches folder name "topmovies": "topmovies", # matches folder "nfmirror": "netflixMirror", "primemirror": "primeMirror", "tokyoinsider": "tokyoInsider", "skymovieshd": "skyMovieHD", "moviebox": "movieBox", "cinevood": "1cinevood", "kmmovies": "kmMovies", "katmoviefix": "katMovieFix", "joya9tv1": "Joya9tv", "cinewood": "1cinevood" } # --- Hardcoded Fallback Catalogs --- DEFAULT_CATALOGS = { "drive": [ {"title": "Latest", "filter": ""}, {"title": "Anime", "filter": "category/anime/"}, {"title": "Netflix", "filter": "category/netflix/"}, {"title": "4K", "filter": "category/2160p-4k/"}, ], "vega": [ {"title": "New", "filter": ""}, {"title": "Netflix", "filter": "web-series/netflix"}, {"title": "Amazon Prime", "filter": "web-series/amazon-prime-video"}, ], "default": [ {"title": "Latest", "filter": ""}, {"title": "Trending", "filter": "trending"}, ] } # Fallback providers FALLBACK_PROVIDERS = [ {"name": "Vega", "value": "vega", "baseUrl": VEGA_API_BASE, "isActive": True}, {"name": "UHD", "value": "uhd", "baseUrl": VEGA_API_BASE, "isActive": True}, {"name": "Drive", "value": "drive", "baseUrl": VEGA_API_BASE, "isActive": True}, {"name": "Movies4u", "value": "movies4u", "baseUrl": VEGA_API_BASE, "isActive": True}, ] app = FastAPI(title="Vega Providers Gateway") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) logging.basicConfig(level=logging.INFO) logger = logging.getLogger("vega_gateway") class ProviderModel(BaseModel): name: str value: str baseUrl: str isActive: bool @app.get("/") async def root(): return {"status": "ok", "message": "Vega Python Gateway Active"} @app.get("/providers", response_model=List[ProviderModel]) async def get_providers(): async with httpx.AsyncClient() as client: try: response = await client.get(MODFLIX_JSON_URL, timeout=4.0) if response.status_code == 200: data = response.json() providers = [] raw_list = data if isinstance(data, list) else data.get("providers", []) for p in raw_list: if isinstance(p, dict): name = p.get("name") or "Unknown" raw_val = p.get("value") or p.get("id") or name # Apply mapping to ensure valid backend ID val = PROVIDER_ID_MAP.get(raw_val.lower(), raw_val) if val: providers.append({ "name": name, "value": val, "baseUrl": p.get("baseUrl") or VEGA_API_BASE, "isActive": True }) if providers: return providers except Exception: pass return FALLBACK_PROVIDERS @app.get("/{provider}/catalog") async def get_catalog(provider: str): # Remap provider if needed (double check) provider_id = PROVIDER_ID_MAP.get(provider.lower(), provider) url = f"{VEGA_API_BASE}/{provider_id}/catalog" try: async with httpx.AsyncClient() as client: resp = await client.get(url, timeout=5.0) if resp.status_code == 200: data = resp.json() if isinstance(data, list) and len(data) > 0: return {"catalog": data} if isinstance(data, dict) and "catalog" in data: return data except Exception: pass fallback = DEFAULT_CATALOGS.get(provider_id) or DEFAULT_CATALOGS.get("default") return {"catalog": fallback} @app.get("/{provider}/posts") async def get_posts(provider: str, filter: str = "", page: int = 1): provider_id = PROVIDER_ID_MAP.get(provider.lower(), provider) url = f"{VEGA_API_BASE}/{provider_id}/posts" params = {"filter": filter, "page": page} async with httpx.AsyncClient() as client: try: resp = await client.get(url, params=params, timeout=12.0) if resp.status_code == 200: data = resp.json() if isinstance(data, list): return data # Handle error response from upstream like {"error": "..."} if isinstance(data, dict) and "error" in data: logger.error(f"Upstream error for {provider_id}: {data['error']}") except Exception as e: logger.error(f"Error posts {provider_id}: {e}") pass return [] @app.get("/{provider}/meta") async def get_meta(provider: str, link: str): provider_id = PROVIDER_ID_MAP.get(provider.lower(), provider) url = f"{VEGA_API_BASE}/{provider_id}/meta" params = {"link": link} async with httpx.AsyncClient() as client: try: resp = await client.get(url, params=params, timeout=15.0) if resp.status_code == 200: return resp.json() except Exception: pass raise HTTPException(status_code=404, detail="Meta not found") @app.get("/{provider}/episodes") async def get_episodes(provider: str, url: str): provider_id = PROVIDER_ID_MAP.get(provider.lower(), provider) api_url = f"{VEGA_API_BASE}/{provider_id}/episodes" params = {"url": url} async with httpx.AsyncClient() as client: try: resp = await client.get(api_url, params=params, timeout=15.0) if resp.status_code == 200 and isinstance(resp.json(), list): return resp.json() except Exception: pass return [] @app.get("/{provider}/stream") async def get_stream(provider: str, link: str, type: str = "movie"): provider_id = PROVIDER_ID_MAP.get(provider.lower(), provider) api_url = f"{VEGA_API_BASE}/{provider_id}/stream" params = {"link": link, "type": type} async with httpx.AsyncClient() as client: try: resp = await client.get(api_url, params=params, timeout=45.0) if resp.status_code == 200 and isinstance(resp.json(), list): return resp.json() except: pass return []