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 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
- body.prompt,
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
- body.prompt,
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metaai-sdk
3
- Version: 2.3.2
3
+ Version: 2.3.4
4
4
  Summary: Feature-rich Python SDK for Meta AI - Chat, Image & Video Generation powered by Llama 3
5
5
  Author-email: Ashiq Hussain Mir <imseldrith@gmail.com>
6
6
  License-Expression: MIT
@@ -1,12 +1,13 @@
1
1
  metaai_api/__init__.py,sha256=YT4Dn_m1Fm3Hh10D4Z9jtmADjQHnZSsdtwIn8IlCjN4,714
2
- metaai_api/api_server.py,sha256=p_h_jV6BNPrk2CVUs5ML9bt55brt-RgANrKTD5LdRr0,12804
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
- metaai_sdk-2.3.2.dist-info/licenses/LICENSE,sha256=hRLLSBixyX0tRh2k0iOGoF7nx-l-vBChNffFfVOIEtc,1290
9
- metaai_sdk-2.3.2.dist-info/METADATA,sha256=6S-IjAGMwlkrbItlRMp-flXMBjtFs5KTmQuQG0_jYbc,27602
10
- metaai_sdk-2.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
- metaai_sdk-2.3.2.dist-info/top_level.txt,sha256=R6YCiIQLYFKKaqhNZXDwXbpj1u01P_YhcMCVbJiDUJs,11
12
- metaai_sdk-2.3.2.dist-info/RECORD,,
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,,