rairo commited on
Commit
c73b0bd
·
verified ·
1 Parent(s): 39e02eb

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +217 -109
main.py CHANGED
@@ -44,6 +44,13 @@ except Exception as e:
44
 
45
  bucket = storage.bucket()
46
 
 
 
 
 
 
 
 
47
  # Gemini API initialization
48
  api_key = os.environ['Gemini']
49
  def configure_gemini():
@@ -161,89 +168,240 @@ def get_user_profile():
161
  # -----------------------
162
  # Content
163
  # -----------------------
 
164
 
165
- # ---------- Video Generation Endpoint ----------
166
-
167
- @app.route('/api/video/generate', methods=['POST'])
168
- def generate_video():
169
  try:
 
170
  auth_header = request.headers.get('Authorization', '')
171
  if not auth_header.startswith('Bearer '):
172
- return jsonify({'error': 'Authorization header missing or malformed'}), 401
173
  token = auth_header.split(' ')[1]
174
  uid = verify_token(token)
175
  if not uid:
176
- return jsonify({'error': 'Invalid token'}), 401
177
-
178
- # Get user data and check credits
179
- user_ref = db.reference(f'users/{uid}')
180
- user_data = user_ref.get()
181
- if not user_data or user_data.get('credits', 0) < 1:
182
- return jsonify({'error': 'Insufficient credits'}), 400
183
 
184
- # Deduct one credit
185
- new_credits = user_data.get('credits', 0) - 5
186
- user_ref.update({'credits': new_credits})
 
 
 
 
 
 
 
187
 
188
- # Get prompt and generate video (simulation using Gemini)
189
- req_data = request.get_json()
190
- prompt = req_data.get('prompt', '')
191
  if not prompt:
192
  return jsonify({'error': 'Prompt is required'}), 400
193
 
194
- generated_video = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
 
196
  return jsonify({
197
- 'success': True,
198
- 'video': generated_video,
199
- 'remaining_credits': new_credits
 
 
200
  })
 
201
  except Exception as e:
202
  return jsonify({'error': str(e)}), 500
203
 
204
- # ---------- Story Generation Endpoint ----------
205
-
206
- @app.route('/api/story/generate', methods=['POST'])
207
- def generate_story():
208
  try:
 
209
  auth_header = request.headers.get('Authorization', '')
210
  if not auth_header.startswith('Bearer '):
211
- return jsonify({'error': 'Authorization header missing or malformed'}), 401
212
  token = auth_header.split(' ')[1]
213
  uid = verify_token(token)
214
  if not uid:
215
- return jsonify({'error': 'Invalid token'}), 401
216
-
217
- # Get user data and check credits
218
- user_ref = db.reference(f'users/{uid}')
219
- user_data = user_ref.get()
220
- if not user_data or user_data.get('credits', 0) < 1:
221
- return jsonify({'error': 'Insufficient credits'}), 400
222
-
223
- # Deduct one credit
224
- new_credits = user_data.get('credits', 0) - 5
225
- user_ref.update({'credits': new_credits})
226
-
227
- # Get prompt and generate video (simulation using Gemini)
228
- req_data = request.get_json()
229
- prompt = req_data.get('prompt', '')
230
- if not prompt:
231
- return jsonify({'error': 'Prompt is required'}), 400
232
-
233
- model = configure_gemini()
234
- # Call to Gemini to generate video content (this is a simulation)
235
- response = "function(s) that does something"
236
- # For demonstration, we assume the response contains a 'result' field
237
- generated_story = response.result if hasattr(response, 'result') else "story generated based on parameters."
238
 
239
- return jsonify({
240
- 'success': True,
241
- 'story': generated_story,
242
- 'remaining_credits': new_credits
243
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  except Exception as e:
245
  return jsonify({'error': str(e)}), 500
246
 
 
247
  # ---------- Audio Generation Endpoint ----------
248
  @app.route('/api/audio/generate', methods=['POST'])
249
  def generate_audio_edit():
@@ -272,15 +430,11 @@ def generate_audio_edit():
272
  if not prompt:
273
  return jsonify({'error': 'Prompt is required'}), 400
274
 
275
- model = configure_gemini()
276
- # Call to Gemini to generate video content (this is a simulation)
277
- response = model.generate(prompt=prompt)
278
- # For demonstration, we assume the response contains a 'result' field
279
- generated_video = response.result if hasattr(response, 'result') else "Video content generated based on prompt."
280
 
281
  return jsonify({
282
  'success': True,
283
- 'video': generated_video,
284
  'remaining_credits': new_credits
285
  })
286
  except Exception as e:
@@ -317,59 +471,13 @@ def generate_image_edit():
317
 
318
  model = configure_gemini()
319
  # Call to Gemini to generate video content (this is a simulation)
320
- response = model.generate(prompt=prompt)
321
- # For demonstration, we assume the response contains a 'result' field
322
- generated_video = response.result if hasattr(response, 'result') else "Video content generated based on prompt."
323
-
324
  return jsonify({
325
- 'success': True,
326
- 'video': generated_video,
327
- 'remaining_credits': new_credits
328
  })
329
  except Exception as e:
330
  return jsonify({'error': str(e)}), 500
331
 
332
- # ---------- Preview Generation Endpoint ----------
333
- @app.route('/api/preview/generate', methods=['POST'])
334
- def generate_preview():
335
- try:
336
- auth_header = request.headers.get('Authorization', '')
337
- if not auth_header.startswith('Bearer '):
338
- return jsonify({'error': 'Authorization header missing or malformed'}), 401
339
- token = auth_header.split(' ')[1]
340
- uid = verify_token(token)
341
- if not uid:
342
- return jsonify({'error': 'Invalid token'}), 401
343
-
344
- # Get user data and check credits
345
- user_ref = db.reference(f'users/{uid}')
346
- user_data = user_ref.get()
347
- if not user_data or user_data.get('credits', 0) < 1:
348
- return jsonify({'error': 'Insufficient credits'}), 400
349
-
350
- # Deduct one credit
351
- new_credits = user_data.get('credits', 0) - 1
352
- user_ref.update({'credits': new_credits})
353
-
354
- # Get prompt and generate video (simulation using Gemini)
355
- req_data = request.get_json()
356
- prompt = req_data.get('prompt', '')
357
- if not prompt:
358
- return jsonify({'error': 'Prompt is required'}), 400
359
-
360
- model = configure_gemini()
361
- # Call to Gemini to generate video content (this is a simulation)
362
- response = model.generate(prompt=prompt)
363
- # For demonstration, we assume the response contains a 'result' field
364
- generated_video = response.result if hasattr(response, 'result') else "Video content generated based on prompt."
365
-
366
- return jsonify({
367
- 'success': True,
368
- 'video': generated_video,
369
- 'remaining_credits': new_credits
370
- })
371
- except Exception as e:
372
- return jsonify({'error': str(e)}), 500
373
 
374
  # ---------- Credit Request Endpoints ----------
375
 
 
44
 
45
  bucket = storage.bucket()
46
 
47
+
48
+ # Helper: Upload a local file to Firebase Storage and return its public URL
49
+ def upload_to_storage(local_path, destination_blob_name):
50
+ blob = bucket.blob(destination_blob_name)
51
+ blob.upload_from_filename(local_path)
52
+ return blob.public_url
53
+
54
  # Gemini API initialization
55
  api_key = os.environ['Gemini']
56
  def configure_gemini():
 
168
  # -----------------------
169
  # Content
170
  # -----------------------
171
+ # ---------- Story Generation Endpoint ----------
172
 
173
+ @app.route('/api/story/generate', methods=['POST'])
174
+ def generate_story_endpoint():
 
 
175
  try:
176
+ # --- Authentication ---
177
  auth_header = request.headers.get('Authorization', '')
178
  if not auth_header.startswith('Bearer '):
179
+ return jsonify({'error': 'Missing or invalid token'}), 401
180
  token = auth_header.split(' ')[1]
181
  uid = verify_token(token)
182
  if not uid:
183
+ return jsonify({'error': 'Invalid or expired token'}), 401
 
 
 
 
 
 
184
 
185
+ # --- Read Request Data ---
186
+ data = request.get_json()
187
+ input_type = data.get('input_type', 'text') # "text", "wiki", "bible", "youtube", or "dataframe"
188
+ prompt = data.get('prompt')
189
+ custom_prompt = data.get('custom_prompt', "") # Optional extra prompt
190
+ story_type = data.get('story_type', 'free_form')
191
+ style = data.get('style', 'whimsical')
192
+ voice_model = data.get('voice_model', 'aura-asteria-en')
193
+ image_model = data.get('image_model', 'hf')
194
+ audio_model = data.get('audio_model', 'deepgram')
195
 
 
 
 
196
  if not prompt:
197
  return jsonify({'error': 'Prompt is required'}), 400
198
 
199
+ # Combine prompt and custom_prompt (if provided)
200
+ combined_prompt = prompt + (" " + custom_prompt if custom_prompt else "")
201
+
202
+ # --- Select the Appropriate Story Generation Function ---
203
+ story_gen_start = time.time()
204
+ full_story = None
205
+
206
+ if input_type == "text":
207
+ from stories import generate_story_from_text
208
+ full_story = generate_story_from_text(combined_prompt, story_type)
209
+ elif input_type == "wiki":
210
+ wiki_url = data.get("wiki_url")
211
+ if not wiki_url:
212
+ return jsonify({'error': 'wiki_url is required for input_type "wiki"'}), 400
213
+ from stories import generate_story_from_wiki
214
+ full_story = generate_story_from_wiki(wiki_url, story_type)
215
+ elif input_type == "bible":
216
+ bible_reference = data.get("bible_reference")
217
+ if not bible_reference:
218
+ return jsonify({'error': 'bible_reference is required for input_type "bible"'}), 400
219
+ from stories import generate_story_from_bible
220
+ full_story = generate_story_from_bible(bible_reference, story_type)
221
+ elif input_type == "youtube":
222
+ youtube_url = data.get("youtube_url")
223
+ if not youtube_url:
224
+ return jsonify({'error': 'youtube_url is required for input_type "youtube"'}), 400
225
+ from stories import generate_story_from_youtube
226
+ full_story = generate_story_from_youtube(youtube_url, story_type)
227
+ elif input_type == "dataframe":
228
+ # Expecting dataframe data as JSON (list of dicts)
229
+ df_data = data.get("data")
230
+ if not df_data:
231
+ return jsonify({'error': 'Data for dataframe input_type is required'}), 400
232
+ df = pd.DataFrame(df_data)
233
+ from stories import generate_story_from_dataframe
234
+ full_story = generate_story_from_dataframe(df, story_type)
235
+ else:
236
+ return jsonify({'error': 'Unsupported input_type'}), 400
237
+
238
+ story_gen_end = time.time()
239
+ story_generation_time = story_gen_end - story_gen_start
240
+
241
+ if not full_story:
242
+ return jsonify({'error': 'Story generation failed'}), 500
243
+
244
+ # --- Split the Story into 5 Sections ---
245
+ sections_raw = [s.strip() for s in full_story.split("[break]") if s.strip()]
246
+ if len(sections_raw) < 5:
247
+ sections_raw += ["(Placeholder section)"] * (5 - len(sections_raw))
248
+ elif len(sections_raw) > 5:
249
+ sections_raw = sections_raw[:5]
250
+
251
+ sections = []
252
+ image_generation_times = []
253
+ audio_generation_times = []
254
+
255
+ # Import generation functions from your modules
256
+ from image_gen import generate_image_with_retry # image generation function
257
+ from audio_gen import generate_audio # audio generation function
258
+
259
+ # Process each section
260
+ for section in sections_raw:
261
+ # --- Image Generation ---
262
+ # Extract an image prompt between angle brackets; otherwise, fallback to the first 100 characters.
263
+ img_prompt_match = re.search(r"<(.*?)>", section)
264
+ img_prompt = img_prompt_match.group(1).strip() if img_prompt_match else section[:100]
265
+
266
+ image_start = time.time()
267
+ image_obj, _ = generate_image_with_retry(img_prompt, style, model=image_model)
268
+ image_end = time.time()
269
+ image_generation_times.append(image_end - image_start)
270
+
271
+ # Save image locally and upload it.
272
+ image_filename = f"/tmp/{uuid.uuid4().hex}.jpg"
273
+ image_obj.save(image_filename, format="JPEG")
274
+ image_blob_name = f"stories/{uid}/{uuid.uuid4().hex}.jpg"
275
+ image_url = upload_to_storage(image_filename, image_blob_name)
276
+
277
+ # --- Audio Generation ---
278
+ audio_start = time.time()
279
+ audio_file_path = generate_audio(section, voice_model, audio_model=audio_model)
280
+ audio_end = time.time()
281
+ audio_generation_times.append(audio_end - audio_start)
282
+
283
+ audio_blob_name = f"stories/{uid}/{uuid.uuid4().hex}.mp3"
284
+ audio_url = upload_to_storage(audio_file_path, audio_blob_name)
285
+
286
+ sections.append({
287
+ "section_text": section,
288
+ "image_url": image_url,
289
+ "audio_url": audio_url
290
+ })
291
+
292
+ # Clean up temporary files
293
+ os.remove(image_filename)
294
+ os.remove(audio_file_path)
295
+
296
+ # --- Store the Story Record in Firebase Realtime Database ---
297
+ story_id = str(uuid.uuid4())
298
+ story_ref = db.reference(f"stories/{story_id}")
299
+ story_record = {
300
+ "uid": uid,
301
+ "full_story": full_story,
302
+ "sections": sections,
303
+ "generation_times": {
304
+ "story_generation_time": story_generation_time,
305
+ "image_generation_times": image_generation_times,
306
+ "audio_generation_times": audio_generation_times
307
+ },
308
+ "created_at": datetime.utcnow().isoformat(),
309
+ "input_type": input_type,
310
+ "story_type": story_type
311
+ }
312
+ story_ref.set(story_record)
313
+
314
+ preview = sections[0] if sections else {}
315
 
316
  return jsonify({
317
+ "story_id": story_id,
318
+ "full_story": full_story,
319
+ "preview": preview,
320
+ "sections": sections,
321
+ "generation_times": story_record["generation_times"]
322
  })
323
+
324
  except Exception as e:
325
  return jsonify({'error': str(e)}), 500
326
 
327
+ # ---------- Video Generation Endpoint ----------
328
+ @app.route('/api/video/generate', methods=['POST'])
329
+ def generate_video_endpoint():
 
330
  try:
331
+ # Verify user token
332
  auth_header = request.headers.get('Authorization', '')
333
  if not auth_header.startswith('Bearer '):
334
+ return jsonify({'error': 'Missing or invalid token'}), 401
335
  token = auth_header.split(' ')[1]
336
  uid = verify_token(token)
337
  if not uid:
338
+ return jsonify({'error': 'Invalid or expired token'}), 401
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
 
340
+ data = request.get_json()
341
+ story_id = data.get('story_id')
342
+ if not story_id:
343
+ return jsonify({'error': 'story_id is required'}), 400
344
+
345
+ # Retrieve the story record from the database.
346
+ story_ref = db.reference(f"stories/{story_id}")
347
+ story_data = story_ref.get()
348
+ if not story_data:
349
+ return jsonify({'error': 'Story not found'}), 404
350
+
351
+ sections = story_data.get("sections", [])
352
+ if not sections:
353
+ return jsonify({'error': 'No sections found in the story'}), 404
354
+
355
+ # Download the image and audio files from their URLs.
356
+ image_files = []
357
+ audio_files = []
358
+ for section in sections:
359
+ image_url = section.get("image_url")
360
+ audio_url = section.get("audio_url")
361
+ # Download image
362
+ img_resp = requests.get(image_url)
363
+ if img_resp.status_code == 200:
364
+ img_path = f"/tmp/{uuid.uuid4().hex}.jpg"
365
+ with open(img_path, "wb") as f:
366
+ f.write(img_resp.content)
367
+ image_files.append(img_path)
368
+ # Download audio
369
+ aud_resp = requests.get(audio_url)
370
+ if aud_resp.status_code == 200:
371
+ aud_path = f"/tmp/{uuid.uuid4().hex}.mp3"
372
+ with open(aud_path, "wb") as f:
373
+ f.write(aud_resp.content)
374
+ audio_files.append(aud_path)
375
+
376
+ if not image_files:
377
+ return jsonify({'error': 'No images available for video generation'}), 500
378
+
379
+ # Generate video using the video generation function (from video_gen.py)
380
+ from video_gen import create_video # from your video_gen.py
381
+ video_output_path = f"/tmp/{uuid.uuid4().hex}.mp4"
382
+ video_file = create_video(image_files, audio_files, output_path=video_output_path)
383
+ if not video_file:
384
+ return jsonify({'error': 'Video generation failed'}), 500
385
+
386
+ # Upload the video to Firebase Storage.
387
+ video_blob_name = f"stories/{uid}/{uuid.uuid4().hex}.mp4"
388
+ video_url = upload_to_storage(video_file, video_blob_name)
389
+
390
+ # Optionally, update the story record with the video URL.
391
+ story_ref.update({"video_url": video_url})
392
+
393
+ # Clean up temporary local files.
394
+ for f in image_files:
395
+ os.remove(f)
396
+ for f in audio_files:
397
+ os.remove(f)
398
+ os.remove(video_file)
399
+
400
+ return jsonify({"video_url": video_url})
401
  except Exception as e:
402
  return jsonify({'error': str(e)}), 500
403
 
404
+
405
  # ---------- Audio Generation Endpoint ----------
406
  @app.route('/api/audio/generate', methods=['POST'])
407
  def generate_audio_edit():
 
430
  if not prompt:
431
  return jsonify({'error': 'Prompt is required'}), 400
432
 
433
+
 
 
 
 
434
 
435
  return jsonify({
436
  'success': True,
437
+ 'audio': generated_audio,
438
  'remaining_credits': new_credits
439
  })
440
  except Exception as e:
 
471
 
472
  model = configure_gemini()
473
  # Call to Gemini to generate video content (this is a simulation)
474
+ generated_image = ""
 
 
 
475
  return jsonify({
476
+ 'audio': '.mp3'
 
 
477
  })
478
  except Exception as e:
479
  return jsonify({'error': str(e)}), 500
480
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
481
 
482
  # ---------- Credit Request Endpoints ----------
483