Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
8 |
+
import tempfile
|
9 |
+
import shutil
|
10 |
+
import mimetypes
|
11 |
+
|
12 |
+
app = Flask(__name__)
|
13 |
+
|
14 |
+
class MediaDownloader:
|
15 |
+
def __init__(self):
|
16 |
+
self.temp_dir = tempfile.mkdtemp()
|
17 |
+
self.create_directories()
|
18 |
+
|
19 |
+
def create_directories(self):
|
20 |
+
self.image_path = os.path.join(self.temp_dir, "images")
|
21 |
+
self.video_path = os.path.join(self.temp_dir, "videos")
|
22 |
+
os.makedirs(self.image_path, exist_ok=True)
|
23 |
+
os.makedirs(self.video_path, exist_ok=True)
|
24 |
+
|
25 |
+
def cleanup(self):
|
26 |
+
shutil.rmtree(self.temp_dir)
|
27 |
+
|
28 |
+
def download_video(self, url):
|
29 |
+
output_template = os.path.join(self.video_path, '%(title)s.%(ext)s')
|
30 |
+
ydl_opts = {
|
31 |
+
'format': 'best',
|
32 |
+
'outtmpl': output_template,
|
33 |
+
'ignoreerrors': True,
|
34 |
+
'no_warnings': True,
|
35 |
+
'quiet': True,
|
36 |
+
'extract_flat': False,
|
37 |
+
'http_headers': {
|
38 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
+
try:
|
43 |
+
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
44 |
+
info = ydl.extract_info(url, download=True)
|
45 |
+
if info:
|
46 |
+
filename = ydl.prepare_filename(info)
|
47 |
+
return filename if os.path.exists(filename) else None
|
48 |
+
return None
|
49 |
+
except:
|
50 |
+
return None
|
51 |
+
|
52 |
+
def download_images(self, url):
|
53 |
+
try:
|
54 |
+
class UrlDl(job.Job):
|
55 |
+
def __init__(self, url, parent=None):
|
56 |
+
job.Job.__init__(self, url, parent)
|
57 |
+
self.urls = []
|
58 |
+
|
59 |
+
def handle_url(self, url, _):
|
60 |
+
self.urls.append(url)
|
61 |
+
|
62 |
+
j = UrlDl(url)
|
63 |
+
j.run()
|
64 |
+
|
65 |
+
if not j.urls:
|
66 |
+
return None
|
67 |
+
|
68 |
+
downloaded_files = []
|
69 |
+
for img_url in j.urls:
|
70 |
+
try:
|
71 |
+
response = requests.get(img_url, headers={
|
72 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
73 |
+
})
|
74 |
+
response.raise_for_status()
|
75 |
+
|
76 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
|
77 |
+
ext = os.path.splitext(urlparse(img_url).path)[1]
|
78 |
+
if not ext:
|
79 |
+
ext = '.jpg'
|
80 |
+
filename = f"image_{timestamp}{ext}"
|
81 |
+
filepath = os.path.join(self.image_path, filename)
|
82 |
+
|
83 |
+
with open(filepath, 'wb') as f:
|
84 |
+
f.write(response.content)
|
85 |
+
downloaded_files.append(filepath)
|
86 |
+
|
87 |
+
except Exception as e:
|
88 |
+
print(f"Error downloading image {img_url}: {str(e)}")
|
89 |
+
continue
|
90 |
+
|
91 |
+
return downloaded_files if downloaded_files else None
|
92 |
+
|
93 |
+
except Exception as e:
|
94 |
+
print(f"Error in download_images: {str(e)}")
|
95 |
+
return None
|
96 |
+
|
97 |
+
def download_media(self, url):
|
98 |
+
video_path = self.download_video(url)
|
99 |
+
if video_path:
|
100 |
+
return [video_path]
|
101 |
+
|
102 |
+
image_paths = self.download_images(url)
|
103 |
+
if image_paths:
|
104 |
+
return image_paths
|
105 |
+
|
106 |
+
return None
|
107 |
+
|
108 |
+
@app.route('/')
|
109 |
+
def home():
|
110 |
+
return """
|
111 |
+
<h1>download</h1>
|
112 |
+
<p>Use: /download?url=YOUR_URL_HERE</p>
|
113 |
+
"""
|
114 |
+
|
115 |
+
@app.route('/download')
|
116 |
+
def download():
|
117 |
+
try:
|
118 |
+
url = request.args.get('url')
|
119 |
+
if not url:
|
120 |
+
return "No URL provided", 400
|
121 |
+
|
122 |
+
downloader = MediaDownloader()
|
123 |
+
|
124 |
+
try:
|
125 |
+
files = downloader.download_media(url)
|
126 |
+
|
127 |
+
if files and len(files) > 0:
|
128 |
+
if len(files) == 1:d
|
129 |
+
response = make_response(send_file(files[0], as_attachment=True))
|
130 |
+
@response.call_on_close
|
131 |
+
def cleanup():
|
132 |
+
downloader.cleanup()
|
133 |
+
return response
|
134 |
+
else:
|
135 |
+
def generate():
|
136 |
+
for file_path in files:
|
137 |
+
with open(file_path, 'rb') as f:
|
138 |
+
content = f.read()
|
139 |
+
filename = os.path.basename(file_path)
|
140 |
+
yield f'Content-Disposition: attachment; filename="{filename}"\n'.encode()
|
141 |
+
yield f'Content-Type: {mimetypes.guess_type(file_path)[0]}\n'.encode()
|
142 |
+
yield f'Content-Length: {len(content)}\n\n'.encode()
|
143 |
+
yield content
|
144 |
+
yield b'\n--boundary--\n'
|
145 |
+
downloader.cleanup()
|
146 |
+
|
147 |
+
response = Response(
|
148 |
+
generate(),
|
149 |
+
mimetype='multipart/x-mixed-replace; boundary=boundary',
|
150 |
+
direct_passthrough=True
|
151 |
+
)
|
152 |
+
response.headers['Content-Type'] = 'multipart/x-mixed-replace; boundary=boundary'
|
153 |
+
return response
|
154 |
+
else:
|
155 |
+
downloader.cleanup()
|
156 |
+
return "Failed to download media", 400
|
157 |
+
|
158 |
+
except Exception as e:
|
159 |
+
downloader.cleanup()
|
160 |
+
return f"Error: {str(e)}", 500
|
161 |
+
|
162 |
+
except Exception as e:
|
163 |
+
return f"Error: {str(e)}", 500
|
164 |
+
|
165 |
+
if __name__ == '__main__':
|
166 |
+
app.run(host='0.0.0.0', port=7860, debug=True)
|