Opera8 commited on
Commit
c52dbe6
·
verified ·
1 Parent(s): b6813da

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +10 -155
app.py CHANGED
@@ -2,10 +2,9 @@ import os
2
  import time
3
  import uuid
4
  import threading
5
- import json
6
- import requests
7
  from flask import Flask, request, jsonify, send_file, render_template_string
8
  from flask_cors import CORS
 
9
 
10
  app = Flask(__name__)
11
  CORS(app)
@@ -13,8 +12,11 @@ CORS(app)
13
  DOWNLOAD_FOLDER = 'downloads'
14
  os.makedirs(DOWNLOAD_FOLDER, exist_ok=True)
15
 
16
- # پاکسازی فایل‌های قدیمی
 
 
17
  def cleanup_old_files():
 
18
  while True:
19
  try:
20
  current_time = time.time()
@@ -24,12 +26,15 @@ def cleanup_old_files():
24
  if current_time - os.path.getctime(file_path) > 3600:
25
  os.remove(file_path)
26
  except Exception as e:
27
- pass
28
  time.sleep(3600)
29
 
30
  cleanup_thread = threading.Thread(target=cleanup_old_files, daemon=True)
31
  cleanup_thread.start()
32
 
 
 
 
33
  HTML_TEMPLATE = """
34
  <!DOCTYPE html>
35
  <html lang="fa" dir="rtl">
@@ -41,154 +46,4 @@ HTML_TEMPLATE = """
41
  body { font-family: Tahoma, Arial, sans-serif; background-color: #f3f4f6; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
42
  .container { background: #fff; padding: 30px; border-radius: 12px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); max-width: 500px; width: 100%; text-align: center; }
43
  input { width: 90%; padding: 12px; margin: 15px 0; border: 1px solid #ccc; border-radius: 8px; font-size: 16px; direction: ltr; }
44
- button { background: #3b82f6; color: white; border: none; padding: 12px 24px; font-size: 16px; border-radius: 8px; cursor: pointer; transition: 0.3s; width: 100%; font-weight: bold; }
45
- button:hover { background: #2563eb; }
46
- button:disabled { background: #9ca3af; cursor: not-allowed; }
47
- #status { margin-top: 15px; font-size: 14px; color: #4b5563; }
48
- .video-container { margin-top: 20px; display: none; }
49
- video { width: 100%; border-radius: 8px; }
50
- .dl-btn { background: #10b981; margin-top: 10px; display: inline-block; text-decoration: none; padding: 10px 20px; color: white; border-radius: 8px; }
51
- .dl-btn:hover { background: #059669; }
52
- </style>
53
- </head>
54
- <body>
55
- <div class="container">
56
- <h2>📥 دانلودر ویدیو</h2>
57
- <input type="text" id="urlInput" placeholder="https://www.youtube.com/watch?v=...">
58
- <button id="downloadBtn" onclick="startDownload()">دریافت ویدیو</button>
59
- <div id="status"></div>
60
-
61
- <div class="video-container" id="videoContainer">
62
- <video id="videoPlayer" controls></video>
63
- <br>
64
- <a id="downloadLink" class="dl-btn" href="#" download>ذخیره ویدیو در دستگاه</a>
65
- </div>
66
- </div>
67
-
68
- <script>
69
- async function startDownload() {
70
- const url = document.getElementById('urlInput').value;
71
- const btn = document.getElementById('downloadBtn');
72
- const status = document.getElementById('status');
73
- const videoContainer = document.getElementById('videoContainer');
74
- const videoPlayer = document.getElementById('videoPlayer');
75
- const downloadLink = document.getElementById('downloadLink');
76
-
77
- if (!url) return alert('لطفا لینک را وارد کنید');
78
-
79
- btn.disabled = true;
80
- status.innerHTML = "⏳ در حال دریافت لینک دانلود از سرور...";
81
- videoContainer.style.display = 'none';
82
-
83
- try {
84
- const response = await fetch('/api/download', {
85
- method: 'POST',
86
- headers: { 'Content-Type': 'application/json' },
87
- body: JSON.stringify({ url: url })
88
- });
89
-
90
- if (!response.ok) {
91
- const data = await response.json();
92
- throw new Error(data.error || 'خطا در دانلود ویدیو');
93
- }
94
-
95
- const blob = await response.blob();
96
- const videoUrl = URL.createObjectURL(blob);
97
-
98
- videoPlayer.src = videoUrl;
99
- downloadLink.href = videoUrl;
100
- downloadLink.download = "video.mp4";
101
-
102
- status.innerHTML = "✅ ویدیو با موفقیت دریافت شد!";
103
- videoContainer.style.display = 'block';
104
-
105
- } catch (error) {
106
- status.innerHTML = "❌ خطا: " + error.message;
107
- } finally {
108
- btn.disabled = false;
109
- }
110
- }
111
- </script>
112
- </body>
113
- </html>
114
- """
115
-
116
- @app.route('/')
117
- def index():
118
- return render_template_string(HTML_TEMPLATE)
119
-
120
- @app.route('/api/download', methods=['POST'])
121
- def download_video():
122
- data = request.get_json()
123
- if not data or 'url' not in data:
124
- return jsonify({'error': 'URL is required'}), 400
125
-
126
- target_url = data['url']
127
-
128
- # تنظیمات هدر برای شبیه‌سازی کامل مرورگر
129
- headers = {
130
- 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Mobile Safari/537.36',
131
- 'Referer': 'https://app.ytdown.to/en8/',
132
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
133
- 'X-Requested-With': 'XMLHttpRequest',
134
- 'Origin': 'https://app.ytdown.to'
135
- }
136
-
137
- try:
138
- # مرحله 1: درخواست اطلاعات ویدیو از سایت واسط
139
- api_url = "https://app.ytdown.to/proxy.php"
140
- payload = {'url': target_url}
141
-
142
- # گام اول: دریافت لیست کیفیت‌ها
143
- response = requests.post(api_url, data=payload, headers=headers, timeout=15)
144
-
145
- if response.status_code != 200:
146
- return jsonify({'error': 'خطا در ارتباط با سرور دانلودر'}), 500
147
-
148
- json_data = response.json()
149
-
150
- # پیدا کردن بهترین کیفیت (معمولاً 1080p یا 720p)
151
- media_items = json_data.get('mediaItems', [])
152
- if not media_items:
153
- return jsonify({'error': 'هیچ ویدیویی یافت نشد'}), 404
154
-
155
- best_media = None
156
- for item in media_items:
157
- # اولویت با کیفیت بالاتر و پسوند mp4
158
- if item.get('mediaExtension') == 'MP4' and item.get('mediaQuality') in ['FHD', 'HD', '1080p', '720p']:
159
- best_media = item
160
- break
161
-
162
- if not best_media:
163
- best_media = media_items[0] # انتخاب اولین مورد در صورت عدم یافتن کیفیت بالا
164
-
165
- download_url = best_media.get('mediaUrl')
166
-
167
- # مرحله 2: اگر لینک مستقیم نبود و نیاز به تبدیل داشت
168
- if not download_url:
169
- return jsonify({'error': 'لینک دانلود مستقیم یافت نشد'}), 500
170
-
171
- # اگر لینک نیاز به پردازش ثانویه داشت (مثل state=merge)
172
- if 'mediaTask' in best_media and best_media['mediaTask'] == 'merge':
173
- # در برخی موارد نیاز است یک درخواست دوم برای دریافت لینک نهایی ارسال شود
174
- # اما طبق لاگ شما، لینک نهایی در mediaUrl موجود است یا با درخواست GET دریافت می‌شود
175
- pass
176
-
177
- # مرحله 3: دانلود فایل نهایی به سرور خودمان
178
- final_file_name = f"{uuid.uuid4()}.mp4"
179
- final_path = os.path.join(DOWNLOAD_FOLDER, final_file_name)
180
-
181
- with requests.get(download_url, stream=True, headers=headers, timeout=60) as r:
182
- r.raise_for_status()
183
- with open(final_path, 'wb') as f:
184
- for chunk in r.iter_content(chunk_size=8192):
185
- f.write(chunk)
186
-
187
- return send_file(final_path, as_attachment=True, download_name=f"video_{int(time.time())}.mp4")
188
-
189
- except Exception as e:
190
- return jsonify({'error': str(e)}), 500
191
-
192
- if __name__ == '__main__':
193
- port = int(os.environ.get('PORT', 7860))
194
- app.run(host='0.0.0.0', port=port)
 
2
  import time
3
  import uuid
4
  import threading
 
 
5
  from flask import Flask, request, jsonify, send_file, render_template_string
6
  from flask_cors import CORS
7
+ import yt_dlp
8
 
9
  app = Flask(__name__)
10
  CORS(app)
 
12
  DOWNLOAD_FOLDER = 'downloads'
13
  os.makedirs(DOWNLOAD_FOLDER, exist_ok=True)
14
 
15
+ # ==========================================
16
+ # سیستم پاکسازی خودکار فایل‌های قدیمی
17
+ # ==========================================
18
  def cleanup_old_files():
19
+ """این تابع هر یک ساعت اجرا می‌شود و فایل‌های قدیمی‌تر از 1 ساعت را پاک می‌کند تا حافظه پر نشود"""
20
  while True:
21
  try:
22
  current_time = time.time()
 
26
  if current_time - os.path.getctime(file_path) > 3600:
27
  os.remove(file_path)
28
  except Exception as e:
29
+ print(f"Cleanup error: {e}")
30
  time.sleep(3600)
31
 
32
  cleanup_thread = threading.Thread(target=cleanup_old_files, daemon=True)
33
  cleanup_thread.start()
34
 
35
+ # ==========================================
36
+ # رابط کاربری ساده برای تست (رابط وب)
37
+ # ==========================================
38
  HTML_TEMPLATE = """
39
  <!DOCTYPE html>
40
  <html lang="fa" dir="rtl">
 
46
  body { font-family: Tahoma, Arial, sans-serif; background-color: #f3f4f6; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
47
  .container { background: #fff; padding: 30px; border-radius: 12px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); max-width: 500px; width: 100%; text-align: center; }
48
  input { width: 90%; padding: 12px; margin: 15px 0; border: 1px solid #ccc; border-radius: 8px; font-size: 16px; direction: ltr; }
49
+ button { background: #3b82f6; color: white; border: none; padding: 12px 24px