SalexAI commited on
Commit
c4819ba
·
verified ·
1 Parent(s): 3ce94a3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +34 -58
app.py CHANGED
@@ -6,7 +6,7 @@ import logging
6
 
7
  app = FastAPI()
8
 
9
- # Set up basic logging
10
  logging.basicConfig(level=logging.INFO)
11
 
12
  # Allow all CORS so browser apps can call this API
@@ -25,14 +25,14 @@ def base62_to_int(e):
25
  t = t * 62 + BASE_62_CHAR_SET.index(char)
26
  return t
27
 
28
- # Construct the base URL for the shared album
29
  def get_base_url(token: str) -> str:
30
  first_char = token[0]
31
  n = base62_to_int(token[1]) if first_char == "A" else base62_to_int(token[1:3])
32
  base_url = f"https://p{n:02d}-sharedstreams.icloud.com/{token}/sharedstreams/"
33
  return base_url
34
 
35
- # Handle 330 redirect by calling the webstream endpoint with allow_redirects=False
36
  def get_redirected_base_url(base_url: str, token: str) -> str:
37
  url = base_url + "webstream"
38
  headers = {
@@ -47,7 +47,7 @@ def get_redirected_base_url(base_url: str, token: str) -> str:
47
  return new_base_url
48
  return base_url
49
 
50
- # Fetch metadata from the webstream endpoint
51
  def get_metadata(base_url: str):
52
  url = base_url + "webstream"
53
  headers = {
@@ -58,7 +58,7 @@ def get_metadata(base_url: str):
58
  response = requests.post(url, headers=headers, data=data)
59
  return response.json().get("photos", [])
60
 
61
- # Fetch asset URLs for a list of photoGuids from the webasseturls endpoint
62
  def get_asset_urls(base_url: str, guids: list):
63
  url = base_url + "webasseturls"
64
  headers = {
@@ -69,7 +69,7 @@ def get_asset_urls(base_url: str, guids: list):
69
  response = requests.post(url, headers=headers, data=data)
70
  return response.json().get("items", {})
71
 
72
- # Main endpoint to extract videos with extensive fallback checks
73
  @app.get("/album/{token}")
74
  def get_album(token: str):
75
  try:
@@ -80,73 +80,49 @@ def get_album(token: str):
80
  asset_urls = get_asset_urls(base_url, guids)
81
 
82
  video_list = []
83
- # Loop over each photo in the album
84
  for photo in metadata:
85
- best_video = None
86
- best_size = 0
87
-
88
- # Check derivatives first
89
- derivatives = photo.get("derivatives", {})
90
- for key, derivative in derivatives.items():
91
- # Log the derivative keys for debugging
92
- logging.info(f"Photo {photo.get('photoGuid')} derivative key: {key} | content: {list(derivative.keys())}")
93
-
94
- # Standard checks
95
- if (derivative.get("mediaAssetType") == "video" or
96
- derivative.get("type", "").startswith("video") or
97
- derivative.get("filename", "").lower().endswith(".mp4")):
98
  try:
99
  file_size = int(derivative.get("fileSize", 0))
100
  except (ValueError, TypeError):
101
  file_size = 0
 
102
  if file_size > best_size:
103
  best_size = file_size
104
  best_video = derivative
 
 
 
 
 
 
 
 
 
 
 
 
105
  else:
106
- # Fallback: Check if any value in the derivative JSON contains "mp4"
107
- derivative_str = json.dumps(derivative).lower()
108
- if "mp4" in derivative_str:
109
- try:
110
- file_size = int(derivative.get("fileSize", 0))
111
- except (ValueError, TypeError):
112
- file_size = 0
113
- if file_size > best_size:
114
- best_size = file_size
115
- best_video = derivative
116
-
117
- # Fallback to 'movies' field if present
118
- if not best_video and "movies" in photo:
119
- for movie in photo["movies"]:
120
- logging.info(f"Photo {photo.get('photoGuid')} movie entry keys: {list(movie.keys())}")
121
- if movie.get("filename", "").lower().endswith(".mp4"):
122
- try:
123
- file_size = int(movie.get("fileSize", 0))
124
- except (ValueError, TypeError):
125
- file_size = 0
126
- if file_size > best_size:
127
- best_size = file_size
128
- best_video = movie
129
-
130
- # Build the video URL if found
131
- if best_video:
132
- checksum = best_video.get("checksum")
133
- if checksum in asset_urls:
134
- url_info = asset_urls[checksum]
135
- video_url = f"https://{url_info['url_location']}{url_info['url_path']}"
136
- video_list.append({
137
- "caption": photo.get("caption", ""),
138
- "url": video_url
139
- })
140
- else:
141
- logging.info(f"Checksum {checksum} not found in asset URLs for photo {photo.get('photoGuid')}")
142
  else:
143
- logging.info(f"No video derivative found for photo {photo.get('photoGuid')}")
144
 
145
  return {"videos": video_list}
146
  except Exception as e:
147
  return {"error": str(e)}
148
 
149
- # Debug endpoint to return raw metadata from iCloud
150
  @app.get("/album/{token}/raw")
151
  def get_album_raw(token: str):
152
  try:
 
6
 
7
  app = FastAPI()
8
 
9
+ # Set up logging for debugging
10
  logging.basicConfig(level=logging.INFO)
11
 
12
  # Allow all CORS so browser apps can call this API
 
25
  t = t * 62 + BASE_62_CHAR_SET.index(char)
26
  return t
27
 
28
+ # Construct the base URL for the shared album from the token
29
  def get_base_url(token: str) -> str:
30
  first_char = token[0]
31
  n = base62_to_int(token[1]) if first_char == "A" else base62_to_int(token[1:3])
32
  base_url = f"https://p{n:02d}-sharedstreams.icloud.com/{token}/sharedstreams/"
33
  return base_url
34
 
35
+ # Handle 330 redirect from iCloud
36
  def get_redirected_base_url(base_url: str, token: str) -> str:
37
  url = base_url + "webstream"
38
  headers = {
 
47
  return new_base_url
48
  return base_url
49
 
50
+ # Fetch metadata (the "webstream" data)
51
  def get_metadata(base_url: str):
52
  url = base_url + "webstream"
53
  headers = {
 
58
  response = requests.post(url, headers=headers, data=data)
59
  return response.json().get("photos", [])
60
 
61
+ # Fetch asset URLs mapping for a list of photoGuids
62
  def get_asset_urls(base_url: str, guids: list):
63
  url = base_url + "webasseturls"
64
  headers = {
 
69
  response = requests.post(url, headers=headers, data=data)
70
  return response.json().get("items", {})
71
 
72
+ # Main endpoint to extract video URLs
73
  @app.get("/album/{token}")
74
  def get_album(token: str):
75
  try:
 
80
  asset_urls = get_asset_urls(base_url, guids)
81
 
82
  video_list = []
83
+ # Process each photo in the album
84
  for photo in metadata:
85
+ # We check if the overall photo is a video
86
+ if photo.get("mediaAssetType", "").lower() == "video":
87
+ best_video = None
88
+ best_size = 0
89
+ # Look at each derivative
90
+ derivatives = photo.get("derivatives", {})
91
+ for key, derivative in derivatives.items():
92
+ # Skip the poster frame (which is clearly an image)
93
+ if key.lower() == "posterframe":
94
+ continue
95
+ # Try to read the fileSize as integer
 
 
96
  try:
97
  file_size = int(derivative.get("fileSize", 0))
98
  except (ValueError, TypeError):
99
  file_size = 0
100
+ # Choose the derivative with the highest file size
101
  if file_size > best_size:
102
  best_size = file_size
103
  best_video = derivative
104
+ # If we found a candidate and its checksum exists in asset_urls, build the video URL
105
+ if best_video:
106
+ checksum = best_video.get("checksum")
107
+ if checksum in asset_urls:
108
+ url_info = asset_urls[checksum]
109
+ video_url = f"https://{url_info['url_location']}{url_info['url_path']}"
110
+ video_list.append({
111
+ "caption": photo.get("caption", ""),
112
+ "url": video_url
113
+ })
114
+ else:
115
+ logging.info(f"Checksum {checksum} not found in asset_urls for photo {photo.get('photoGuid')}")
116
  else:
117
+ logging.info(f"No video derivative found for photo {photo.get('photoGuid')}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  else:
119
+ logging.info(f"Photo {photo.get('photoGuid')} is not a video. mediaAssetType: {photo.get('mediaAssetType')}")
120
 
121
  return {"videos": video_list}
122
  except Exception as e:
123
  return {"error": str(e)}
124
 
125
+ # Debug endpoint to return raw metadata for further inspection
126
  @app.get("/album/{token}/raw")
127
  def get_album_raw(token: str):
128
  try: