Spaces:
Running
Running
| from fastapi import FastAPI | |
| from fastapi.middleware.cors import CORSMiddleware | |
| import httpx | |
| import json | |
| import logging | |
| app = FastAPI() | |
| logging.basicConfig(level=logging.INFO) | |
| # Enable CORS for all origins | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| BASE_62_MAP = {c: i for i, c in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")} | |
| async def get_client() -> httpx.AsyncClient: | |
| if not hasattr(app.state, "client"): | |
| app.state.client = httpx.AsyncClient(timeout=15.0) | |
| return app.state.client | |
| def base62_to_int(token: str) -> int: | |
| result = 0 | |
| for ch in token: | |
| result = result * 62 + BASE_62_MAP[ch] | |
| return result | |
| async def get_base_url(token: str) -> str: | |
| first = token[0] | |
| if first == "A": | |
| n = base62_to_int(token[1]) | |
| else: | |
| n = base62_to_int(token[1:3]) | |
| return f"https://p{n:02d}-sharedstreams.icloud.com/{token}/sharedstreams/" | |
| ICLOUD_HEADERS = { | |
| "Origin": "https://www.icloud.com", | |
| "Content-Type": "text/plain" | |
| } | |
| ICLOUD_PAYLOAD = '{"streamCtag":null}' | |
| async def get_redirected_base_url(base_url: str, token: str) -> str: | |
| client = await get_client() | |
| resp = await client.post( | |
| f"{base_url}webstream", headers=ICLOUD_HEADERS, data=ICLOUD_PAYLOAD, follow_redirects=False | |
| ) | |
| if resp.status_code == 330: | |
| try: | |
| body = resp.json() | |
| host = body.get("X-Apple-MMe-Host") | |
| if not host: | |
| raise ValueError("Missing X-Apple-MMe-Host in 330 response") | |
| logging.info(f"Redirected to {host}") | |
| return f"https://{host}/{token}/sharedstreams/" | |
| except Exception as e: | |
| logging.error(f"Redirect parsing failed: {e}") | |
| raise | |
| elif resp.status_code == 200: | |
| return base_url | |
| else: | |
| resp.raise_for_status() | |
| async def post_json(path: str, base_url: str, payload: str) -> dict: | |
| client = await get_client() | |
| resp = await client.post(f"{base_url}{path}", headers=ICLOUD_HEADERS, data=payload) | |
| resp.raise_for_status() | |
| return resp.json() | |
| async def get_metadata(base_url: str) -> list: | |
| data = await post_json("webstream", base_url, ICLOUD_PAYLOAD) | |
| return data.get("photos", []) | |
| async def get_asset_urls(base_url: str, guids: list) -> dict: | |
| payload = json.dumps({"photoGuids": guids}) | |
| data = await post_json("webasseturls", base_url, payload) | |
| return data.get("items", {}) | |
| async def get_album(token: str): | |
| try: | |
| base_url = await get_base_url(token) | |
| base_url = await get_redirected_base_url(base_url, token) | |
| metadata = await get_metadata(base_url) | |
| guids = [photo["photoGuid"] for photo in metadata] | |
| asset_map = await get_asset_urls(base_url, guids) | |
| videos = [] | |
| for photo in metadata: | |
| if photo.get("mediaAssetType", "").lower() != "video": | |
| continue | |
| derivatives = photo.get("derivatives", {}) | |
| best = max( | |
| (d for k, d in derivatives.items() if k.lower() != "posterframe"), | |
| key=lambda d: int(d.get("fileSize") or 0), | |
| default=None | |
| ) | |
| if not best: | |
| continue | |
| checksum = best.get("checksum") | |
| info = asset_map.get(checksum) | |
| if not info: | |
| continue | |
| video_url = f"https://{info['url_location']}{info['url_path']}" | |
| poster = None | |
| pf = derivatives.get("PosterFrame") | |
| if pf: | |
| pf_info = asset_map.get(pf.get("checksum")) | |
| if pf_info: | |
| poster = f"https://{pf_info['url_location']}{pf_info['url_path']}" | |
| videos.append({ | |
| "caption": photo.get("caption", ""), | |
| "url": video_url, | |
| "poster": poster or "" | |
| }) | |
| return {"videos": videos} | |
| except Exception as e: | |
| logging.exception("Error in get_album") | |
| return {"error": str(e)} | |
| async def get_album_raw(token: str): | |
| try: | |
| base_url = await get_base_url(token) | |
| base_url = await get_redirected_base_url(base_url, token) | |
| metadata = await get_metadata(base_url) | |
| guids = [photo["photoGuid"] for photo in metadata] | |
| asset_map = await get_asset_urls(base_url, guids) | |
| return {"metadata": metadata, "asset_urls": asset_map} | |
| except Exception as e: | |
| logging.exception("Error in get_album_raw") | |
| return {"error": str(e)} |