MyanmarSwe commited on
Commit
c0a3e03
·
verified ·
1 Parent(s): 3d0eb99

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +108 -47
main.py CHANGED
@@ -7,12 +7,15 @@ from google.oauth2.service_account import Credentials
7
 
8
  app = FastAPI()
9
 
 
10
  TMDB_KEY = os.getenv("TMDB_KEY")
11
  SHEET_ID = os.getenv("SHEET_ID")
12
  SERVICE_ACCOUNT_STR = os.getenv("SERVICE_ACCOUNT_JSON")
13
 
 
14
  db_cache = {"movies": [], "tv_shows": [], "users": {}}
15
  tmdb_cache = {}
 
16
 
17
  def get_sheet():
18
  creds = Credentials.from_service_account_info(
@@ -22,15 +25,14 @@ def get_sheet():
22
  return gspread.authorize(creds).open_by_key(SHEET_ID)
23
 
24
  async def fetch_tmdb_meta(tmdb_id, media_type="movie"):
25
- cache_key = f"{media_type}_{tmdb_id}"
26
- if cache_key in tmdb_cache: return tmdb_cache[cache_key]
27
-
28
- url = f"https://api.themoviedb.org/3/{media_type}/{tmdb_id}?api_key={TMDB_KEY}"
29
  async with httpx.AsyncClient() as client:
30
  res = await client.get(url)
31
  if res.status_code == 200:
32
  data = res.json()
33
- tmdb_cache[cache_key] = data
34
  return data
35
  return None
36
 
@@ -38,91 +40,150 @@ async def fetch_tmdb_meta(tmdb_id, media_type="movie"):
38
  async def refresh_db():
39
  try:
40
  ss = get_sheet()
 
 
 
41
 
42
- # Sync Movies
43
- movie_ws = ss.worksheet("movies")
44
- movies = movie_ws.get_all_records()
45
- db_cache["movies"] = movies
46
- for i, row in enumerate(movies):
47
- if not row.get("status"):
48
- movie_ws.update_cell(i + 2, 4, "synced") # Column D
49
-
50
- # Sync TV Shows
51
- tv_ws = ss.worksheet("tv_shows")
52
- tvs = tv_ws.get_all_records()
53
- db_cache["tv_shows"] = tvs
54
- for i, row in enumerate(tvs):
55
- if not row.get("status"):
56
- tv_ws.update_cell(i + 2, 6, "synced") # Column F
57
-
58
- # Sync Users
59
- user_ws = ss.worksheet("users")
60
- db_cache["users"] = {r["user_token"]: r for r in user_ws.get_all_records() if r["status"] == "active"}
61
-
62
- return {"status": "success", "message": "Database Refreshed & Status Updated"}
63
  except Exception as e:
64
  return {"status": "error", "message": str(e)}
65
 
66
  @app.get("/{token}/manifest.json")
67
  async def get_manifest(token: str):
68
  if token not in db_cache["users"]: raise HTTPException(status_code=403)
 
 
 
 
 
 
 
69
  return {
70
- "id": f"org.gemini.{token}",
71
- "version": "1.0.0",
72
- "name": "My Private Addon",
73
  "resources": ["catalog", "meta", "stream"],
74
  "types": ["movie", "series"],
75
  "catalogs": [
76
- {"type": "movie", "id": "my_movies", "name": "Movies"},
77
- {"type": "series", "id": "my_series", "name": "TV Series"}
 
 
 
 
 
 
 
 
 
 
78
  ]
79
  }
80
 
81
  @app.get("/{token}/catalog/{type}/{id}.json")
82
- async def get_catalog(token: str, type: str, id: str):
83
  items = db_cache["movies"] if type == "movie" else db_cache["tv_shows"]
84
  metas = []
85
  processed_ids = set()
 
 
 
 
86
 
87
  for item in items:
88
  tid = str(item["tmdb_id"])
89
  if tid in processed_ids: continue
90
 
91
  tmdb_data = await fetch_tmdb_meta(tid, "movie" if type == "movie" else "tv")
92
- if tmdb_data:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  metas.append({
94
  "id": f"tmdb:{tid}",
95
  "type": type,
96
- "name": tmdb_data.get("title") or tmdb_data.get("name"),
97
- "poster": f"https://image.tmdb.org/t/p/w500{tmdb_data.get('poster_path')}"
98
  })
99
  processed_ids.add(tid)
 
100
  return {"metas": metas}
101
 
102
  @app.get("/{token}/meta/{type}/tmdb:{tmdb_id}.json")
103
  async def get_meta(token: str, type: str, tmdb_id: str):
104
  tmdb_data = await fetch_tmdb_meta(tmdb_id, "movie" if type == "movie" else "tv")
105
- return {"meta": {
 
106
  "id": f"tmdb:{tmdb_id}",
107
  "type": type,
108
  "name": tmdb_data.get("title") or tmdb_data.get("name"),
109
- "poster": f"https://image.tmdb.org/t/p/w500{tmdb_data.get('poster_path')}",
110
- "background": f"https://image.tmdb.org/t/p/original{tmdb_data.get('backdrop_path')}",
111
  "description": tmdb_data.get("overview")
112
- }}
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
  @app.get("/{token}/stream/{type}/{id}.json")
115
  async def get_stream(token: str, type: str, id: str):
116
  if token not in db_cache["users"]: return {"streams": []}
117
-
118
- parts = id.split(":") # tmdb:1399:1:1
119
  tmdb_id = parts[1]
120
-
121
- if type == "series":
122
- season, episode = parts[2], parts[3]
123
- matched = next((i for i in db_cache["tv_shows"] if str(i["tmdb_id"]) == tmdb_id and str(i["season"]) == season and str(i["episode"]) == episode), None)
124
  else:
125
  matched = next((i for i in db_cache["movies"] if str(i["tmdb_id"]) == tmdb_id), None)
126
-
127
  if not matched: return {"streams": []}
128
- return {"streams": [{"title": "Direct Link", "url": matched["link"]}]}
 
7
 
8
  app = FastAPI()
9
 
10
+ # Config
11
  TMDB_KEY = os.getenv("TMDB_KEY")
12
  SHEET_ID = os.getenv("SHEET_ID")
13
  SERVICE_ACCOUNT_STR = os.getenv("SERVICE_ACCOUNT_JSON")
14
 
15
+ # Cache
16
  db_cache = {"movies": [], "tv_shows": [], "users": {}}
17
  tmdb_cache = {}
18
+ filter_options = {"years": set(), "genres": set(), "casts": set()}
19
 
20
  def get_sheet():
21
  creds = Credentials.from_service_account_info(
 
25
  return gspread.authorize(creds).open_by_key(SHEET_ID)
26
 
27
  async def fetch_tmdb_meta(tmdb_id, media_type="movie"):
28
+ ck = f"{media_type}_{tmdb_id}"
29
+ if ck in tmdb_cache: return tmdb_cache[ck]
30
+ url = f"https://api.themoviedb.org/3/{media_type}/{tmdb_id}?api_key={TMDB_KEY}&append_to_response=credits"
 
31
  async with httpx.AsyncClient() as client:
32
  res = await client.get(url)
33
  if res.status_code == 200:
34
  data = res.json()
35
+ tmdb_cache[ck] = data
36
  return data
37
  return None
38
 
 
40
  async def refresh_db():
41
  try:
42
  ss = get_sheet()
43
+ db_cache["movies"] = ss.worksheet("movies").get_all_records()
44
+ db_cache["tv_shows"] = ss.worksheet("tv_shows").get_all_records()
45
+ db_cache["users"] = {r["user_token"]: r for r in ss.worksheet("users").get_all_records() if r["status"] == "active"}
46
 
47
+ # Filter Options များကို ပြန်ဆန်းသစ်ခြင်း
48
+ filter_options["years"].clear()
49
+ filter_options["genres"].clear()
50
+ filter_options["casts"].clear()
51
+
52
+ all_items = db_cache["movies"] + db_cache["tv_shows"]
53
+ for item in all_items:
54
+ m_type = "movie" if item in db_cache["movies"] else "tv"
55
+ data = await fetch_tmdb_meta(item["tmdb_id"], m_type)
56
+ if data:
57
+ rd = data.get("release_date") or data.get("first_air_date", "0000")
58
+ filter_options["years"].add(rd.split("-")[0])
59
+ for g in data.get("genres", []): filter_options["genres"].add(g["name"])
60
+ for person in data.get("credits", {}).get("cast", [])[:5]:
61
+ filter_options["casts"].add(person["name"])
62
+
63
+ return {"status": "success", "message": "Database & Filters Synced"}
 
 
 
 
64
  except Exception as e:
65
  return {"status": "error", "message": str(e)}
66
 
67
  @app.get("/{token}/manifest.json")
68
  async def get_manifest(token: str):
69
  if token not in db_cache["users"]: raise HTTPException(status_code=403)
70
+
71
+ # Sidebar Filters စီစဉ်ခြင်း
72
+ years = [f"Year: {y}" for y in sorted(list(filter_options["years"]), reverse=True)]
73
+ genres = [f"Genre: {g}" for g in sorted(list(filter_options["genres"]))]
74
+ casts = [f"Actor: {c}" for c in sorted(list(filter_options["casts"]))]
75
+ combined = ["All"] + years + genres + casts
76
+
77
  return {
78
+ "id": f"ms.private.{token}",
79
+ "version": "2.1.0",
80
+ "name": "MyanmarSwe Private Cinema",
81
  "resources": ["catalog", "meta", "stream"],
82
  "types": ["movie", "series"],
83
  "catalogs": [
84
+ {
85
+ "type": "movie", "id": "ms_latest", "name": "✨ New Arrivals",
86
+ "extra": [{"name": "genre", "options": combined}]
87
+ },
88
+ {
89
+ "type": "movie", "id": "ms_collections", "name": "📚 Movie Collections",
90
+ "extra": [{"name": "genre", "options": combined}]
91
+ },
92
+ {
93
+ "type": "series", "id": "ms_series", "name": "📺 TV Series",
94
+ "extra": [{"name": "genre", "options": combined}]
95
+ }
96
  ]
97
  }
98
 
99
  @app.get("/{token}/catalog/{type}/{id}.json")
100
+ async def get_catalog(token: str, type: str, id: str, genre: str = None):
101
  items = db_cache["movies"] if type == "movie" else db_cache["tv_shows"]
102
  metas = []
103
  processed_ids = set()
104
+ processed_collections = set()
105
+
106
+ # Sort: New Arrivals အတွက် နောက်ဆုံးထည့်ထားတာကို အရင်ပြရန်
107
+ if id == "ms_latest": items = items[::-1]
108
 
109
  for item in items:
110
  tid = str(item["tmdb_id"])
111
  if tid in processed_ids: continue
112
 
113
  tmdb_data = await fetch_tmdb_meta(tid, "movie" if type == "movie" else "tv")
114
+ if not tmdb_data: continue
115
+
116
+ # --- Filter Logic ---
117
+ show_item = False
118
+ if not genre or genre == "All":
119
+ show_item = True
120
+ else:
121
+ try:
122
+ prefix, value = genre.split(": ")
123
+ if prefix == "Year":
124
+ rd = tmdb_data.get("release_date") or tmdb_data.get("first_air_date", "0000")
125
+ if rd.startswith(value): show_item = True
126
+ elif prefix == "Genre":
127
+ if any(g["name"] == value for g in tmdb_data.get("genres", [])): show_item = True
128
+ elif prefix == "Actor":
129
+ cast = [p["name"] for p in tmdb_data.get("credits", {}).get("cast", [])[:5]]
130
+ if value in cast: show_item = True
131
+ except: show_item = True
132
+
133
+ # --- Catalog Specific Logic ---
134
+ if id == "ms_collections":
135
+ col = tmdb_data.get("belongs_to_collection")
136
+ if not col or col["id"] in processed_collections: continue
137
+ processed_collections.add(col["id"])
138
+ name, poster = col["name"], col["poster_path"]
139
+ else:
140
+ name, poster = tmdb_data.get("title") or tmdb_data.get("name"), tmdb_data.get("poster_path")
141
+
142
+ if show_item:
143
  metas.append({
144
  "id": f"tmdb:{tid}",
145
  "type": type,
146
+ "name": name,
147
+ "poster": f"https://image.tmdb.org/t/p/w500{poster}"
148
  })
149
  processed_ids.add(tid)
150
+
151
  return {"metas": metas}
152
 
153
  @app.get("/{token}/meta/{type}/tmdb:{tmdb_id}.json")
154
  async def get_meta(token: str, type: str, tmdb_id: str):
155
  tmdb_data = await fetch_tmdb_meta(tmdb_id, "movie" if type == "movie" else "tv")
156
+ if not tmdb_data: return {"meta": {}}
157
+ meta = {
158
  "id": f"tmdb:{tmdb_id}",
159
  "type": type,
160
  "name": tmdb_data.get("title") or tmdb_data.get("name"),
161
+ "poster": f"https://image.tmdb.org/t/p/w500{tmdb_data['poster_path']}",
162
+ "background": f"https://image.tmdb.org/t/p/original{tmdb_data['backdrop_path']}",
163
  "description": tmdb_data.get("overview")
164
+ }
165
+ if type == "series":
166
+ videos = []
167
+ episodes = [row for row in db_cache["tv_shows"] if str(row["tmdb_id"]) == str(tmdb_id)]
168
+ for ep in episodes:
169
+ videos.append({
170
+ "id": f"tmdb:{tmdb_id}:{ep['season']}:{ep['episode']}",
171
+ "title": ep.get("name", f"Episode {ep['episode']}"),
172
+ "season": int(ep["season"]), "episode": int(ep["episode"]),
173
+ "released": "2020-01-01T00:00:00.000Z"
174
+ })
175
+ meta["videos"] = sorted(videos, key=lambda x: (x['season'], x['episode']))
176
+ return {"meta": meta}
177
 
178
  @app.get("/{token}/stream/{type}/{id}.json")
179
  async def get_stream(token: str, type: str, id: str):
180
  if token not in db_cache["users"]: return {"streams": []}
181
+ parts = id.split(":")
 
182
  tmdb_id = parts[1]
183
+ if type == "series" and len(parts) >= 4:
184
+ s, e = str(parts[2]), str(parts[3])
185
+ matched = next((i for i in db_cache["tv_shows"] if str(i["tmdb_id"]) == tmdb_id and str(i["season"]) == s and str(i["episode"]) == e), None)
 
186
  else:
187
  matched = next((i for i in db_cache["movies"] if str(i["tmdb_id"]) == tmdb_id), None)
 
188
  if not matched: return {"streams": []}
189
+ return {"streams": [{"title": "🚀 MyanmarSwe Direct", "url": matched["link"]}]}