Update main.py
Browse files
main.py
CHANGED
|
@@ -639,12 +639,95 @@ def generate_sozo_report(project_id):
|
|
| 639 |
return jsonify({'error': str(e)}), 500
|
| 640 |
|
| 641 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 642 |
@app.route('/api/sozo/projects/<string:project_id>/generate-video', methods=['POST'])
|
| 643 |
def generate_sozo_video(project_id):
|
| 644 |
try:
|
| 645 |
token = request.headers.get('Authorization', '').split(' ')[1]
|
| 646 |
uid = verify_token(token)
|
| 647 |
-
if not uid:
|
|
|
|
| 648 |
|
| 649 |
# Check user credits first
|
| 650 |
user_ref = db.reference(f'users/{uid}')
|
|
@@ -658,11 +741,15 @@ def generate_sozo_video(project_id):
|
|
| 658 |
|
| 659 |
project_ref = db.reference(f'sozo_projects/{project_id}')
|
| 660 |
project_data = project_ref.get()
|
| 661 |
-
if not project_data or project_data.get('uid') != uid:
|
|
|
|
| 662 |
|
| 663 |
data = request.get_json()
|
| 664 |
voice_model = data.get('voice_model', 'aura-2-andromeda-en')
|
| 665 |
|
|
|
|
|
|
|
|
|
|
| 666 |
project_ref.update({'status': 'generating_video'})
|
| 667 |
|
| 668 |
blob_path = f"sozo_projects/{uid}/{project_id}/data{Path(project_data['originalFilename']).suffix}"
|
|
@@ -670,28 +757,49 @@ def generate_sozo_video(project_id):
|
|
| 670 |
file_bytes = blob.download_as_bytes()
|
| 671 |
df = load_dataframe_safely(io.BytesIO(file_bytes), project_data['originalFilename'])
|
| 672 |
|
| 673 |
-
#
|
| 674 |
video_url = generate_video_from_project(
|
| 675 |
df,
|
| 676 |
project_data.get('rawMarkdown', ''),
|
| 677 |
-
project_data.get('dataContext', {}),
|
| 678 |
uid,
|
| 679 |
project_id,
|
| 680 |
voice_model,
|
| 681 |
-
bucket
|
| 682 |
)
|
| 683 |
|
| 684 |
-
if not video_url:
|
|
|
|
| 685 |
|
| 686 |
project_ref.update({'status': 'video_complete', 'videoUrl': video_url})
|
| 687 |
|
| 688 |
# Deduct credits ONLY after successful generation
|
| 689 |
user_ref.update({'credits': current_credits - 5})
|
| 690 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 691 |
return jsonify({
|
| 692 |
'success': True,
|
| 693 |
'video_url': video_url,
|
| 694 |
-
'credits_remaining': current_credits - 5
|
|
|
|
| 695 |
}), 200
|
| 696 |
|
| 697 |
except Exception as e:
|
|
|
|
| 639 |
return jsonify({'error': str(e)}), 500
|
| 640 |
|
| 641 |
|
| 642 |
+
def send_video_generation_notification(user_id, user_email, project_name, video_url, send_email=False):
|
| 643 |
+
"""
|
| 644 |
+
Send notification when video generation is completed successfully.
|
| 645 |
+
|
| 646 |
+
Args:
|
| 647 |
+
user_id (str): Firebase user ID
|
| 648 |
+
user_email (str): User's email address
|
| 649 |
+
project_name (str): Name of the project
|
| 650 |
+
video_url (str): URL of the generated video
|
| 651 |
+
send_email (bool): Whether to send email notification
|
| 652 |
+
|
| 653 |
+
Returns:
|
| 654 |
+
bool: True if successful, False otherwise
|
| 655 |
+
"""
|
| 656 |
+
try:
|
| 657 |
+
# Create the notification message
|
| 658 |
+
message_content = f"Your video for project '{project_name}' has been generated successfully!"
|
| 659 |
+
|
| 660 |
+
# Create email subject and body if email notification is requested
|
| 661 |
+
email_subject = None
|
| 662 |
+
email_body = None
|
| 663 |
+
|
| 664 |
+
if send_email:
|
| 665 |
+
email_subject = f"🎥 Video Generation Complete - {project_name}"
|
| 666 |
+
email_body = f"""
|
| 667 |
+
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f9f9f9;">
|
| 668 |
+
<div style="background-color: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
|
| 669 |
+
<div style="text-align: center; margin-bottom: 30px;">
|
| 670 |
+
<h1 style="color: #2563eb; margin: 0; font-size: 28px;">🎉 Video Generation Complete!</h1>
|
| 671 |
+
</div>
|
| 672 |
+
|
| 673 |
+
<div style="background-color: #f0f9ff; padding: 20px; border-radius: 8px; border-left: 4px solid #2563eb; margin-bottom: 25px;">
|
| 674 |
+
<h2 style="color: #1e40af; margin: 0 0 10px 0; font-size: 20px;">Your video is ready!</h2>
|
| 675 |
+
<p style="color: #374151; margin: 0; font-size: 16px;">
|
| 676 |
+
Great news! Your video for project <strong>"{project_name}"</strong> has been successfully generated and is ready for viewing.
|
| 677 |
+
</p>
|
| 678 |
+
</div>
|
| 679 |
+
|
| 680 |
+
<div style="text-align: center; margin: 30px 0;">
|
| 681 |
+
<a href="{video_url}"
|
| 682 |
+
style="display: inline-block; background-color: #2563eb; color: white; padding: 15px 30px;
|
| 683 |
+
text-decoration: none; border-radius: 8px; font-weight: bold; font-size: 16px;
|
| 684 |
+
box-shadow: 0 2px 4px rgba(37, 99, 235, 0.3);">
|
| 685 |
+
🎬 Watch Your Video
|
| 686 |
+
</a>
|
| 687 |
+
</div>
|
| 688 |
+
|
| 689 |
+
<div style="background-color: #f8fafc; padding: 20px; border-radius: 8px; margin-top: 25px;">
|
| 690 |
+
<h3 style="color: #374151; margin: 0 0 15px 0; font-size: 18px;">What's Next?</h3>
|
| 691 |
+
<ul style="color: #6b7280; margin: 0; padding-left: 20px; line-height: 1.6;">
|
| 692 |
+
<li>Click the button above to view your generated video</li>
|
| 693 |
+
<li>Share your video with your team or clients</li>
|
| 694 |
+
<li>Download the video for offline use</li>
|
| 695 |
+
<li>Create more videos with your remaining credits</li>
|
| 696 |
+
</ul>
|
| 697 |
+
</div>
|
| 698 |
+
|
| 699 |
+
<div style="text-align: center; margin-top: 30px; padding-top: 20px; border-top: 1px solid #e5e7eb;">
|
| 700 |
+
<p style="color: #9ca3af; font-size: 14px; margin: 0;">
|
| 701 |
+
This video was generated by <strong>Sozo Business Studio</strong><br>
|
| 702 |
+
Need help? Contact us at <a href="mailto:support@sozofix.tech" style="color: #2563eb;">support@sozofix.tech</a>
|
| 703 |
+
</p>
|
| 704 |
+
</div>
|
| 705 |
+
</div>
|
| 706 |
+
</div>
|
| 707 |
+
"""
|
| 708 |
+
|
| 709 |
+
# Use the existing _send_notification function
|
| 710 |
+
return _send_notification(
|
| 711 |
+
user_id=user_id,
|
| 712 |
+
user_email=user_email,
|
| 713 |
+
message_content=message_content,
|
| 714 |
+
send_email=send_email,
|
| 715 |
+
email_subject=email_subject,
|
| 716 |
+
email_body=email_body
|
| 717 |
+
)
|
| 718 |
+
|
| 719 |
+
except Exception as e:
|
| 720 |
+
logger.error(f"Failed to send video generation notification: {e}")
|
| 721 |
+
return False
|
| 722 |
+
|
| 723 |
+
|
| 724 |
@app.route('/api/sozo/projects/<string:project_id>/generate-video', methods=['POST'])
|
| 725 |
def generate_sozo_video(project_id):
|
| 726 |
try:
|
| 727 |
token = request.headers.get('Authorization', '').split(' ')[1]
|
| 728 |
uid = verify_token(token)
|
| 729 |
+
if not uid:
|
| 730 |
+
return jsonify({'error': 'Unauthorized'}), 401
|
| 731 |
|
| 732 |
# Check user credits first
|
| 733 |
user_ref = db.reference(f'users/{uid}')
|
|
|
|
| 741 |
|
| 742 |
project_ref = db.reference(f'sozo_projects/{project_id}')
|
| 743 |
project_data = project_ref.get()
|
| 744 |
+
if not project_data or project_data.get('uid') != uid:
|
| 745 |
+
return jsonify({'error': 'Project not found or unauthorized'}), 404
|
| 746 |
|
| 747 |
data = request.get_json()
|
| 748 |
voice_model = data.get('voice_model', 'aura-2-andromeda-en')
|
| 749 |
|
| 750 |
+
# NEW: Get notification preference from request
|
| 751 |
+
send_email_notification = data.get('send_email_notification', False)
|
| 752 |
+
|
| 753 |
project_ref.update({'status': 'generating_video'})
|
| 754 |
|
| 755 |
blob_path = f"sozo_projects/{uid}/{project_id}/data{Path(project_data['originalFilename']).suffix}"
|
|
|
|
| 757 |
file_bytes = blob.download_as_bytes()
|
| 758 |
df = load_dataframe_safely(io.BytesIO(file_bytes), project_data['originalFilename'])
|
| 759 |
|
| 760 |
+
# Generate the video
|
| 761 |
video_url = generate_video_from_project(
|
| 762 |
df,
|
| 763 |
project_data.get('rawMarkdown', ''),
|
| 764 |
+
project_data.get('dataContext', {}),
|
| 765 |
uid,
|
| 766 |
project_id,
|
| 767 |
voice_model,
|
| 768 |
+
bucket
|
| 769 |
)
|
| 770 |
|
| 771 |
+
if not video_url:
|
| 772 |
+
raise Exception("Video generation failed in core function.")
|
| 773 |
|
| 774 |
project_ref.update({'status': 'video_complete', 'videoUrl': video_url})
|
| 775 |
|
| 776 |
# Deduct credits ONLY after successful generation
|
| 777 |
user_ref.update({'credits': current_credits - 5})
|
| 778 |
|
| 779 |
+
# NEW: Send notification if requested
|
| 780 |
+
if send_email_notification:
|
| 781 |
+
project_name = project_data.get('name', 'Unnamed Project')
|
| 782 |
+
user_email = user_data.get('email')
|
| 783 |
+
|
| 784 |
+
if user_email:
|
| 785 |
+
notification_sent = send_video_generation_notification(
|
| 786 |
+
user_id=uid,
|
| 787 |
+
user_email=user_email,
|
| 788 |
+
project_name=project_name,
|
| 789 |
+
video_url=video_url,
|
| 790 |
+
send_email=True
|
| 791 |
+
)
|
| 792 |
+
|
| 793 |
+
if not notification_sent:
|
| 794 |
+
logger.warning(f"Failed to send notification for video generation completion to user {uid}")
|
| 795 |
+
else:
|
| 796 |
+
logger.warning(f"No email found for user {uid}, skipping email notification")
|
| 797 |
+
|
| 798 |
return jsonify({
|
| 799 |
'success': True,
|
| 800 |
'video_url': video_url,
|
| 801 |
+
'credits_remaining': current_credits - 5,
|
| 802 |
+
'notification_sent': send_email_notification and user_data.get('email') is not None
|
| 803 |
}), 200
|
| 804 |
|
| 805 |
except Exception as e:
|