Spaces:
Running
on
Zero
Running
on
Zero
Create video.py
Browse files- tools/download/video.py +146 -0
tools/download/video.py
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import io
|
2 |
+
from contextlib import redirect_stderr
|
3 |
+
from tempfile import mkdtemp
|
4 |
+
from typing import List
|
5 |
+
|
6 |
+
import yt_dlp
|
7 |
+
from yt_dlp import YoutubeDL
|
8 |
+
from yt_dlp.postprocessor import PostProcessor
|
9 |
+
|
10 |
+
|
11 |
+
class FilenameCollectorPP(PostProcessor):
|
12 |
+
def __init__(self):
|
13 |
+
super(FilenameCollectorPP, self).__init__(None)
|
14 |
+
self.filenames = []
|
15 |
+
|
16 |
+
def run(self, information):
|
17 |
+
self.filenames.append(information["filepath"])
|
18 |
+
return [], information
|
19 |
+
|
20 |
+
|
21 |
+
def download_url(
|
22 |
+
url: str,
|
23 |
+
maxDuration: int = None,
|
24 |
+
destinationDirectory: str = None,
|
25 |
+
playlistItems: str = "1",
|
26 |
+
cookies_from_browser: str = None,
|
27 |
+
) -> List[str]:
|
28 |
+
try:
|
29 |
+
return _perform_download(
|
30 |
+
url,
|
31 |
+
maxDuration=maxDuration,
|
32 |
+
outputTemplate=None,
|
33 |
+
destinationDirectory=destinationDirectory,
|
34 |
+
playlistItems=playlistItems,
|
35 |
+
cookies_from_browser=cookies_from_browser,
|
36 |
+
)
|
37 |
+
except yt_dlp.utils.DownloadError as e:
|
38 |
+
# In case of an OS error, try again with a different output template
|
39 |
+
if e.msg and e.msg.find("[Errno 36] File name too long") >= 0:
|
40 |
+
return _perform_download(
|
41 |
+
url,
|
42 |
+
maxDuration=maxDuration,
|
43 |
+
outputTemplate="%(title).10s %(id)s.%(ext)s",
|
44 |
+
cookies_from_browser=cookies_from_browser,
|
45 |
+
)
|
46 |
+
raise
|
47 |
+
|
48 |
+
|
49 |
+
def _perform_download(
|
50 |
+
url: str,
|
51 |
+
maxDuration: int = None,
|
52 |
+
outputTemplate: str = None,
|
53 |
+
destinationDirectory: str = None,
|
54 |
+
playlistItems: str = "1",
|
55 |
+
onlyAudio: bool = False,
|
56 |
+
cookies_from_browser: str = None,
|
57 |
+
):
|
58 |
+
# Create a temporary directory to store the downloaded files
|
59 |
+
if destinationDirectory is None:
|
60 |
+
destinationDirectory = mkdtemp()
|
61 |
+
|
62 |
+
ydl_opts = {
|
63 |
+
"format": "bestaudio/best"
|
64 |
+
if onlyAudio
|
65 |
+
else "bestvideo[ext=mp4][vcodec^=avc1]+bestaudio[ext=m4a]/best",
|
66 |
+
"paths": {"home": destinationDirectory},
|
67 |
+
"ignoreerrors": True,
|
68 |
+
}
|
69 |
+
|
70 |
+
if playlistItems:
|
71 |
+
ydl_opts["playlist_items"] = playlistItems
|
72 |
+
|
73 |
+
if cookies_from_browser:
|
74 |
+
ydl_opts["cookies_from_browser"] = cookies_from_browser
|
75 |
+
|
76 |
+
# Add output template if specified
|
77 |
+
if outputTemplate:
|
78 |
+
ydl_opts["outtmpl"] = outputTemplate
|
79 |
+
|
80 |
+
errStrIO = EventStringIO(on_write=lambda text: print(f"\033[91m{text}\033[0m"))
|
81 |
+
|
82 |
+
filename_collector = FilenameCollectorPP()
|
83 |
+
with redirect_stderr(errStrIO):
|
84 |
+
for _ in (True,):
|
85 |
+
with YoutubeDL(ydl_opts) as ydl:
|
86 |
+
if maxDuration and maxDuration > 0:
|
87 |
+
info = ydl.extract_info(url, download=False)
|
88 |
+
if not info:
|
89 |
+
break
|
90 |
+
|
91 |
+
entries = "entries" in info and info["entries"] or [info]
|
92 |
+
|
93 |
+
total_duration = 0
|
94 |
+
|
95 |
+
# Compute total duration
|
96 |
+
for entry in entries:
|
97 |
+
if entry:
|
98 |
+
total_duration += float(entry["duration"])
|
99 |
+
|
100 |
+
if total_duration >= maxDuration:
|
101 |
+
raise ExceededMaximumDuration(
|
102 |
+
videoDuration=total_duration,
|
103 |
+
maxDuration=maxDuration,
|
104 |
+
message="Video is too long",
|
105 |
+
)
|
106 |
+
|
107 |
+
ydl.add_post_processor(filename_collector)
|
108 |
+
ydl.download([url])
|
109 |
+
|
110 |
+
errMsg = errStrIO.getvalue()
|
111 |
+
errMsg = (
|
112 |
+
[text for text in errMsg.split("\n") if text.startswith("ERROR")]
|
113 |
+
if errMsg
|
114 |
+
else ""
|
115 |
+
)
|
116 |
+
|
117 |
+
if len(filename_collector.filenames) <= 0:
|
118 |
+
raise Exception(
|
119 |
+
f"Cannot download {url}, " + "\n".join(errMsg) if errMsg else ""
|
120 |
+
)
|
121 |
+
|
122 |
+
result = []
|
123 |
+
|
124 |
+
for filename in filename_collector.filenames:
|
125 |
+
result.append(filename)
|
126 |
+
print("Downloaded " + filename)
|
127 |
+
|
128 |
+
return result
|
129 |
+
|
130 |
+
|
131 |
+
class ExceededMaximumDuration(Exception):
|
132 |
+
def __init__(self, videoDuration, maxDuration, message):
|
133 |
+
self.videoDuration = videoDuration
|
134 |
+
self.maxDuration = maxDuration
|
135 |
+
super().__init__(message)
|
136 |
+
|
137 |
+
|
138 |
+
class EventStringIO(io.StringIO):
|
139 |
+
def __init__(self, on_write=None, *args, **kwargs):
|
140 |
+
super().__init__(*args, **kwargs)
|
141 |
+
self.on_write = on_write
|
142 |
+
|
143 |
+
def write(self, text):
|
144 |
+
super().write(text)
|
145 |
+
if self.on_write:
|
146 |
+
self.on_write(text)
|