Update app.py
Browse files
app.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
from flask import Flask, send_file, request, make_response, Response
|
2 |
import yt_dlp
|
3 |
import os
|
4 |
-
from urllib.parse import urlparse
|
5 |
from gallery_dl import job
|
6 |
import requests
|
7 |
from datetime import datetime
|
@@ -9,6 +9,7 @@ import tempfile
|
|
9 |
import shutil
|
10 |
import mimetypes
|
11 |
import uuid
|
|
|
12 |
|
13 |
app = Flask(__name__)
|
14 |
|
@@ -24,11 +25,29 @@ class MediaDownloader:
|
|
24 |
os.makedirs(self.video_path, exist_ok=True)
|
25 |
|
26 |
def cleanup(self):
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
|
29 |
def download_video(self, url):
|
|
|
|
|
|
|
30 |
unique_id = str(uuid.uuid4())
|
31 |
output_template = os.path.join(self.video_path, f'{unique_id}.%(ext)s')
|
|
|
32 |
ydl_opts = {
|
33 |
'format': 'best',
|
34 |
'outtmpl': output_template,
|
@@ -47,11 +66,15 @@ class MediaDownloader:
|
|
47 |
if info:
|
48 |
filename = ydl.prepare_filename(info)
|
49 |
return filename if os.path.exists(filename) else None
|
|
|
|
|
50 |
return None
|
51 |
-
|
52 |
-
return None
|
53 |
|
54 |
def download_images(self, url):
|
|
|
|
|
|
|
55 |
try:
|
56 |
class UrlDl(job.Job):
|
57 |
def __init__(self, url, parent=None):
|
@@ -72,14 +95,15 @@ class MediaDownloader:
|
|
72 |
try:
|
73 |
response = requests.get(img_url, headers={
|
74 |
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
75 |
-
})
|
76 |
response.raise_for_status()
|
77 |
|
78 |
unique_id = str(uuid.uuid4())
|
79 |
ext = os.path.splitext(urlparse(img_url).path)[1]
|
80 |
-
if not ext:
|
81 |
ext = '.jpg'
|
82 |
-
|
|
|
83 |
filepath = os.path.join(self.image_path, filename)
|
84 |
|
85 |
with open(filepath, 'wb') as f:
|
@@ -97,6 +121,9 @@ class MediaDownloader:
|
|
97 |
return None
|
98 |
|
99 |
def download_media(self, url):
|
|
|
|
|
|
|
100 |
video_path = self.download_video(url)
|
101 |
if video_path:
|
102 |
return [video_path]
|
@@ -114,8 +141,8 @@ def health_check():
|
|
114 |
@app.route('/')
|
115 |
def home():
|
116 |
return """
|
117 |
-
<h1>
|
118 |
-
<p>Use: /download?url=
|
119 |
"""
|
120 |
|
121 |
@app.route('/download')
|
@@ -125,45 +152,55 @@ def download():
|
|
125 |
if not url:
|
126 |
return "No URL", 400
|
127 |
|
|
|
128 |
downloader = MediaDownloader()
|
129 |
|
130 |
try:
|
131 |
files = downloader.download_media(url)
|
132 |
|
133 |
-
if
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
142 |
for file_path in files:
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
|
|
|
|
|
|
|
|
151 |
downloader.cleanup()
|
152 |
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
else:
|
161 |
-
downloader.cleanup()
|
162 |
-
return "failed", 400
|
163 |
|
164 |
except Exception as e:
|
165 |
downloader.cleanup()
|
166 |
-
return f"
|
167 |
|
168 |
except Exception as e:
|
169 |
return f"Server error: {str(e)}", 500
|
|
|
1 |
from flask import Flask, send_file, request, make_response, Response
|
2 |
import yt_dlp
|
3 |
import os
|
4 |
+
from urllib.parse import urlparse, unquote
|
5 |
from gallery_dl import job
|
6 |
import requests
|
7 |
from datetime import datetime
|
|
|
9 |
import shutil
|
10 |
import mimetypes
|
11 |
import uuid
|
12 |
+
import re
|
13 |
|
14 |
app = Flask(__name__)
|
15 |
|
|
|
25 |
os.makedirs(self.video_path, exist_ok=True)
|
26 |
|
27 |
def cleanup(self):
|
28 |
+
try:
|
29 |
+
if os.path.exists(self.temp_dir):
|
30 |
+
shutil.rmtree(self.temp_dir)
|
31 |
+
except Exception as e:
|
32 |
+
print(f"Cleanup error: {str(e)}")
|
33 |
+
|
34 |
+
def is_valid_url(self, url):
|
35 |
+
try:
|
36 |
+
result = urlparse(url)
|
37 |
+
return all([result.scheme, result.netloc])
|
38 |
+
except:
|
39 |
+
return False
|
40 |
+
|
41 |
+
def sanitize_filename(self, filename):
|
42 |
+
return re.sub(r'[<>:"/\\|?*]', '_', filename)
|
43 |
|
44 |
def download_video(self, url):
|
45 |
+
if not self.is_valid_url(url):
|
46 |
+
return None
|
47 |
+
|
48 |
unique_id = str(uuid.uuid4())
|
49 |
output_template = os.path.join(self.video_path, f'{unique_id}.%(ext)s')
|
50 |
+
|
51 |
ydl_opts = {
|
52 |
'format': 'best',
|
53 |
'outtmpl': output_template,
|
|
|
66 |
if info:
|
67 |
filename = ydl.prepare_filename(info)
|
68 |
return filename if os.path.exists(filename) else None
|
69 |
+
except Exception as e:
|
70 |
+
print(f"Video download error: {str(e)}")
|
71 |
return None
|
72 |
+
return None
|
|
|
73 |
|
74 |
def download_images(self, url):
|
75 |
+
if not self.is_valid_url(url):
|
76 |
+
return None
|
77 |
+
|
78 |
try:
|
79 |
class UrlDl(job.Job):
|
80 |
def __init__(self, url, parent=None):
|
|
|
95 |
try:
|
96 |
response = requests.get(img_url, headers={
|
97 |
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
98 |
+
}, timeout=30)
|
99 |
response.raise_for_status()
|
100 |
|
101 |
unique_id = str(uuid.uuid4())
|
102 |
ext = os.path.splitext(urlparse(img_url).path)[1]
|
103 |
+
if not ext or ext.lower() not in ['.jpg', '.jpeg', '.png', '.gif', '.webp']:
|
104 |
ext = '.jpg'
|
105 |
+
|
106 |
+
filename = self.sanitize_filename(f"{unique_id}{ext}")
|
107 |
filepath = os.path.join(self.image_path, filename)
|
108 |
|
109 |
with open(filepath, 'wb') as f:
|
|
|
121 |
return None
|
122 |
|
123 |
def download_media(self, url):
|
124 |
+
if not url or not self.is_valid_url(url):
|
125 |
+
return None
|
126 |
+
|
127 |
video_path = self.download_video(url)
|
128 |
if video_path:
|
129 |
return [video_path]
|
|
|
141 |
@app.route('/')
|
142 |
def home():
|
143 |
return """
|
144 |
+
<h1>Downloader</h1>
|
145 |
+
<p>Use: /download?url=encoded_url</p>
|
146 |
"""
|
147 |
|
148 |
@app.route('/download')
|
|
|
152 |
if not url:
|
153 |
return "No URL", 400
|
154 |
|
155 |
+
url = unquote(url)
|
156 |
downloader = MediaDownloader()
|
157 |
|
158 |
try:
|
159 |
files = downloader.download_media(url)
|
160 |
|
161 |
+
if not files:
|
162 |
+
downloader.cleanup()
|
163 |
+
return "Download failed", 400
|
164 |
+
|
165 |
+
if len(files) == 1:
|
166 |
+
response = make_response(send_file(
|
167 |
+
files[0],
|
168 |
+
as_attachment=True,
|
169 |
+
download_name=os.path.basename(files[0])
|
170 |
+
))
|
171 |
+
@response.call_on_close
|
172 |
+
def cleanup():
|
173 |
+
downloader.cleanup()
|
174 |
+
return response
|
175 |
+
else:
|
176 |
+
def generate():
|
177 |
+
try:
|
178 |
for file_path in files:
|
179 |
+
if os.path.exists(file_path):
|
180 |
+
with open(file_path, 'rb') as f:
|
181 |
+
content = f.read()
|
182 |
+
filename = os.path.basename(file_path)
|
183 |
+
yield f'--boundary\n'.encode()
|
184 |
+
yield f'Content-Disposition: attachment; filename="{filename}"\n'.encode()
|
185 |
+
yield f'Content-Type: {mimetypes.guess_type(file_path)[0]}\n'.encode()
|
186 |
+
yield f'Content-Length: {len(content)}\n\n'.encode()
|
187 |
+
yield content
|
188 |
+
yield b'\n'
|
189 |
+
yield b'--boundary--\n'
|
190 |
+
finally:
|
191 |
downloader.cleanup()
|
192 |
|
193 |
+
response = Response(
|
194 |
+
generate(),
|
195 |
+
mimetype='multipart/x-mixed-replace; boundary=boundary',
|
196 |
+
direct_passthrough=True
|
197 |
+
)
|
198 |
+
response.headers['Content-Type'] = 'multipart/x-mixed-replace; boundary=boundary'
|
199 |
+
return response
|
|
|
|
|
|
|
200 |
|
201 |
except Exception as e:
|
202 |
downloader.cleanup()
|
203 |
+
return f"Download error: {str(e)}", 500
|
204 |
|
205 |
except Exception as e:
|
206 |
return f"Server error: {str(e)}", 500
|