metaai-sdk 2.3.2__py3-none-any.whl → 2.3.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- metaai_api/api_server.py +6 -2
- metaai_api/video_generation.py +769 -0
- {metaai_sdk-2.3.2.dist-info → metaai_sdk-2.3.4.dist-info}/METADATA +1 -1
- {metaai_sdk-2.3.2.dist-info → metaai_sdk-2.3.4.dist-info}/RECORD +7 -6
- {metaai_sdk-2.3.2.dist-info → metaai_sdk-2.3.4.dist-info}/WHEEL +0 -0
- {metaai_sdk-2.3.2.dist-info → metaai_sdk-2.3.4.dist-info}/licenses/LICENSE +0 -0
- {metaai_sdk-2.3.2.dist-info → metaai_sdk-2.3.4.dist-info}/top_level.txt +0 -0
metaai_api/api_server.py
CHANGED
|
@@ -216,8 +216,10 @@ async def chat(body: ChatRequest, cookies: Dict[str, str] = Depends(get_cookies)
|
|
|
216
216
|
async def image(body: ImageRequest, cookies: Dict[str, str] = Depends(get_cookies)) -> Dict[str, Any]:
|
|
217
217
|
ai = MetaAI(cookies=cookies, proxy=_get_proxies())
|
|
218
218
|
try:
|
|
219
|
+
# Automatically prepend "generate image of" to the prompt
|
|
220
|
+
prompt = f"generate image of {body.prompt}" if not body.prompt.lower().startswith(("generate image", "create image")) else body.prompt
|
|
219
221
|
return cast(Dict[str, Any], ai.prompt(
|
|
220
|
-
|
|
222
|
+
prompt,
|
|
221
223
|
stream=False,
|
|
222
224
|
new_conversation=body.new_conversation,
|
|
223
225
|
media_ids=body.media_ids,
|
|
@@ -233,9 +235,11 @@ async def image(body: ImageRequest, cookies: Dict[str, str] = Depends(get_cookie
|
|
|
233
235
|
async def video(body: VideoRequest, cookies: Dict[str, str] = Depends(get_cookies)) -> Dict[str, Any]:
|
|
234
236
|
ai = MetaAI(cookies=cookies, proxy=_get_proxies())
|
|
235
237
|
try:
|
|
238
|
+
# Automatically prepend "generate a video" to the prompt
|
|
239
|
+
prompt = f"generate a video {body.prompt}" if not body.prompt.lower().startswith(("generate a video", "generate video", "create a video", "create video")) else body.prompt
|
|
236
240
|
return await run_in_threadpool(
|
|
237
241
|
ai.generate_video,
|
|
238
|
-
|
|
242
|
+
prompt,
|
|
239
243
|
body.media_ids,
|
|
240
244
|
body.attachment_metadata,
|
|
241
245
|
body.wait_before_poll,
|
|
@@ -0,0 +1,769 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Meta AI Video Generation Module
|
|
3
|
+
Advanced video generation and retrieval using Meta AI's GraphQL API
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
import json
|
|
8
|
+
import time
|
|
9
|
+
import uuid
|
|
10
|
+
from typing import Dict, List, Optional, Any
|
|
11
|
+
from requests_html import HTMLSession
|
|
12
|
+
from metaai_api.utils import extract_value
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class VideoGenerator:
|
|
16
|
+
"""
|
|
17
|
+
A class to handle video generation requests to Meta AI's GraphQL API.
|
|
18
|
+
Supports creating videos from text prompts and retrieving video URLs.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
GRAPHQL_URL = 'https://www.meta.ai/api/graphql/'
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
cookies_str: Optional[str] = None,
|
|
26
|
+
cookies_dict: Optional[Dict[str, str]] = None
|
|
27
|
+
):
|
|
28
|
+
"""
|
|
29
|
+
Initialize the VideoGenerator.
|
|
30
|
+
Automatically fetches lsd and fb_dtsg tokens from Meta AI.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
cookies_str: Cookie string in format "key=value; key=value"
|
|
34
|
+
cookies_dict: Pre-parsed cookies dictionary
|
|
35
|
+
"""
|
|
36
|
+
if cookies_dict:
|
|
37
|
+
self.cookies = cookies_dict
|
|
38
|
+
self.cookies_str = "; ".join([f"{k}={v}" for k, v in cookies_dict.items()])
|
|
39
|
+
elif cookies_str:
|
|
40
|
+
self.cookies = self._parse_cookies(cookies_str)
|
|
41
|
+
self.cookies_str = cookies_str
|
|
42
|
+
else:
|
|
43
|
+
raise ValueError("Either cookies_str or cookies_dict must be provided")
|
|
44
|
+
|
|
45
|
+
# Always auto-fetch tokens
|
|
46
|
+
try:
|
|
47
|
+
tokens = self.get_lsd_and_dtsg(self.cookies_str)
|
|
48
|
+
self.lsd = tokens['lsd']
|
|
49
|
+
self.fb_dtsg = tokens['fb_dtsg']
|
|
50
|
+
except Exception as e:
|
|
51
|
+
raise ValueError(f"Failed to auto-fetch tokens: {e}")
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def _parse_cookies(cookie_str: str) -> Dict[str, str]:
|
|
55
|
+
"""Parse cookie string into dictionary"""
|
|
56
|
+
cookies = {}
|
|
57
|
+
for item in cookie_str.split('; '):
|
|
58
|
+
if '=' in item:
|
|
59
|
+
key, value = item.split('=', 1)
|
|
60
|
+
cookies[key] = value
|
|
61
|
+
return cookies
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def get_lsd_and_dtsg(cookies_str: str) -> Dict[str, str]:
|
|
65
|
+
"""
|
|
66
|
+
Extract lsd and fb_dtsg from Meta AI page using provided cookies.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
cookies_str: Cookie string in format "key1=value1; key2=value2; ..."
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Dictionary with 'lsd' and 'fb_dtsg' keys
|
|
73
|
+
"""
|
|
74
|
+
# Fetch Meta AI page with cookies
|
|
75
|
+
session = HTMLSession()
|
|
76
|
+
headers = {"cookie": cookies_str}
|
|
77
|
+
response = session.get("https://www.meta.ai/", headers=headers)
|
|
78
|
+
|
|
79
|
+
# Extract lsd and fb_dtsg
|
|
80
|
+
lsd = extract_value(response.text, start_str='"LSD",[],{"token":"', end_str='"')
|
|
81
|
+
fb_dtsg = extract_value(response.text, start_str='DTSGInitData",[],{"token":"', end_str='"')
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
"lsd": lsd,
|
|
85
|
+
"fb_dtsg": fb_dtsg
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def quick_generate(
|
|
90
|
+
cls,
|
|
91
|
+
cookies_str: str,
|
|
92
|
+
prompt: str,
|
|
93
|
+
media_ids: Optional[List[str]] = None,
|
|
94
|
+
attachment_metadata: Optional[Dict[str, Any]] = None,
|
|
95
|
+
verbose: bool = True
|
|
96
|
+
) -> Dict:
|
|
97
|
+
"""
|
|
98
|
+
Convenience method to generate a video with minimal setup.
|
|
99
|
+
Automatically fetches tokens and generates video in one call.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
cookies_str: Cookie string in format "key1=value1; key2=value2; ..."
|
|
103
|
+
prompt: Text prompt for video generation
|
|
104
|
+
media_ids: Optional list of media IDs from uploaded images
|
|
105
|
+
attachment_metadata: Optional dict with 'file_size' (int) and 'mime_type' (str)
|
|
106
|
+
verbose: Whether to print status messages
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Dictionary with success status, conversation_id, prompt, video_urls, and timestamp
|
|
110
|
+
|
|
111
|
+
Example:
|
|
112
|
+
result = VideoGenerator.quick_generate(
|
|
113
|
+
cookies_str="datr=...; abra_sess=...",
|
|
114
|
+
prompt="Generate a video of a sunset",
|
|
115
|
+
media_ids=["1234567890"],
|
|
116
|
+
attachment_metadata={'file_size': 3310, 'mime_type': 'image/jpeg'}
|
|
117
|
+
)
|
|
118
|
+
"""
|
|
119
|
+
generator = cls(cookies_str=cookies_str)
|
|
120
|
+
return generator.generate_video(prompt=prompt, media_ids=media_ids, attachment_metadata=attachment_metadata, verbose=verbose)
|
|
121
|
+
|
|
122
|
+
def build_headers(
|
|
123
|
+
self,
|
|
124
|
+
content_type: str = 'application/x-www-form-urlencoded',
|
|
125
|
+
friendly_name: Optional[str] = None,
|
|
126
|
+
additional_headers: Optional[Dict[str, str]] = None
|
|
127
|
+
) -> Dict[str, str]:
|
|
128
|
+
"""
|
|
129
|
+
Build dynamic headers for Meta AI API requests.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
content_type: Content-Type header value
|
|
133
|
+
friendly_name: Optional X-FB-Friendly-Name for the request
|
|
134
|
+
additional_headers: Optional dict of additional headers to merge
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Complete headers dictionary
|
|
138
|
+
"""
|
|
139
|
+
# Base headers common to all requests
|
|
140
|
+
headers = {
|
|
141
|
+
'accept': '*/*',
|
|
142
|
+
'accept-language': 'en-US,en;q=0.5',
|
|
143
|
+
'content-type': content_type,
|
|
144
|
+
'origin': 'https://www.meta.ai',
|
|
145
|
+
'referer': 'https://www.meta.ai/',
|
|
146
|
+
'sec-ch-ua': '"Brave";v="141", "Not?A_Brand";v="8", "Chromium";v="141"',
|
|
147
|
+
'sec-ch-ua-mobile': '?0',
|
|
148
|
+
'sec-ch-ua-platform': '"Windows"',
|
|
149
|
+
'sec-fetch-dest': 'empty',
|
|
150
|
+
'sec-fetch-mode': 'cors',
|
|
151
|
+
'sec-fetch-site': 'same-origin',
|
|
152
|
+
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36',
|
|
153
|
+
'x-asbd-id': '359341',
|
|
154
|
+
'x-fb-lsd': self.lsd,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# Add optional friendly name
|
|
158
|
+
if friendly_name:
|
|
159
|
+
headers['x-fb-friendly-name'] = friendly_name
|
|
160
|
+
|
|
161
|
+
# Add additional headers specific to request type
|
|
162
|
+
if additional_headers:
|
|
163
|
+
headers.update(additional_headers)
|
|
164
|
+
|
|
165
|
+
return headers
|
|
166
|
+
|
|
167
|
+
def create_video_generation_request(
|
|
168
|
+
self,
|
|
169
|
+
prompt_text: str,
|
|
170
|
+
media_ids: Optional[List[str]] = None,
|
|
171
|
+
attachment_metadata: Optional[Dict[str, Any]] = None,
|
|
172
|
+
verbose: bool = True
|
|
173
|
+
) -> Optional[str]:
|
|
174
|
+
"""
|
|
175
|
+
Send video generation request to Meta AI using raw multipart body.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
prompt_text: The text prompt for video generation
|
|
179
|
+
media_ids: Optional list of media IDs from uploaded images
|
|
180
|
+
attachment_metadata: Optional dict with 'file_size' (int) and 'mime_type' (str)
|
|
181
|
+
verbose: Whether to print status messages
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
external_conversation_id if successful, None otherwise
|
|
185
|
+
"""
|
|
186
|
+
# Generate unique IDs
|
|
187
|
+
external_conversation_id = str(uuid.uuid4())
|
|
188
|
+
offline_threading_id = str(int(time.time() * 1000000000))[:19]
|
|
189
|
+
thread_session_id = str(uuid.uuid4())
|
|
190
|
+
bot_offline_threading_id = str(int(time.time() * 1000000000) + 1)[:19]
|
|
191
|
+
qpl_join_id = str(uuid.uuid4()).replace('-', '')
|
|
192
|
+
|
|
193
|
+
# Build headers with multipart-specific additions
|
|
194
|
+
multipart_headers = {
|
|
195
|
+
'priority': 'u=1, i',
|
|
196
|
+
'sec-ch-ua-full-version-list': '"Brave";v="141.0.0.0", "Not?A_Brand";v="8.0.0.0", "Chromium";v="141.0.0.0"',
|
|
197
|
+
'sec-ch-ua-model': '""',
|
|
198
|
+
'sec-ch-ua-platform-version': '"19.0.0"',
|
|
199
|
+
'sec-gpc': '1',
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
headers = self.build_headers(
|
|
203
|
+
content_type='multipart/form-data; boundary=----WebKitFormBoundaryu59CeaZS4ag939lz',
|
|
204
|
+
additional_headers=multipart_headers
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Build variables JSON (compact, no extra spaces)
|
|
208
|
+
variables = json.dumps({
|
|
209
|
+
"message": {"sensitive_string_value": prompt_text},
|
|
210
|
+
"externalConversationId": external_conversation_id,
|
|
211
|
+
"offlineThreadingId": offline_threading_id,
|
|
212
|
+
"threadSessionId": thread_session_id,
|
|
213
|
+
"isNewConversation": True,
|
|
214
|
+
"suggestedPromptIndex": None,
|
|
215
|
+
"promptPrefix": None,
|
|
216
|
+
"entrypoint": "KADABRA__CHAT__UNIFIED_INPUT_BAR",
|
|
217
|
+
"attachments": [],
|
|
218
|
+
"attachmentsV2": media_ids if media_ids else [],
|
|
219
|
+
"activeMediaSets": [],
|
|
220
|
+
"activeCardVersions": [],
|
|
221
|
+
"activeArtifactVersion": None,
|
|
222
|
+
"userUploadEditModeInput": None,
|
|
223
|
+
"reelComposeInput": None,
|
|
224
|
+
"qplJoinId": qpl_join_id,
|
|
225
|
+
"sourceRemixPostId": None,
|
|
226
|
+
"gkPlannerOrReasoningEnabled": True,
|
|
227
|
+
"selectedModel": "BASIC_OPTION",
|
|
228
|
+
"conversationMode": None,
|
|
229
|
+
"selectedAgentType": "PLANNER",
|
|
230
|
+
"conversationStarterId": None,
|
|
231
|
+
"promptType": None,
|
|
232
|
+
"artifactRewriteOptions": None,
|
|
233
|
+
"imagineOperationRequest": None,
|
|
234
|
+
"imagineClientOptions": {"orientation": "VERTICAL"},
|
|
235
|
+
"spaceId": None,
|
|
236
|
+
"sparkSnapshotId": None,
|
|
237
|
+
"topicPageId": None,
|
|
238
|
+
"includeSpace": False,
|
|
239
|
+
"storybookId": None,
|
|
240
|
+
"messagePersistentInput": {
|
|
241
|
+
"attachment_size": attachment_metadata.get('file_size') if attachment_metadata else None,
|
|
242
|
+
"attachment_type": attachment_metadata.get('mime_type') if attachment_metadata else None,
|
|
243
|
+
"bot_message_offline_threading_id": bot_offline_threading_id,
|
|
244
|
+
"conversation_mode": None,
|
|
245
|
+
"external_conversation_id": external_conversation_id,
|
|
246
|
+
"is_new_conversation": True,
|
|
247
|
+
"meta_ai_entry_point": "KADABRA__CHAT__UNIFIED_INPUT_BAR",
|
|
248
|
+
"offline_threading_id": offline_threading_id,
|
|
249
|
+
"prompt_id": None,
|
|
250
|
+
"prompt_session_id": thread_session_id
|
|
251
|
+
},
|
|
252
|
+
"alakazam_enabled": True,
|
|
253
|
+
"skipInFlightMessageWithParams": None,
|
|
254
|
+
"__relay_internal__pv__KadabraSocialSearchEnabledrelayprovider": False,
|
|
255
|
+
"__relay_internal__pv__KadabraZeitgeistEnabledrelayprovider": False,
|
|
256
|
+
"__relay_internal__pv__alakazam_enabledrelayprovider": True,
|
|
257
|
+
"__relay_internal__pv__sp_kadabra_survey_invitationrelayprovider": True,
|
|
258
|
+
"__relay_internal__pv__KadabraAINativeUXrelayprovider": False,
|
|
259
|
+
"__relay_internal__pv__enable_kadabra_partial_resultsrelayprovider": False,
|
|
260
|
+
"__relay_internal__pv__AbraArtifactsEnabledrelayprovider": True,
|
|
261
|
+
"__relay_internal__pv__KadabraMemoryEnabledrelayprovider": False,
|
|
262
|
+
"__relay_internal__pv__AbraPlannerEnabledrelayprovider": True,
|
|
263
|
+
"__relay_internal__pv__AbraWidgetsEnabledrelayprovider": False,
|
|
264
|
+
"__relay_internal__pv__KadabraDeepResearchEnabledrelayprovider": False,
|
|
265
|
+
"__relay_internal__pv__KadabraThinkHarderEnabledrelayprovider": False,
|
|
266
|
+
"__relay_internal__pv__KadabraVergeEnabledrelayprovider": False,
|
|
267
|
+
"__relay_internal__pv__KadabraSpacesEnabledrelayprovider": False,
|
|
268
|
+
"__relay_internal__pv__KadabraProductSearchEnabledrelayprovider": False,
|
|
269
|
+
"__relay_internal__pv__KadabraAreServiceEnabledrelayprovider": False,
|
|
270
|
+
"__relay_internal__pv__kadabra_render_reasoning_response_statesrelayprovider": True,
|
|
271
|
+
"__relay_internal__pv__kadabra_reasoning_cotrelayprovider": False,
|
|
272
|
+
"__relay_internal__pv__AbraSearchInlineReferencesEnabledrelayprovider": True,
|
|
273
|
+
"__relay_internal__pv__AbraComposedTextWidgetsrelayprovider": True,
|
|
274
|
+
"__relay_internal__pv__KadabraNewCitationsEnabledrelayprovider": True,
|
|
275
|
+
"__relay_internal__pv__WebPixelRatiorelayprovider": 1,
|
|
276
|
+
"__relay_internal__pv__KadabraVideoDeliveryRequestrelayprovider": {
|
|
277
|
+
"dash_manifest_requests": [{}],
|
|
278
|
+
"progressive_url_requests": [{"quality": "HD"}, {"quality": "SD"}]
|
|
279
|
+
},
|
|
280
|
+
"__relay_internal__pv__KadabraWidgetsRedesignEnabledrelayprovider": False,
|
|
281
|
+
"__relay_internal__pv__kadabra_enable_send_message_retryrelayprovider": True,
|
|
282
|
+
"__relay_internal__pv__KadabraEmailCalendarIntegrationrelayprovider": False,
|
|
283
|
+
"__relay_internal__pv__kadabra_reels_connect_featuresrelayprovider": False,
|
|
284
|
+
"__relay_internal__pv__AbraBugNubrelayprovider": False,
|
|
285
|
+
"__relay_internal__pv__AbraRedteamingrelayprovider": False,
|
|
286
|
+
"__relay_internal__pv__AbraDebugDevOnlyrelayprovider": False,
|
|
287
|
+
"__relay_internal__pv__kadabra_enable_open_in_editor_message_actionrelayprovider": True,
|
|
288
|
+
"__relay_internal__pv__AbraThreadsEnabledrelayprovider": False,
|
|
289
|
+
"__relay_internal__pv__kadabra_story_builder_enabledrelayprovider": False,
|
|
290
|
+
"__relay_internal__pv__kadabra_imagine_canvas_enable_dev_settingsrelayprovider": False,
|
|
291
|
+
"__relay_internal__pv__kadabra_create_media_deletionrelayprovider": False,
|
|
292
|
+
"__relay_internal__pv__kadabra_moodboardrelayprovider": False,
|
|
293
|
+
"__relay_internal__pv__AbraArtifactDragImagineFromConversationrelayprovider": True,
|
|
294
|
+
"__relay_internal__pv__kadabra_media_item_renderer_heightrelayprovider": 545,
|
|
295
|
+
"__relay_internal__pv__kadabra_media_item_renderer_widthrelayprovider": 620,
|
|
296
|
+
"__relay_internal__pv__AbraQPDocUploadNuxTriggerNamerelayprovider": "meta_dot_ai_abra_web_doc_upload_nux_tour",
|
|
297
|
+
"__relay_internal__pv__AbraSurfaceNuxIDrelayprovider": "12177",
|
|
298
|
+
"__relay_internal__pv__KadabraConversationRenamingrelayprovider": True,
|
|
299
|
+
"__relay_internal__pv__AbraIsLoggedOutrelayprovider": False,
|
|
300
|
+
"__relay_internal__pv__KadabraCanvasDisplayHeaderV2relayprovider": False,
|
|
301
|
+
"__relay_internal__pv__AbraArtifactEditorDebugModerelayprovider": False,
|
|
302
|
+
"__relay_internal__pv__AbraArtifactEditorDownloadHTMLEnabledrelayprovider": False,
|
|
303
|
+
"__relay_internal__pv__kadabra_create_row_hover_optionsrelayprovider": False,
|
|
304
|
+
"__relay_internal__pv__kadabra_media_info_pillsrelayprovider": True,
|
|
305
|
+
"__relay_internal__pv__KadabraConcordInternalProfileBadgeEnabledrelayprovider": False,
|
|
306
|
+
"__relay_internal__pv__KadabraSocialGraphrelayprovider": True
|
|
307
|
+
}, separators=(',', ':'))
|
|
308
|
+
|
|
309
|
+
print(variables)
|
|
310
|
+
|
|
311
|
+
# Build raw multipart body (exactly as in working example)
|
|
312
|
+
spin_t = str(int(time.time()))
|
|
313
|
+
body = f"""------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
314
|
+
Content-Disposition: form-data; name="av"\r
|
|
315
|
+
\r
|
|
316
|
+
813590375178585\r
|
|
317
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
318
|
+
Content-Disposition: form-data; name="__user"\r
|
|
319
|
+
\r
|
|
320
|
+
0\r
|
|
321
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
322
|
+
Content-Disposition: form-data; name="__a"\r
|
|
323
|
+
\r
|
|
324
|
+
1\r
|
|
325
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
326
|
+
Content-Disposition: form-data; name="__req"\r
|
|
327
|
+
\r
|
|
328
|
+
q\r
|
|
329
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
330
|
+
Content-Disposition: form-data; name="__hs"\r
|
|
331
|
+
\r
|
|
332
|
+
20413.HYP:kadabra_pkg.2.1...0\r
|
|
333
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
334
|
+
Content-Disposition: form-data; name="dpr"\r
|
|
335
|
+
\r
|
|
336
|
+
1\r
|
|
337
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
338
|
+
Content-Disposition: form-data; name="__ccg"\r
|
|
339
|
+
\r
|
|
340
|
+
GOOD\r
|
|
341
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
342
|
+
Content-Disposition: form-data; name="__rev"\r
|
|
343
|
+
\r
|
|
344
|
+
1030219547\r
|
|
345
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
346
|
+
Content-Disposition: form-data; name="__s"\r
|
|
347
|
+
\r
|
|
348
|
+
q59jx4:9bnqdw:3ats33\r
|
|
349
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
350
|
+
Content-Disposition: form-data; name="__hsi"\r
|
|
351
|
+
\r
|
|
352
|
+
7575127759957881428\r
|
|
353
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
354
|
+
Content-Disposition: form-data; name="__dyn"\r
|
|
355
|
+
\r
|
|
356
|
+
7xeUjG1mxu1syUqxemh0no6u5U4e2C1vzEdE98K360CEbo1nEhw2nVEtwMw6ywaq221FwpUO0n24oaEnxO0Bo7O2l0Fwqo31w9O1lwlE-U2zxe2GewbS361qw82dUlwhE-15wmo423-0j52oS0Io5d0bS1LBwNwKG0WE8oC1IwGw-wlUcE2-G2O7E5y1rwa211wo84y1iwfe1aw\r
|
|
357
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
358
|
+
Content-Disposition: form-data; name="__csr"\r
|
|
359
|
+
\r
|
|
360
|
+
gaJNBjWsAJvliQPqlWFFknAiUB2bBjWLmhyblepaGyVFGy8y2i5pEW68mwwwPwxgtNgv2AMEu6PAgrCwc7F212xxe5YyVC1pAg01sq99uQ1zK0dp75gKzAy8y0EjcgQ8Ek0yMJC6G1og5KrXD4GexS8wdasU8U1e4075UeEuwfCA8K0hWiU2tAyE5m0gm0Jo0xUGxh1veU0gGyWfe0iK1xo32yXhoKkw56pwMw1e25onU4i0TA0xaxu00B1Q2ha2K3V0eqCmawnEgg2Gw\r
|
|
361
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
362
|
+
Content-Disposition: form-data; name="__hsdp"\r
|
|
363
|
+
\r
|
|
364
|
+
gdDdNhMlJ8bNG7i42aHgWzckH57ylAt8NkkOGCVQ8Ay8myETxW1vh48gHx-UC9Bgpy87G0BUfU7i0JFUeo7Cm12wlo5OawRwDwzxW1zg33wgodU\r
|
|
365
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
366
|
+
Content-Disposition: form-data; name="__hblp"\r
|
|
367
|
+
\r
|
|
368
|
+
08W5EWt0BzUWp5Q4vz4HOk5kVogDGqmHgyi8xq9gNrxG1vh8B2K6pry4mVk8x28wuE5a1DxO1Qwr84Cu3C1VBwCxK2W2qi2y1LwDwzyK445Gwi63-0wUkxa9AyEjgogy3-\r
|
|
369
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
370
|
+
Content-Disposition: form-data; name="__sjsp"\r
|
|
371
|
+
\r
|
|
372
|
+
gdDtsAFMlJ8bNG7i47AG5lxmUmDiFQca9U\r
|
|
373
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
374
|
+
Content-Disposition: form-data; name="__comet_req"\r
|
|
375
|
+
\r
|
|
376
|
+
72\r
|
|
377
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
378
|
+
Content-Disposition: form-data; name="fb_dtsg"\r
|
|
379
|
+
\r
|
|
380
|
+
{self.fb_dtsg}\r
|
|
381
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
382
|
+
Content-Disposition: form-data; name="jazoest"\r
|
|
383
|
+
\r
|
|
384
|
+
25499\r
|
|
385
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
386
|
+
Content-Disposition: form-data; name="lsd"\r
|
|
387
|
+
\r
|
|
388
|
+
{self.lsd}\r
|
|
389
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
390
|
+
Content-Disposition: form-data; name="__spin_r"\r
|
|
391
|
+
\r
|
|
392
|
+
1030219547\r
|
|
393
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
394
|
+
Content-Disposition: form-data; name="__spin_b"\r
|
|
395
|
+
\r
|
|
396
|
+
trunk\r
|
|
397
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
398
|
+
Content-Disposition: form-data; name="__spin_t"\r
|
|
399
|
+
\r
|
|
400
|
+
{spin_t}\r
|
|
401
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
402
|
+
Content-Disposition: form-data; name="__jssesw"\r
|
|
403
|
+
\r
|
|
404
|
+
1\r
|
|
405
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
406
|
+
Content-Disposition: form-data; name="__crn"\r
|
|
407
|
+
\r
|
|
408
|
+
comet.kadabra.KadabraAssistantRoute\r
|
|
409
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
410
|
+
Content-Disposition: form-data; name="fb_api_caller_class"\r
|
|
411
|
+
\r
|
|
412
|
+
RelayModern\r
|
|
413
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
414
|
+
Content-Disposition: form-data; name="fb_api_req_friendly_name"\r
|
|
415
|
+
\r
|
|
416
|
+
useKadabraSendMessageMutation\r
|
|
417
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
418
|
+
Content-Disposition: form-data; name="server_timestamps"\r
|
|
419
|
+
\r
|
|
420
|
+
true\r
|
|
421
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
422
|
+
Content-Disposition: form-data; name="variables"\r
|
|
423
|
+
\r
|
|
424
|
+
{variables}\r
|
|
425
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz\r
|
|
426
|
+
Content-Disposition: form-data; name="doc_id"\r
|
|
427
|
+
\r
|
|
428
|
+
25290947477183545\r
|
|
429
|
+
------WebKitFormBoundaryu59CeaZS4ag939lz--\r
|
|
430
|
+
"""
|
|
431
|
+
|
|
432
|
+
# URL with query parameters
|
|
433
|
+
url = f"{self.GRAPHQL_URL}?fb_dtsg={self.fb_dtsg}&jazoest=25499&lsd={self.lsd}"
|
|
434
|
+
|
|
435
|
+
try:
|
|
436
|
+
response = requests.post(
|
|
437
|
+
url,
|
|
438
|
+
cookies=self.cookies,
|
|
439
|
+
headers=headers,
|
|
440
|
+
data=body.encode('utf-8'),
|
|
441
|
+
timeout=30
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
if response.status_code == 200:
|
|
445
|
+
return external_conversation_id
|
|
446
|
+
else:
|
|
447
|
+
return None
|
|
448
|
+
|
|
449
|
+
except Exception as e:
|
|
450
|
+
return None
|
|
451
|
+
|
|
452
|
+
def fetch_video_urls(
|
|
453
|
+
self,
|
|
454
|
+
conversation_id: str,
|
|
455
|
+
max_attempts: int = 30,
|
|
456
|
+
wait_seconds: int = 5,
|
|
457
|
+
verbose: bool = True
|
|
458
|
+
) -> List[str]:
|
|
459
|
+
"""
|
|
460
|
+
Poll for video URLs from a conversation.
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
conversation_id: The conversation ID to fetch videos from
|
|
464
|
+
max_attempts: Maximum number of polling attempts
|
|
465
|
+
wait_seconds: Seconds to wait between attempts
|
|
466
|
+
verbose: Whether to print status messages
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
List of video URLs
|
|
470
|
+
"""
|
|
471
|
+
# Build headers with query-specific friendly name
|
|
472
|
+
headers = self.build_headers(
|
|
473
|
+
content_type='application/x-www-form-urlencoded',
|
|
474
|
+
friendly_name='KadabraPromptRootQuery'
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
# Build variables
|
|
478
|
+
variables = {
|
|
479
|
+
"prompt_id": conversation_id,
|
|
480
|
+
"__relay_internal__pv__kadabra_voice_consumptionrelayprovider": False,
|
|
481
|
+
"__relay_internal__pv__AbraIsLoggedOutrelayprovider": False,
|
|
482
|
+
"__relay_internal__pv__KadabraConversationRenamingrelayprovider": True,
|
|
483
|
+
"__relay_internal__pv__KadabraSpacesEnabledrelayprovider": False,
|
|
484
|
+
"__relay_internal__pv__KadabraRecipesEnabledrelayprovider": False,
|
|
485
|
+
"__relay_internal__pv__KadabraFOASharingEnabledrelayprovider": True,
|
|
486
|
+
"__relay_internal__pv__KadabraFeedImageDimensionrelayprovider": 800,
|
|
487
|
+
"__relay_internal__pv__kadabra_story_builder_enabledrelayprovider": False,
|
|
488
|
+
"__relay_internal__pv__kadabra_imagine_canvas_enable_dev_settingsrelayprovider": False,
|
|
489
|
+
"__relay_internal__pv__enable_kadabra_partial_resultsrelayprovider": False,
|
|
490
|
+
"__relay_internal__pv__kadabra_create_media_deletionrelayprovider": False,
|
|
491
|
+
"__relay_internal__pv__kadabra_moodboardrelayprovider": False,
|
|
492
|
+
"__relay_internal__pv__KadabraVideoDeliveryRequestrelayprovider": {
|
|
493
|
+
"dash_manifest_requests": [{}],
|
|
494
|
+
"progressive_url_requests": [{"quality": "HD"}, {"quality": "SD"}]
|
|
495
|
+
},
|
|
496
|
+
"__relay_internal__pv__AbraSearchInlineReferencesEnabledrelayprovider": True,
|
|
497
|
+
"__relay_internal__pv__AbraComposedTextWidgetsrelayprovider": True,
|
|
498
|
+
"__relay_internal__pv__KadabraNewCitationsEnabledrelayprovider": True,
|
|
499
|
+
"__relay_internal__pv__WebPixelRatiorelayprovider": 1,
|
|
500
|
+
"__relay_internal__pv__KadabraWidgetsRedesignEnabledrelayprovider": False,
|
|
501
|
+
"__relay_internal__pv__AbraArtifactDragImagineFromConversationrelayprovider": True,
|
|
502
|
+
"__relay_internal__pv__kadabra_media_item_renderer_heightrelayprovider": 545,
|
|
503
|
+
"__relay_internal__pv__kadabra_media_item_renderer_widthrelayprovider": 620,
|
|
504
|
+
"__relay_internal__pv__AbraBugNubrelayprovider": False,
|
|
505
|
+
"__relay_internal__pv__AbraDebugDevOnlyrelayprovider": False,
|
|
506
|
+
"__relay_internal__pv__abra_silverstone_enable_hidden_commentsrelayprovider": True,
|
|
507
|
+
"__relay_internal__pv__kadabra_voicerelayprovider": True,
|
|
508
|
+
"__relay_internal__pv__KadabraSocialSearchEnabledrelayprovider": False,
|
|
509
|
+
"__relay_internal__pv__KadabraZeitgeistEnabledrelayprovider": False,
|
|
510
|
+
"__relay_internal__pv__alakazam_enabledrelayprovider": True,
|
|
511
|
+
"__relay_internal__pv__sp_kadabra_survey_invitationrelayprovider": True,
|
|
512
|
+
"__relay_internal__pv__KadabraAINativeUXrelayprovider": False,
|
|
513
|
+
"__relay_internal__pv__AbraArtifactsEnabledrelayprovider": True,
|
|
514
|
+
"__relay_internal__pv__KadabraMemoryEnabledrelayprovider": False,
|
|
515
|
+
"__relay_internal__pv__AbraPlannerEnabledrelayprovider": True,
|
|
516
|
+
"__relay_internal__pv__AbraWidgetsEnabledrelayprovider": False,
|
|
517
|
+
"__relay_internal__pv__KadabraDeepResearchEnabledrelayprovider": False,
|
|
518
|
+
"__relay_internal__pv__KadabraThinkHarderEnabledrelayprovider": False,
|
|
519
|
+
"__relay_internal__pv__KadabraVergeEnabledrelayprovider": False,
|
|
520
|
+
"__relay_internal__pv__KadabraProductSearchEnabledrelayprovider": False,
|
|
521
|
+
"__relay_internal__pv__KadabraAreServiceEnabledrelayprovider": False,
|
|
522
|
+
"__relay_internal__pv__kadabra_render_reasoning_response_statesrelayprovider": True,
|
|
523
|
+
"__relay_internal__pv__kadabra_reasoning_cotrelayprovider": False,
|
|
524
|
+
"__relay_internal__pv__kadabra_enable_send_message_retryrelayprovider": True,
|
|
525
|
+
"__relay_internal__pv__KadabraEmailCalendarIntegrationrelayprovider": False,
|
|
526
|
+
"__relay_internal__pv__kadabra_reels_connect_featuresrelayprovider": False,
|
|
527
|
+
"__relay_internal__pv__AbraRedteamingrelayprovider": False,
|
|
528
|
+
"__relay_internal__pv__kadabra_enable_open_in_editor_message_actionrelayprovider": True,
|
|
529
|
+
"__relay_internal__pv__AbraThreadsEnabledrelayprovider": False,
|
|
530
|
+
"__relay_internal__pv__AbraQPDocUploadNuxTriggerNamerelayprovider": "meta_dot_ai_abra_web_doc_upload_nux_tour",
|
|
531
|
+
"__relay_internal__pv__AbraSurfaceNuxIDrelayprovider": "12177"
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
# Build data payload
|
|
535
|
+
data = {
|
|
536
|
+
'av': '813590375178585',
|
|
537
|
+
'__user': '0',
|
|
538
|
+
'__a': '1',
|
|
539
|
+
'__req': 's',
|
|
540
|
+
'__hs': '20413.HYP:kadabra_pkg.2.1...0',
|
|
541
|
+
'dpr': '1',
|
|
542
|
+
'__ccg': 'GOOD',
|
|
543
|
+
'__rev': '1030219547',
|
|
544
|
+
'__s': 'q59jx4:9bnqdw:3ats33',
|
|
545
|
+
'__hsi': '7575127759957881428',
|
|
546
|
+
'__dyn': '7xeUjG1mxu1syUqxemh0no6u5U4e2C1vzEdE98K360CEbo1nEhw2nVEtwMw6ywaq221FwpUO0n24oaEnxO0Bo7O2l0Fwqo31w9O1lwlE-U2zxe2GewbS361qw82dUlwhE-15wmo423-0j52oS0Io5d0bS1LBwNwKG0WE8oC1IwGw-wlUcE2-G2O7E5y1rwa211wo84y1iwfe1aw',
|
|
547
|
+
'__csr': 'gaJNBjWsAJvliQPqlWFFknAiUB2bBjWLmhyblepaGyVFGy8y2i5pEW68mwwwPwxgtNgv2AMEu6PAgrCwc7F212xxe5YyVC1pAg01sq99uQ1zK0dp75gKzAy8y0EjcgQ8Ek0yMJC6G1og5KrXD4GexS8wdasU8U1e4075UeEuwfCA8K0hWiU2tAyE5m0gm0Jo0xUGxh1veU0gGyWfe0iK1xo32yXhoKkw56pwMw1e25onU4i0TA0xaxu00B1Q2ha2K3V0eqCmawnEgg2Gw',
|
|
548
|
+
'__hsdp': 'gdDdNhMlJ8bNG7i42aHgWzckH57ylAt8NkkOGCVQ8Ay8myETxW1vh48gHx-UC9Bgpy87G0BUfU7i0JFUeo7Cm12wlo5OawRwDwzxW1zg33wgodU',
|
|
549
|
+
'__hblp': '08W5EWt0BzUWp5Q4vz4HOk5kVogDGqmHgyi8xq9gNrxG1vh8B2K6pry4mVk8x28wuE5a1DxO1Qwr84Cu3C1VBwCxK2W2qi2y1LwDwzyK445Gwi63-0wUkxa9AyEjgogy3-',
|
|
550
|
+
'__sjsp': 'gdDtsAFMlJ8bNG7i47AG5lxmUmDiFQca9U',
|
|
551
|
+
'__comet_req': '72',
|
|
552
|
+
'fb_dtsg': self.fb_dtsg,
|
|
553
|
+
'jazoest': '25499',
|
|
554
|
+
'lsd': self.lsd,
|
|
555
|
+
'__spin_r': '1030219547',
|
|
556
|
+
'__spin_b': 'trunk',
|
|
557
|
+
'__spin_t': str(int(time.time())),
|
|
558
|
+
'__jssesw': '1',
|
|
559
|
+
'__crn': 'comet.kadabra.KadabraAssistantRoute',
|
|
560
|
+
'fb_api_caller_class': 'RelayModern',
|
|
561
|
+
'fb_api_req_friendly_name': 'KadabraPromptRootQuery',
|
|
562
|
+
'server_timestamps': 'true',
|
|
563
|
+
'variables': json.dumps(variables),
|
|
564
|
+
'doc_id': '25290569913909283',
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
for attempt in range(1, max_attempts + 1):
|
|
568
|
+
try:
|
|
569
|
+
response = requests.post(
|
|
570
|
+
self.GRAPHQL_URL,
|
|
571
|
+
cookies=self.cookies,
|
|
572
|
+
headers=headers,
|
|
573
|
+
data=data,
|
|
574
|
+
timeout=30
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
if response.status_code == 200:
|
|
578
|
+
# Extract video URLs from response
|
|
579
|
+
video_urls = self._extract_video_urls_from_response(response.text)
|
|
580
|
+
|
|
581
|
+
if video_urls:
|
|
582
|
+
return video_urls
|
|
583
|
+
else:
|
|
584
|
+
time.sleep(wait_seconds)
|
|
585
|
+
else:
|
|
586
|
+
time.sleep(wait_seconds)
|
|
587
|
+
|
|
588
|
+
except Exception as e:
|
|
589
|
+
time.sleep(wait_seconds)
|
|
590
|
+
|
|
591
|
+
return []
|
|
592
|
+
|
|
593
|
+
@staticmethod
|
|
594
|
+
def _extract_video_urls_from_response(response_text: str) -> List[str]:
|
|
595
|
+
"""
|
|
596
|
+
Extract video URLs from Meta AI GraphQL response.
|
|
597
|
+
Uses the CORRECT structure from the original GitHub repo.
|
|
598
|
+
|
|
599
|
+
Args:
|
|
600
|
+
response_text: The response text to extract URLs from
|
|
601
|
+
|
|
602
|
+
Returns:
|
|
603
|
+
List of video URLs
|
|
604
|
+
"""
|
|
605
|
+
import logging
|
|
606
|
+
logger = logging.getLogger(__name__)
|
|
607
|
+
|
|
608
|
+
logger.info("[VIDEO URL EXTRACTION] Starting extraction with proper structure...")
|
|
609
|
+
logger.debug(f"[VIDEO URL EXTRACTION] Response length: {len(response_text)} characters")
|
|
610
|
+
|
|
611
|
+
urls: List[str] = []
|
|
612
|
+
|
|
613
|
+
try:
|
|
614
|
+
# Parse the response
|
|
615
|
+
data = json.loads(response_text)
|
|
616
|
+
logger.info("[VIDEO URL EXTRACTION] Successfully parsed response as JSON")
|
|
617
|
+
|
|
618
|
+
# CORRECT STRUCTURE from original GitHub code:
|
|
619
|
+
# data -> xfb_genai_fetch_post (or xab_abra__xfb_genai_fetch_post)
|
|
620
|
+
# -> messages -> edges -> node -> content -> imagine_video
|
|
621
|
+
|
|
622
|
+
data_obj = data.get("data", {})
|
|
623
|
+
fetch_post = data_obj.get("xfb_genai_fetch_post") or data_obj.get("xab_abra__xfb_genai_fetch_post") or {}
|
|
624
|
+
|
|
625
|
+
if not fetch_post:
|
|
626
|
+
logger.warning("[VIDEO URL EXTRACTION] No xfb_genai_fetch_post or xab_abra__xfb_genai_fetch_post found in response")
|
|
627
|
+
else:
|
|
628
|
+
logger.info(f"[VIDEO URL EXTRACTION] Found fetch_post: {list(fetch_post.keys())}")
|
|
629
|
+
|
|
630
|
+
messages = fetch_post.get("messages", {}).get("edges", [])
|
|
631
|
+
logger.info(f"[VIDEO URL EXTRACTION] Found {len(messages)} message edges")
|
|
632
|
+
|
|
633
|
+
for edge_idx, edge in enumerate(messages):
|
|
634
|
+
node = edge.get("node", {})
|
|
635
|
+
content = node.get("content", {})
|
|
636
|
+
imagine_video = content.get("imagine_video") or {}
|
|
637
|
+
|
|
638
|
+
if not imagine_video:
|
|
639
|
+
logger.debug(f"[VIDEO URL EXTRACTION] Edge {edge_idx}: No imagine_video found")
|
|
640
|
+
continue
|
|
641
|
+
|
|
642
|
+
logger.info(f"[VIDEO URL EXTRACTION] Edge {edge_idx}: Found imagine_video with keys: {list(imagine_video.keys())}")
|
|
643
|
+
|
|
644
|
+
# Extract from videos.nodes[] array
|
|
645
|
+
videos = imagine_video.get("videos", {}).get("nodes", [])
|
|
646
|
+
logger.info(f"[VIDEO URL EXTRACTION] Edge {edge_idx}: Found {len(videos)} video nodes")
|
|
647
|
+
|
|
648
|
+
for video_idx, video in enumerate(videos):
|
|
649
|
+
# Try video_url or uri
|
|
650
|
+
uri = video.get("video_url") or video.get("uri")
|
|
651
|
+
if uri:
|
|
652
|
+
logger.info(f"[VIDEO URL EXTRACTION] Found video_url/uri in videos.nodes[{video_idx}]: {uri[:100]}...")
|
|
653
|
+
urls.append(uri)
|
|
654
|
+
|
|
655
|
+
# Try videoDeliveryResponseResult.progressive_urls[]
|
|
656
|
+
delivery = video.get("videoDeliveryResponseResult") or {}
|
|
657
|
+
prog = delivery.get("progressive_urls", [])
|
|
658
|
+
logger.info(f"[VIDEO URL EXTRACTION] Found {len(prog)} progressive_urls in video {video_idx}")
|
|
659
|
+
|
|
660
|
+
for prog_idx, p in enumerate(prog):
|
|
661
|
+
pu = p.get("progressive_url")
|
|
662
|
+
if pu:
|
|
663
|
+
logger.info(f"[VIDEO URL EXTRACTION] Found progressive_url[{prog_idx}]: {pu[:100]}...")
|
|
664
|
+
urls.append(pu)
|
|
665
|
+
|
|
666
|
+
# Extract from single video object
|
|
667
|
+
single_video = imagine_video.get("video") or {}
|
|
668
|
+
if isinstance(single_video, dict) and single_video:
|
|
669
|
+
logger.info(f"[VIDEO URL EXTRACTION] Found single video object with keys: {list(single_video.keys())}")
|
|
670
|
+
|
|
671
|
+
uri = single_video.get("video_url") or single_video.get("uri")
|
|
672
|
+
if uri:
|
|
673
|
+
logger.info(f"[VIDEO URL EXTRACTION] Found video_url/uri in single video: {uri[:100]}...")
|
|
674
|
+
urls.append(uri)
|
|
675
|
+
|
|
676
|
+
delivery = single_video.get("videoDeliveryResponseResult") or {}
|
|
677
|
+
prog = delivery.get("progressive_urls", [])
|
|
678
|
+
logger.info(f"[VIDEO URL EXTRACTION] Found {len(prog)} progressive_urls in single video")
|
|
679
|
+
|
|
680
|
+
for prog_idx, p in enumerate(prog):
|
|
681
|
+
pu = p.get("progressive_url")
|
|
682
|
+
if pu:
|
|
683
|
+
logger.info(f"[VIDEO URL EXTRACTION] Found progressive_url[{prog_idx}]: {pu[:100]}...")
|
|
684
|
+
urls.append(pu)
|
|
685
|
+
|
|
686
|
+
except json.JSONDecodeError as e:
|
|
687
|
+
logger.error(f"[VIDEO URL EXTRACTION] JSON decode failed: {e}")
|
|
688
|
+
logger.debug(f"[VIDEO URL EXTRACTION] Response preview: {response_text[:500]}")
|
|
689
|
+
|
|
690
|
+
# Fallback to regex
|
|
691
|
+
logger.info("[VIDEO URL EXTRACTION] Falling back to regex extraction...")
|
|
692
|
+
import re
|
|
693
|
+
urls = re.findall(r'https?://[^\s"\'<>]+fbcdn[^\s"\'<>]+\.mp4[^\s"\'<>]*', response_text)
|
|
694
|
+
logger.info(f"[VIDEO URL EXTRACTION] Regex found {len(urls)} .mp4 URLs")
|
|
695
|
+
|
|
696
|
+
# Deduplicate while preserving order
|
|
697
|
+
seen = set()
|
|
698
|
+
unique_urls: List[str] = []
|
|
699
|
+
for u in urls:
|
|
700
|
+
if u and u not in seen:
|
|
701
|
+
seen.add(u)
|
|
702
|
+
unique_urls.append(u)
|
|
703
|
+
|
|
704
|
+
logger.info(f"[VIDEO URL EXTRACTION] Final result: {len(unique_urls)} unique video URLs")
|
|
705
|
+
if unique_urls:
|
|
706
|
+
for idx, url in enumerate(unique_urls, 1):
|
|
707
|
+
logger.info(f"[VIDEO URL EXTRACTION] Final URL {idx}: {url[:150]}...")
|
|
708
|
+
else:
|
|
709
|
+
logger.warning("[VIDEO URL EXTRACTION] ⚠️ NO VIDEO URLs FOUND!")
|
|
710
|
+
logger.debug(f"[VIDEO URL EXTRACTION] Response preview (first 1000 chars): {response_text[:1000]}")
|
|
711
|
+
|
|
712
|
+
return unique_urls
|
|
713
|
+
|
|
714
|
+
def generate_video(
|
|
715
|
+
self,
|
|
716
|
+
prompt: str,
|
|
717
|
+
media_ids: Optional[List[str]] = None,
|
|
718
|
+
attachment_metadata: Optional[Dict[str, Any]] = None,
|
|
719
|
+
wait_before_poll: int = 10,
|
|
720
|
+
max_attempts: int = 30,
|
|
721
|
+
wait_seconds: int = 5,
|
|
722
|
+
verbose: bool = True
|
|
723
|
+
) -> Dict:
|
|
724
|
+
"""
|
|
725
|
+
Main function to generate video and retrieve URLs.
|
|
726
|
+
|
|
727
|
+
Args:
|
|
728
|
+
prompt: Text prompt for video generation
|
|
729
|
+
media_ids: Optional list of media IDs from uploaded images
|
|
730
|
+
attachment_metadata: Optional dict with 'file_size' (int) and 'mime_type' (str)
|
|
731
|
+
wait_before_poll: Seconds to wait before starting to poll
|
|
732
|
+
max_attempts: Maximum polling attempts
|
|
733
|
+
wait_seconds: Seconds between polling attempts
|
|
734
|
+
verbose: Whether to print status messages
|
|
735
|
+
|
|
736
|
+
Returns:
|
|
737
|
+
Dictionary with success status, conversation_id, prompt, video_urls, and timestamp
|
|
738
|
+
"""
|
|
739
|
+
# Step 1: Create video generation request
|
|
740
|
+
conversation_id = self.create_video_generation_request(
|
|
741
|
+
prompt_text=prompt,
|
|
742
|
+
media_ids=media_ids,
|
|
743
|
+
attachment_metadata=attachment_metadata,
|
|
744
|
+
verbose=verbose
|
|
745
|
+
)
|
|
746
|
+
|
|
747
|
+
if not conversation_id:
|
|
748
|
+
return {"success": False, "error": "Failed to create video generation request"}
|
|
749
|
+
|
|
750
|
+
# Step 2: Wait a bit before polling
|
|
751
|
+
time.sleep(wait_before_poll)
|
|
752
|
+
|
|
753
|
+
# Step 3: Poll for video URLs
|
|
754
|
+
video_urls = self.fetch_video_urls(
|
|
755
|
+
conversation_id=conversation_id,
|
|
756
|
+
max_attempts=max_attempts,
|
|
757
|
+
wait_seconds=wait_seconds,
|
|
758
|
+
verbose=verbose
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
result = {
|
|
762
|
+
"success": len(video_urls) > 0,
|
|
763
|
+
"conversation_id": conversation_id,
|
|
764
|
+
"prompt": prompt,
|
|
765
|
+
"video_urls": video_urls,
|
|
766
|
+
"timestamp": time.time()
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
return result
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
metaai_api/__init__.py,sha256=YT4Dn_m1Fm3Hh10D4Z9jtmADjQHnZSsdtwIn8IlCjN4,714
|
|
2
|
-
metaai_api/api_server.py,sha256=
|
|
2
|
+
metaai_api/api_server.py,sha256=hb1C3rEarPOHF6W-qqENaxrZWwn8A0qUrjSVlRtNV2s,13248
|
|
3
3
|
metaai_api/client.py,sha256=Th46qW1l8OS8Hu5pj0aGFn4iQNz62A3sbXko-LP-SAU,5263
|
|
4
4
|
metaai_api/exceptions.py,sha256=MRRAppZa0OFA0QLSvC0nABgZN_Ll1dUq9JfhECTqV-Q,114
|
|
5
5
|
metaai_api/image_upload.py,sha256=DQ2xqKdM1I_pF1rZBsB7-QTvXLzke2_0XiIOxFhpc70,6563
|
|
6
6
|
metaai_api/main.py,sha256=3kWYikKjq7pk2l8x6h12OkHe2pwxcs_UxzLj2qSy9Qs,28384
|
|
7
7
|
metaai_api/utils.py,sha256=qzfIO3WkRH-gSV99b8RiECnMOku8lZEY3Jka9lTLExA,11979
|
|
8
|
-
|
|
9
|
-
metaai_sdk-2.3.
|
|
10
|
-
metaai_sdk-2.3.
|
|
11
|
-
metaai_sdk-2.3.
|
|
12
|
-
metaai_sdk-2.3.
|
|
8
|
+
metaai_api/video_generation.py,sha256=NlNLvzzZqHw-nvbY9ATvn4BDusHs2luOYDrOSvhMWnk,34537
|
|
9
|
+
metaai_sdk-2.3.4.dist-info/licenses/LICENSE,sha256=hRLLSBixyX0tRh2k0iOGoF7nx-l-vBChNffFfVOIEtc,1290
|
|
10
|
+
metaai_sdk-2.3.4.dist-info/METADATA,sha256=yR6jKg1VXjGZSzPYydKF4ucOMu0Nd79Xr9XkhzrOioc,27602
|
|
11
|
+
metaai_sdk-2.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
+
metaai_sdk-2.3.4.dist-info/top_level.txt,sha256=R6YCiIQLYFKKaqhNZXDwXbpj1u01P_YhcMCVbJiDUJs,11
|
|
13
|
+
metaai_sdk-2.3.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|