Opera8 commited on
Commit
6c6bd59
·
verified ·
1 Parent(s): aaae3e3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +89 -79
app.py CHANGED
@@ -7,7 +7,7 @@ import aiohttp
7
  import requests
8
  import subprocess
9
  import shutil
10
- import random # اضافه شده برای انتخاب تصادفی کلید
11
  from flask import Flask, request, jsonify, send_file, render_template
12
  from flask_cors import CORS
13
  from werkzeug.utils import secure_filename
@@ -18,6 +18,12 @@ import yt_dlp
18
  app = Flask(__name__, template_folder='templates', static_folder='static')
19
  CORS(app)
20
 
 
 
 
 
 
 
21
  # تنظیمات مسیرها
22
  UPLOAD_FOLDER = 'uploads'
23
  TEMP_AUDIO_FOLDER = 'temp_audio'
@@ -54,6 +60,63 @@ def download_youtube(url):
54
  ydl.download([url])
55
  return filepath
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  async def generate_audio_async(session, text, speaker, index):
58
  """تولید صدا به صورت غیرهمزمان از API پادکست"""
59
  try:
@@ -102,20 +165,11 @@ def serve_audio(filename):
102
 
103
  @app.route('/api/analyze', methods=['POST'])
104
  def analyze_video():
105
- # دریافت کلیدها به صورت رشته (ممکن است شامل کاما باشد)
106
- raw_api_keys = request.form.get('api_key')
107
  youtube_url = request.form.get('youtube_url')
108
  video_file = request.files.get('video_file')
109
  target_lang = request.form.get('language', 'Persian')
110
 
111
- if not raw_api_keys: return jsonify({"error": "API Key is required"}), 400
112
-
113
- # تبدیل رشته کلیدها به لیست و حذف فاصله‌های اضافی
114
- api_keys_list = [k.strip() for k in raw_api_keys.split(',') if k.strip()]
115
-
116
- # بهم ریختن ترتیب کلیدها برای انتخاب تصادفی
117
- random.shuffle(api_keys_list)
118
-
119
  try:
120
  # 1. دریافت ویدیو
121
  if youtube_url:
@@ -130,66 +184,26 @@ def analyze_video():
130
  # 2. استخراج صدا
131
  audio_path = extract_audio(video_path)
132
 
133
- # 3. تلاش برای استفاده از کلیدها (Logic Retry & Fallback)
134
- script = None
135
- last_error = None
 
136
 
137
- for current_key in api_keys_list:
138
- try:
139
- print(f"Trying API Key: {current_key[:5]}...") # فقط لاگ کردن ابتدای کلید
140
- genai.configure(api_key=current_key)
141
- model = genai.GenerativeModel('gemini-2.5-flash')
142
-
143
- prompt = f"""
144
- You are a Dubbing Director.
145
- {CAST_PROMPT}
146
-
147
- TASK:
148
- 1. Identify speakers in the audio.
149
- 2. Assign a Voice Actor ID from the list to each segment based on gender/tone.
150
- 3. Translate the dialogue to {target_lang}.
151
- 4. Return a JSON Array.
152
-
153
- Format:
154
- [
155
- {{"start": 0.0, "end": 4.5, "speaker_id": "Charon", "text": "Translated text..."}},
156
- ...
157
- ]
158
- """
159
-
160
- # آپلود فایل (توجه: فایل باید با همان کلیدی آپلود شود که جنریت انجام می‌شود)
161
- audio_file = genai.upload_file(audio_path)
162
-
163
- # انتظار برای پردازش فایل
164
- while audio_file.state.name == "PROCESSING":
165
- time.sleep(1)
166
- audio_file = genai.get_file(audio_file.name)
167
-
168
- if audio_file.state.name == "FAILED":
169
- raise Exception("File upload failed on Google servers")
170
-
171
- res = model.generate_content([prompt, audio_file], generation_config={"response_mime_type": "application/json"})
172
-
173
- # پارس کردن خروجی
174
- try:
175
- script = json.loads(res.text)
176
- except:
177
- # گاهی اوقات مدل جیسون خالص نمی‌دهد، پاکسازی ساده
178
- clean_json = res.text.replace('```json', '').replace('```', '')
179
- script = json.loads(clean_json)
180
-
181
- # اگر موفق بود، حلقه را بشکن
182
- break
183
-
184
- except Exception as e:
185
- print(f"Key {current_key[:5]}... failed. Error: {e}")
186
- last_error = e
187
- # ادامه می‌دهیم به کلید بعدی در لیست
188
- continue
189
-
190
- # اگر بعد از امتحان تمام کلیدها اسکریپت هنوز خالی بود
191
- if script is None:
192
- return jsonify({"error": f"All API keys failed. Last error: {str(last_error)}"}), 500
193
 
194
  # 4. تولید صدای اولیه به صورت همزمان (Batch Processing)
195
  results = asyncio.run(batch_generate_audio(script))
@@ -217,7 +231,7 @@ def regenerate_segment():
217
  speaker = data.get('speaker_id')
218
 
219
  try:
220
- # درخواست تکی به API پادکست (Sync request via standard requests lib)
221
  resp = requests.post(PODCAST_API_URL, json={"text": text, "speaker": speaker, "temperature": 0.9})
222
  if resp.status_code == 200:
223
  filename = f"seg_{uuid.uuid4()}.wav"
@@ -256,15 +270,12 @@ def render_final():
256
  current_dur_ms = len(audio)
257
 
258
  if current_dur_ms > 0:
259
- # اگر تفاوت زمان زیاد بود، سرعت را تغییر بده (محدودیت بین 0.5 تا 2.0 برابر)
260
  speed = current_dur_ms / target_dur_ms
261
  speed = max(0.6, min(2.0, speed))
262
 
263
  if abs(speed - 1.0) > 0.05:
264
- # تغییر سرعت با FFmpeg چون کیفیت بهتری از pydub دارد
265
  temp_out = seg_path.replace('.wav', '_speed.wav')
266
  atempo = f"atempo={speed}"
267
- # برای سرعت‌های خیلی زیاد یا کم نیاز به زنجیره فیلتر است
268
  if speed > 2.0: atempo = "atempo=2.0,atempo={}".format(speed/2)
269
  elif speed < 0.5: atempo = "atempo=0.5,atempo={}".format(speed/0.5)
270
 
@@ -284,15 +295,14 @@ def render_final():
284
  # ترکیب با ویدیو
285
  final_video_path = os.path.join(UPLOAD_FOLDER, f"dubbed_{uuid.uuid4()}.mp4")
286
 
287
- # دستور FFmpeg: ویدیو اصلی + صدای جدید (جایگزین صدای قبلی)
288
  cmd = [
289
  'ffmpeg', '-y',
290
  '-i', video_path,
291
  '-i', final_mix_path,
292
- '-c:v', 'copy', # کپی ویدیو بدون انکود مجدد (سریع)
293
- '-c:a', 'aac', # انکود صدا
294
- '-map', '0:v:0', # تصویر از فایل اول
295
- '-map', '1:a:0', # صدا از فایل دوم
296
  '-shortest',
297
  final_video_path
298
  ]
 
7
  import requests
8
  import subprocess
9
  import shutil
10
+ import random
11
  from flask import Flask, request, jsonify, send_file, render_template
12
  from flask_cors import CORS
13
  from werkzeug.utils import secure_filename
 
18
  app = Flask(__name__, template_folder='templates', static_folder='static')
19
  CORS(app)
20
 
21
+ # ==========================================
22
+ # تنظیمات کلیدهای API (محل قرارگیری کلیدها)
23
+ # ==========================================
24
+ # کلیدهای خود را در اینجا وارد کنید و با کاما (,) جدا کنید
25
+ ALL_GEMINI_API_KEYS = "YOUR_API_KEY_1,YOUR_API_KEY_2,YOUR_API_KEY_3,YOUR_API_KEY_4"
26
+
27
  # تنظیمات مسیرها
28
  UPLOAD_FOLDER = 'uploads'
29
  TEMP_AUDIO_FOLDER = 'temp_audio'
 
60
  ydl.download([url])
61
  return filepath
62
 
63
+ def generate_content_with_retry(prompt, audio_file_path):
64
+ """
65
+ این تابع کلیدها را مدیریت می‌کند.
66
+ اگر یک کلید خطا داد، سراغ بعدی می‌رود.
67
+ """
68
+ # تبدیل رشته کلیدها به لیست و حذف فاصله‌های اضافی
69
+ keys_list = [k.strip() for k in ALL_GEMINI_API_KEYS.split(',') if k.strip()]
70
+
71
+ if not keys_list:
72
+ raise Exception("هیچ کلید API تعریف نشده است.")
73
+
74
+ # مخلوط کردن کلیدها برای انتخاب تصادفی
75
+ random.shuffle(keys_list)
76
+
77
+ last_exception = None
78
+
79
+ for api_key in keys_list:
80
+ try:
81
+ print(f"Trying with API Key: ...{api_key[-4:]}") # لاگ کردن ۴ حرف آخر کلید برای دیباگ
82
+ genai.configure(api_key=api_key)
83
+ model = genai.GenerativeModel('gemini-2.5-flash')
84
+
85
+ # آپلود فایل برای جمینای
86
+ # نکته: هر کلید فضای فایل خود را دارد، پس باید با کلید جدید دوباره آپلود شود
87
+ uploaded_file = genai.upload_file(audio_file_path)
88
+
89
+ # انتظار برای پردازش فایل
90
+ while uploaded_file.state.name == "PROCESSING":
91
+ time.sleep(1)
92
+ uploaded_file = genai.get_file(uploaded_file.name)
93
+
94
+ if uploaded_file.state.name == "FAILED":
95
+ raise Exception("Google failed to process audio file.")
96
+
97
+ # تولید محتوا
98
+ response = model.generate_content(
99
+ [prompt, uploaded_file],
100
+ generation_config={"response_mime_type": "application/json"}
101
+ )
102
+
103
+ # پاک کردن فایل از سرور گوگل برای جلوگیری از پر شدن حافظه
104
+ try:
105
+ genai.delete_file(uploaded_file.name)
106
+ except:
107
+ pass
108
+
109
+ return json.loads(response.text)
110
+
111
+ except Exception as e:
112
+ print(f"Key failed: {e}")
113
+ last_exception = e
114
+ # حلقه ادامه می‌یابد و سراغ کلید بعدی می‌رود
115
+ continue
116
+
117
+ # اگر از حلقه خارج شد یعنی همه کلیدها خطا داده‌اند
118
+ raise Exception(f"All API keys failed. Last error: {str(last_exception)}")
119
+
120
  async def generate_audio_async(session, text, speaker, index):
121
  """تولید صدا به صورت غیرهمزمان از API پادکست"""
122
  try:
 
165
 
166
  @app.route('/api/analyze', methods=['POST'])
167
  def analyze_video():
168
+ # دیگر نیازی به دریافت api_key از سمت کلاینت نیست
 
169
  youtube_url = request.form.get('youtube_url')
170
  video_file = request.files.get('video_file')
171
  target_lang = request.form.get('language', 'Persian')
172
 
 
 
 
 
 
 
 
 
173
  try:
174
  # 1. دریافت ویدیو
175
  if youtube_url:
 
184
  # 2. استخراج صدا
185
  audio_path = extract_audio(video_path)
186
 
187
+ # 3. ارسال به Gemini با قابلیت Retry و چرخش کلیدها
188
+ prompt = f"""
189
+ You are a Dubbing Director.
190
+ {CAST_PROMPT}
191
 
192
+ TASK:
193
+ 1. Identify speakers in the audio.
194
+ 2. Assign a Voice Actor ID from the list to each segment based on gender/tone.
195
+ 3. Translate the dialogue to {target_lang}.
196
+ 4. Return a JSON Array.
197
+
198
+ Format:
199
+ [
200
+ {{"start": 0.0, "end": 4.5, "speaker_id": "Charon", "text": "Translated text..."}},
201
+ ...
202
+ ]
203
+ """
204
+
205
+ # استفاده از تابع جدید که مدیریت کلیدها را بر عهده دارد
206
+ script = generate_content_with_retry(prompt, audio_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
 
208
  # 4. تولید صدای اولیه به صورت همزمان (Batch Processing)
209
  results = asyncio.run(batch_generate_audio(script))
 
231
  speaker = data.get('speaker_id')
232
 
233
  try:
234
+ # درخواست تکی به API پادکست
235
  resp = requests.post(PODCAST_API_URL, json={"text": text, "speaker": speaker, "temperature": 0.9})
236
  if resp.status_code == 200:
237
  filename = f"seg_{uuid.uuid4()}.wav"
 
270
  current_dur_ms = len(audio)
271
 
272
  if current_dur_ms > 0:
 
273
  speed = current_dur_ms / target_dur_ms
274
  speed = max(0.6, min(2.0, speed))
275
 
276
  if abs(speed - 1.0) > 0.05:
 
277
  temp_out = seg_path.replace('.wav', '_speed.wav')
278
  atempo = f"atempo={speed}"
 
279
  if speed > 2.0: atempo = "atempo=2.0,atempo={}".format(speed/2)
280
  elif speed < 0.5: atempo = "atempo=0.5,atempo={}".format(speed/0.5)
281
 
 
295
  # ترکیب با ویدیو
296
  final_video_path = os.path.join(UPLOAD_FOLDER, f"dubbed_{uuid.uuid4()}.mp4")
297
 
 
298
  cmd = [
299
  'ffmpeg', '-y',
300
  '-i', video_path,
301
  '-i', final_mix_path,
302
+ '-c:v', 'copy',
303
+ '-c:a', 'aac',
304
+ '-map', '0:v:0',
305
+ '-map', '1:a:0',
306
  '-shortest',
307
  final_video_path
308
  ]