sanch1tx commited on
Commit
d517fcd
·
verified ·
1 Parent(s): 76f4401

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +177 -0
app.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import httpx
2
+ import json
3
+ import logging
4
+ from fastapi import FastAPI, HTTPException, Query
5
+ from fastapi.middleware.cors import CORSMiddleware
6
+ from typing import List, Optional, Dict, Any
7
+ from pydantic import BaseModel
8
+
9
+ # --- Configuration ---
10
+ # The base URL of your existing TypeScript/Node API (Vega Providers)
11
+ VEGA_API_BASE = "https://sanch1tx-vega-providers.hf.space"
12
+ # The URL for the provider list
13
+ MODFLIX_JSON_URL = "https://raw.githubusercontent.com/himanshu8443/providers/main/modflix.json"
14
+
15
+ # Fallback providers in case GitHub is down or inaccessible
16
+ FALLBACK_PROVIDERS = [
17
+ {"name": "vega", "baseUrl": VEGA_API_BASE, "isActive": True},
18
+ {"name": "uhd", "baseUrl": VEGA_API_BASE, "isActive": True},
19
+ {"name": "movies4u", "baseUrl": VEGA_API_BASE, "isActive": True},
20
+ {"name": "flimmo", "baseUrl": VEGA_API_BASE, "isActive": True},
21
+ {"name": "tobias", "baseUrl": VEGA_API_BASE, "isActive": True},
22
+ {"name": "goku", "baseUrl": VEGA_API_BASE, "isActive": True},
23
+ {"name": "kissKh", "baseUrl": VEGA_API_BASE, "isActive": True},
24
+ {"name": "superVideoExtractor", "baseUrl": VEGA_API_BASE, "isActive": True},
25
+ ]
26
+
27
+ # --- FastAPI App Setup ---
28
+ app = FastAPI(title="Vega Providers Gateway", description="A Python bridge for Vega Providers")
29
+
30
+ # Enable CORS for your frontend
31
+ app.add_middleware(
32
+ CORSMiddleware,
33
+ allow_origins=["*"], # Allow all origins for now (adjust for production)
34
+ allow_credentials=True,
35
+ allow_methods=["*"],
36
+ allow_headers=["*"],
37
+ )
38
+
39
+ # Logger setup
40
+ logging.basicConfig(level=logging.INFO)
41
+ logger = logging.getLogger("vega_gateway")
42
+
43
+ # --- Data Models ---
44
+ # (Optional, but good for documentation)
45
+ class Provider(BaseModel):
46
+ name: str
47
+ baseUrl: str
48
+ isActive: bool
49
+
50
+ # --- Helper Functions ---
51
+
52
+ async def fetch_providers_list():
53
+ """Fetches the provider list from GitHub with fallback."""
54
+ async with httpx.AsyncClient() as client:
55
+ try:
56
+ logger.info(f"Fetching providers from {MODFLIX_JSON_URL}")
57
+ response = await client.get(MODFLIX_JSON_URL, timeout=5.0)
58
+ response.raise_for_status()
59
+ data = response.json()
60
+
61
+ # Normalize data structure
62
+ providers = []
63
+ if isinstance(data, list):
64
+ providers = data
65
+ elif isinstance(data, dict) and "providers" in data:
66
+ providers = data["providers"]
67
+ elif isinstance(data, dict):
68
+ providers = list(data.values())
69
+
70
+ # Normalize fields
71
+ normalized = []
72
+ for p in providers:
73
+ if not isinstance(p, dict): continue
74
+ name = p.get("name") or p.get("value") or p.get("id") or "Unknown"
75
+ base_url = p.get("baseUrl") or VEGA_API_BASE
76
+ is_active = p.get("isActive", not p.get("disabled", False))
77
+ normalized.append({"name": name, "baseUrl": base_url, "isActive": is_active})
78
+
79
+ if not normalized:
80
+ logger.warning("Fetched data empty, using fallback.")
81
+ return FALLBACK_PROVIDERS
82
+
83
+ return normalized
84
+
85
+ except Exception as e:
86
+ logger.error(f"Failed to fetch providers: {e}. Using fallback.")
87
+ return FALLBACK_PROVIDERS
88
+
89
+ # --- Endpoints ---
90
+
91
+ @app.get("/", tags=["Health"])
92
+ async def root():
93
+ return {"status": "ok", "message": "Vega Python Gateway is running"}
94
+
95
+ @app.get("/providers", response_model=List[Provider], tags=["Core"])
96
+ async def get_providers():
97
+ """Returns a clean list of active providers."""
98
+ return await fetch_providers_list()
99
+
100
+ @app.get("/{provider}/catalog", tags=["Provider"])
101
+ async def get_catalog(provider: str):
102
+ """Fetches the catalog for a specific provider."""
103
+ url = f"{VEGA_API_BASE}/{provider}/catalog"
104
+ async with httpx.AsyncClient() as client:
105
+ try:
106
+ resp = await client.get(url, timeout=10.0)
107
+ if resp.status_code != 200:
108
+ # Return empty catalog instead of erroring out
109
+ logger.warning(f"Catalog fetch failed for {provider}: {resp.status_code}")
110
+ return {"catalog": []}
111
+ return resp.json()
112
+ except Exception as e:
113
+ logger.error(f"Error fetching catalog for {provider}: {e}")
114
+ return {"catalog": []}
115
+
116
+ @app.get("/{provider}/posts", tags=["Provider"])
117
+ async def get_posts(
118
+ provider: str,
119
+ filter: str = Query(..., description="The filter path from catalog"),
120
+ page: int = 1
121
+ ):
122
+ """Fetches posts (movies/series) for a provider and filter."""
123
+ url = f"{VEGA_API_BASE}/{provider}/posts"
124
+ params = {"filter": filter, "page": page}
125
+ async with httpx.AsyncClient() as client:
126
+ try:
127
+ resp = await client.get(url, params=params, timeout=15.0)
128
+ data = resp.json()
129
+ # Ensure it's always a list
130
+ if isinstance(data, list): return data
131
+ return []
132
+ except Exception as e:
133
+ logger.error(f"Error fetching posts for {provider}: {e}")
134
+ return []
135
+
136
+ @app.get("/{provider}/meta", tags=["Provider"])
137
+ async def get_meta(provider: str, link: str):
138
+ """Fetches metadata for a specific item."""
139
+ url = f"{VEGA_API_BASE}/{provider}/meta"
140
+ params = {"link": link}
141
+ async with httpx.AsyncClient() as client:
142
+ try:
143
+ resp = await client.get(url, params=params, timeout=15.0)
144
+ resp.raise_for_status()
145
+ return resp.json()
146
+ except Exception as e:
147
+ raise HTTPException(status_code=500, detail=str(e))
148
+
149
+ @app.get("/{provider}/episodes", tags=["Provider"])
150
+ async def get_episodes(provider: str, url: str):
151
+ """Fetches episodes if they require a separate call."""
152
+ api_url = f"{VEGA_API_BASE}/{provider}/episodes"
153
+ params = {"url": url}
154
+ async with httpx.AsyncClient() as client:
155
+ try:
156
+ resp = await client.get(api_url, params=params, timeout=15.0)
157
+ data = resp.json()
158
+ if isinstance(data, list): return data
159
+ return []
160
+ except Exception as e:
161
+ logger.error(f"Error fetching episodes: {e}")
162
+ return []
163
+
164
+ @app.get("/{provider}/stream", tags=["Provider"])
165
+ async def get_stream(provider: str, link: str, type: str = "movie"):
166
+ """Fetches stream links (runs extractors on the upstream server)."""
167
+ api_url = f"{VEGA_API_BASE}/{provider}/stream"
168
+ params = {"link": link, "type": type}
169
+ async with httpx.AsyncClient() as client:
170
+ try:
171
+ resp = await client.get(api_url, params=params, timeout=30.0) # Longer timeout for extraction
172
+ data = resp.json()
173
+ if isinstance(data, list): return data
174
+ return []
175
+ except Exception as e:
176
+ logger.error(f"Error fetching stream: {e}")
177
+ return []