Spaces:
Runtime error
Runtime error
First version of video dowloader
Browse files- app.py +255 -0
- assets/MFN.min.svg +3 -0
- assets/MFN.svg +12 -0
- assets/facebook.svg +6 -0
- assets/github.svg +6 -0
- assets/instagram.svg +6 -0
- assets/kaggle.svg +6 -0
- assets/linkedin.svg +6 -0
- assets/sitemap.svg +5 -0
- assets/tiktok.svg +6 -0
- assets/twitch.svg +6 -0
- assets/x.svg +6 -0
- assets/youtube.svg +6 -0
- download.py +105 -0
- icons/delete.svg +6 -0
- icons/paste.svg +6 -0
app.py
ADDED
@@ -0,0 +1,255 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import argparse
|
3 |
+
import os
|
4 |
+
from time import sleep
|
5 |
+
from pytube import YouTube
|
6 |
+
import re
|
7 |
+
|
8 |
+
# url = "https://www.youtube.com/watch?v=yX5EJf4R77s"
|
9 |
+
|
10 |
+
html_social_media = '''
|
11 |
+
<div style="float: right;">
|
12 |
+
<a href="https://maximofn.com/" rel="noopener noreferrer" aria-disabled="false" class="sm secondary svelte-cmf5ev" id="component-1" style="flex-grow: 100;" target="_blank">
|
13 |
+
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 576 512">
|
14 |
+
<style>
|
15 |
+
svg {
|
16 |
+
fill:#f3f4f6
|
17 |
+
}
|
18 |
+
</style><path d="M208 80c0-26.5 21.5-48 48-48h64c26.5 0 48 21.5 48 48v64c0 26.5-21.5 48-48 48h-8v40H464c30.9 0 56 25.1 56 56v32h8c26.5 0 48 21.5 48 48v64c0 26.5-21.5 48-48 48H464c-26.5 0-48-21.5-48-48V368c0-26.5 21.5-48 48-48h8V288c0-4.4-3.6-8-8-8H312v40h8c26.5 0 48 21.5 48 48v64c0 26.5-21.5 48-48 48H256c-26.5 0-48-21.5-48-48V368c0-26.5 21.5-48 48-48h8V280H112c-4.4 0-8 3.6-8 8v32h8c26.5 0 48 21.5 48 48v64c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V368c0-26.5 21.5-48 48-48h8V288c0-30.9 25.1-56 56-56H264V192h-8c-26.5 0-48-21.5-48-48V80z"/>
|
19 |
+
</svg>
|
20 |
+
</a>
|
21 |
+
<a href="http://github.com/maximofn" rel="noopener noreferrer" aria-disabled="false" class="sm secondary svelte-cmf5ev" id="component-1" style="flex-grow: 100;" target="_blank">
|
22 |
+
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 496 512">
|
23 |
+
<style>
|
24 |
+
svg {
|
25 |
+
fill: #f3f4f6;
|
26 |
+
}
|
27 |
+
</style>
|
28 |
+
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/>
|
29 |
+
</svg>
|
30 |
+
</a>
|
31 |
+
<a href="http://linkedin.com/in/MaximoFN/" rel="noopener noreferrer" aria-disabled="false" class="sm secondary svelte-cmf5ev" id="component-1" style="flex-grow: 100;" target="_blank">
|
32 |
+
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512">
|
33 |
+
<style>
|
34 |
+
svg {
|
35 |
+
fill:#f3f4f6
|
36 |
+
}
|
37 |
+
</style>
|
38 |
+
<path d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5zm282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9V416z"/>
|
39 |
+
</svg>
|
40 |
+
</a>
|
41 |
+
<a href="http://kaggle.com/maximofn" rel="noopener noreferrer" aria-disabled="false" class="sm secondary svelte-cmf5ev" id="component-1" style="flex-grow: 100;" target="_blank">
|
42 |
+
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 320 512">
|
43 |
+
<style>
|
44 |
+
svg {
|
45 |
+
fill:#f3f4f6
|
46 |
+
}
|
47 |
+
</style>
|
48 |
+
<path d="M304.2 501.5L158.4 320.3 298.2 185c2.6-2.7 1.7-10.5-5.3-10.5h-69.2c-3.5 0-7 1.8-10.5 5.3L80.9 313.5V7.5q0-7.5-7.5-7.5H21.5Q14 0 14 7.5v497q0 7.5 7.5 7.5h51.9q7.5 0 7.5-7.5v-109l30.8-29.3 110.5 140.6c3 3.5 6.5 5.3 10.5 5.3h66.9q5.25 0 6-3z"/>
|
49 |
+
</svg>
|
50 |
+
</a>
|
51 |
+
<a href="https://twitter.com/Maximo_fn" rel="noopener noreferrer" aria-disabled="false" class="sm secondary svelte-cmf5ev" id="component-1" style="flex-grow: 100;" target="_blank">
|
52 |
+
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512">
|
53 |
+
<style>
|
54 |
+
svg {
|
55 |
+
fill:#f3f4f6
|
56 |
+
}
|
57 |
+
</style>
|
58 |
+
<path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"/>
|
59 |
+
</svg>
|
60 |
+
</a>
|
61 |
+
<a href="https://www.instagram.com/maximo__fn/" rel="noopener noreferrer" aria-disabled="false" class="sm secondary svelte-cmf5ev" id="component-1" style="flex-grow: 100;" target="_blank">
|
62 |
+
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512">
|
63 |
+
<style>
|
64 |
+
svg {
|
65 |
+
fill:#f3f4f6
|
66 |
+
}
|
67 |
+
</style>
|
68 |
+
<path d="M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141zm0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7zm146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-12-26.8-26.8s12-26.8 26.8-26.8 26.8 12 26.8 26.8zm76.1 27.2c-1.7-35.9-9.9-67.7-36.2-93.9-26.2-26.2-58-34.4-93.9-36.2-37-2.1-147.9-2.1-184.9 0-35.8 1.7-67.6 9.9-93.9 36.1s-34.4 58-36.2 93.9c-2.1 37-2.1 147.9 0 184.9 1.7 35.9 9.9 67.7 36.2 93.9s58 34.4 93.9 36.2c37 2.1 147.9 2.1 184.9 0 35.9-1.7 67.7-9.9 93.9-36.2 26.2-26.2 34.4-58 36.2-93.9 2.1-37 2.1-147.8 0-184.8zM398.8 388c-7.8 19.6-22.9 34.7-42.6 42.6-29.5 11.7-99.5 9-132.1 9s-102.7 2.6-132.1-9c-19.6-7.8-34.7-22.9-42.6-42.6-11.7-29.5-9-99.5-9-132.1s-2.6-102.7 9-132.1c7.8-19.6 22.9-34.7 42.6-42.6 29.5-11.7 99.5-9 132.1-9s102.7-2.6 132.1 9c19.6 7.8 34.7 22.9 42.6 42.6 11.7 29.5 9 99.5 9 132.1s2.7 102.7-9 132.1z"/>
|
69 |
+
</svg>
|
70 |
+
</a>
|
71 |
+
<a href="https://www.youtube.com/channel/UCdQwg2JU_fWRsHn3yIlf3tw" rel="noopener noreferrer" aria-disabled="false" class="sm secondary svelte-cmf5ev" id="component-1" style="flex-grow: 100;" target="_blank">
|
72 |
+
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 576 512">
|
73 |
+
<style>
|
74 |
+
svg {
|
75 |
+
fill:#f3f4f6
|
76 |
+
}
|
77 |
+
</style>
|
78 |
+
<path d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z"/>
|
79 |
+
</svg>
|
80 |
+
</a>
|
81 |
+
<a href="https://www.facebook.com/profile.php?id=100085177670661" rel="noopener noreferrer" aria-disabled="false" class="sm secondary svelte-cmf5ev" id="component-1" style="flex-grow: 100;" target="_blank">
|
82 |
+
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512">
|
83 |
+
<style>
|
84 |
+
svg {
|
85 |
+
fill:#f3f4f6
|
86 |
+
}
|
87 |
+
</style>
|
88 |
+
<path d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z"/>
|
89 |
+
</svg>
|
90 |
+
</a>
|
91 |
+
<a href="https://www.tiktok.com/@maximo__fn" rel="noopener noreferrer" aria-disabled="false" class="sm secondary svelte-cmf5ev" id="component-1" style="flex-grow: 100;" target="_blank">
|
92 |
+
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512">
|
93 |
+
<style>
|
94 |
+
svg {
|
95 |
+
fill:#f3f4f6
|
96 |
+
}
|
97 |
+
</style>
|
98 |
+
<path d="M448,209.91a210.06,210.06,0,0,1-122.77-39.25V349.38A162.55,162.55,0,1,1,185,188.31V278.2a74.62,74.62,0,1,0,52.23,71.18V0l88,0a121.18,121.18,0,0,0,1.86,22.17h0A122.18,122.18,0,0,0,381,102.39a121.43,121.43,0,0,0,67,20.14Z"/>
|
99 |
+
</svg>
|
100 |
+
</a>
|
101 |
+
<a href="https://www.twitch.tv/maximofn/" rel="noopener noreferrer" aria-disabled="false" class="sm secondary svelte-cmf5ev" id="component-1" style="flex-grow: 100;" target="_blank">
|
102 |
+
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512">
|
103 |
+
<style>
|
104 |
+
svg {
|
105 |
+
fill:#f3f4f6
|
106 |
+
}
|
107 |
+
</style>
|
108 |
+
<path d="M391.17,103.47H352.54v109.7h38.63ZM285,103H246.37V212.75H285ZM120.83,0,24.31,91.42V420.58H140.14V512l96.53-91.42h77.25L487.69,256V0ZM449.07,237.75l-77.22,73.12H294.61l-67.6,64v-64H140.14V36.58H449.07Z"/>
|
109 |
+
</svg>
|
110 |
+
</a>
|
111 |
+
</div>
|
112 |
+
'''
|
113 |
+
|
114 |
+
html_buy_me_a_coffe = '''
|
115 |
+
<div style="float: right;">
|
116 |
+
<a href="https://www.buymeacoffee.com/maximofn" target="_blank">
|
117 |
+
<img src="https://img.shields.io/badge/Buy_Me_A_Coffee-support_my_work-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=white&labelColor=101010" alt="buy me a coffe">
|
118 |
+
</a>
|
119 |
+
</div>
|
120 |
+
'''
|
121 |
+
|
122 |
+
def remove_all_files():
|
123 |
+
command = f"rm -r audios"
|
124 |
+
os.system(command)
|
125 |
+
command = f"rm -r videos"
|
126 |
+
os.system(command)
|
127 |
+
|
128 |
+
def reset_frontend():
|
129 |
+
visible = False
|
130 |
+
return (
|
131 |
+
"",
|
132 |
+
gr.Image(visible=visible),
|
133 |
+
gr.Button(visible=visible),
|
134 |
+
gr.Textbox(visible=visible),
|
135 |
+
gr.Textbox(visible=visible),
|
136 |
+
gr.Audio(visible=visible),
|
137 |
+
gr.Video(visible=visible),
|
138 |
+
)
|
139 |
+
|
140 |
+
def get_youtube_thumbnail(url):
|
141 |
+
yt = YouTube(url)
|
142 |
+
thumbnail_url = yt.thumbnail_url
|
143 |
+
return thumbnail_url
|
144 |
+
|
145 |
+
def is_valid_youtube_url(url):
|
146 |
+
# This regular expression should match the following YouTube URL formats:
|
147 |
+
# - https://youtube.com/watch?v=video_id
|
148 |
+
# - https://www.youtube.com/watch?v=video_id
|
149 |
+
# - https://youtu.be/video_id
|
150 |
+
patron_youtube = r'(https?://)?(www\.)?(youtube\.com/watch\?v=|youtu\.be/)[\w-]+'
|
151 |
+
return bool(re.match(patron_youtube, url))
|
152 |
+
|
153 |
+
def is_valid_twitch_url(url):
|
154 |
+
# This regular expression should match the following Twitch URL formats:
|
155 |
+
# - https://twitch.tv/channel_name
|
156 |
+
# - https://www.twitch.tv/channel_name
|
157 |
+
# - https://twitch.tv/videos/video_id
|
158 |
+
twitch_pattern = r'(https?://)?(www\.)?twitch\.tv/(videos/\d+|\w+)'
|
159 |
+
return bool(re.match(twitch_pattern, url))
|
160 |
+
|
161 |
+
def is_valid_url(url):
|
162 |
+
visible = True
|
163 |
+
width = 600
|
164 |
+
height = 400
|
165 |
+
image = None
|
166 |
+
|
167 |
+
# Youtube
|
168 |
+
if "youtube" in url.lower() or "youtu.be" in url.lower():
|
169 |
+
if is_valid_youtube_url(url):
|
170 |
+
thumbnail = get_youtube_thumbnail(url)
|
171 |
+
if thumbnail:
|
172 |
+
image = thumbnail
|
173 |
+
else:
|
174 |
+
image = "https://maximofn.com/wp-content/uploads/2023/11/youtube-no-thumbnails.webp"
|
175 |
+
# Twitch
|
176 |
+
elif "twitch" in url.lower() or "twitch.tv" in url.lower():
|
177 |
+
if is_valid_twitch_url(url):
|
178 |
+
image = "https://maximofn.com/wp-content/uploads/2023/11/twitch.webp"
|
179 |
+
# Error
|
180 |
+
else:
|
181 |
+
visible = False
|
182 |
+
image = "https://maximofn.com/wp-content/uploads/2023/11/youtube_error.webp"
|
183 |
+
|
184 |
+
return (
|
185 |
+
gr.Image(value=image, visible=visible, width=width, height=height),
|
186 |
+
gr.Button(visible=visible),
|
187 |
+
gr.Audio(visible=visible, show_download_button=False, show_share_button=False, container=True),
|
188 |
+
gr.Video(visible=visible, show_share_button=False, container=True),
|
189 |
+
)
|
190 |
+
|
191 |
+
def download_audio_and_video(url):
|
192 |
+
audios_folder = "audios"
|
193 |
+
videos_folder = "videos"
|
194 |
+
if not os.path.exists(audios_folder):
|
195 |
+
os.makedirs(audios_folder)
|
196 |
+
if not os.path.exists(videos_folder):
|
197 |
+
os.makedirs(videos_folder)
|
198 |
+
|
199 |
+
python_file = "download.py"
|
200 |
+
command = f"python {python_file} {url}"
|
201 |
+
os.system(command)
|
202 |
+
sleep(1)
|
203 |
+
|
204 |
+
audio_path = "audios/download_audio.mp3"
|
205 |
+
video_path = "videos/download_video.mp4"
|
206 |
+
|
207 |
+
return (
|
208 |
+
gr.Textbox(value=audio_path),
|
209 |
+
gr.Textbox(value=video_path),
|
210 |
+
gr.Audio(audio_path, show_download_button=True, show_share_button=True, container=False),
|
211 |
+
gr.Video(video_path, show_share_button=True, container=False),
|
212 |
+
)
|
213 |
+
|
214 |
+
def video_downloader():
|
215 |
+
with gr.Blocks() as demo:
|
216 |
+
# Layout
|
217 |
+
gr.Markdown(html_social_media)
|
218 |
+
gr.Markdown("<h1 style='text-align: center;'>Video downloader</h1>")
|
219 |
+
with gr.Row(variant="panel"):
|
220 |
+
url_textbox = gr.Textbox(placeholder="Add video URL here and wait a moment", label="Video URL", elem_id="video_url", scale=1, interactive=True)
|
221 |
+
# paste_button = gr.Button(size="sm", icon="icons/paste.svg", value="paste", min_width="10px", scale=0)
|
222 |
+
delete_button = gr.Button(size="sm", icon="icons/delete.svg", value="clear", min_width="10px", scale=0)
|
223 |
+
|
224 |
+
visible = False
|
225 |
+
with gr.Row(equal_height=False):
|
226 |
+
image = gr.Image(visible=visible, show_download_button=False, container=False, scale=1)
|
227 |
+
download_button = gr.Button(size="lg", value="Download", min_width="10px", scale=0, visible=visible)
|
228 |
+
|
229 |
+
original_audio_path = gr.Textbox(label="Original audio path", elem_id="original_audio_path", visible=visible)
|
230 |
+
original_video_path = gr.Textbox(label="Original video path", elem_id="original_video_path", visible=visible)
|
231 |
+
with gr.Row():
|
232 |
+
audio = gr.Audio(label="Audio", elem_id="audio", visible=visible, interactive=False)
|
233 |
+
video = gr.Video(label="Video", elem_id="video", visible=visible, interactive=False)
|
234 |
+
|
235 |
+
gr.Markdown(html_buy_me_a_coffe)
|
236 |
+
|
237 |
+
|
238 |
+
# Events
|
239 |
+
url_textbox.change(fn=is_valid_url, inputs=url_textbox, outputs=[image, download_button, audio, video])
|
240 |
+
delete_button.click(fn=reset_frontend, outputs=[url_textbox, image, download_button, original_audio_path, original_video_path, audio, video])
|
241 |
+
download_button.click(fn=download_audio_and_video, inputs=url_textbox, outputs=[original_audio_path, original_video_path, audio, video])
|
242 |
+
|
243 |
+
|
244 |
+
demo.launch()
|
245 |
+
|
246 |
+
|
247 |
+
if __name__ == "__main__":
|
248 |
+
parser = argparse.ArgumentParser()
|
249 |
+
parser.add_argument("--remove_all_files", action="store_true")
|
250 |
+
args = parser.parse_args()
|
251 |
+
|
252 |
+
if args.remove_all_files:
|
253 |
+
remove_all_files()
|
254 |
+
else:
|
255 |
+
video_downloader()
|
assets/MFN.min.svg
ADDED
|
assets/MFN.svg
ADDED
|
assets/facebook.svg
ADDED
|
assets/github.svg
ADDED
|
assets/instagram.svg
ADDED
|
assets/kaggle.svg
ADDED
|
assets/linkedin.svg
ADDED
|
assets/sitemap.svg
ADDED
|
assets/tiktok.svg
ADDED
|
assets/twitch.svg
ADDED
|
assets/x.svg
ADDED
|
assets/youtube.svg
ADDED
|
download.py
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pytube import YouTube
|
2 |
+
import os
|
3 |
+
import argparse
|
4 |
+
import twitchdl.commands as twitch_downloader
|
5 |
+
import twitchdl.twitch
|
6 |
+
from twitchdl.commands.download import _parse_playlists
|
7 |
+
from tqdm import tqdm
|
8 |
+
import re
|
9 |
+
|
10 |
+
VIDEO_FOLDER = 'videos'
|
11 |
+
AUDIO_FOLDER = 'audios'
|
12 |
+
|
13 |
+
DOWNLOAD_VIDEO_NAME = 'download_video'
|
14 |
+
DOWNLOAD_AUDIO_NAME = 'download_audio'
|
15 |
+
|
16 |
+
DOWNLOAD_VIDEO_FORMAT = 'mp4'
|
17 |
+
DOWNLOAD_AUDIO_FORMAT = 'mp3'
|
18 |
+
|
19 |
+
DOWNLOAD_VIDEO = 'video'
|
20 |
+
DOWNLOAD_AUDIO = 'audio'
|
21 |
+
|
22 |
+
def download_twitch(url, type):
|
23 |
+
# Create a new parser for the download command
|
24 |
+
argparser = argparse.ArgumentParser(description='Download twitch video from URL')
|
25 |
+
argparser.add_argument('--auth-token', default=None, help='Twitch auth token')
|
26 |
+
argparser.add_argument('--chapter', default=None, help='Chapter to download')
|
27 |
+
argparser.add_argument('--debug', default=False, help='Debug', action='store_true')
|
28 |
+
argparser.add_argument('--end', default=None, help='End')
|
29 |
+
argparser.add_argument('--format', default=f'{DOWNLOAD_VIDEO_FORMAT}', help='Format')
|
30 |
+
argparser.add_argument('--keep', default=False, help='Keep', action='store_true')
|
31 |
+
argparser.add_argument('--max_workers', default=5, help='Max workers')
|
32 |
+
argparser.add_argument('--no_color', default=False, help='No color', action='store_true')
|
33 |
+
argparser.add_argument('--no_join', default=False, help='No join', action='store_true')
|
34 |
+
argparser.add_argument('--output', default=f'{VIDEO_FOLDER}/{DOWNLOAD_VIDEO_NAME}.{format}', help='Output')
|
35 |
+
argparser.add_argument('--overwrite', default=False, help='Overwrite', action='store_true')
|
36 |
+
argparser.add_argument('--quality', default=None, help='Quality')
|
37 |
+
argparser.add_argument('--rate_limit', default=None, help='Rate limit')
|
38 |
+
argparser.add_argument('--start', default=None, help='Start')
|
39 |
+
argparser.add_argument('--version', default=False, help='Version', action='store_true')
|
40 |
+
argparser.add_argument('videos', default=[url], help='Videos', nargs='+')
|
41 |
+
args = argparser.parse_args()
|
42 |
+
|
43 |
+
# Get video id
|
44 |
+
video_id = re.search(r'(?<=videos\/)\d+', url).group(0)
|
45 |
+
|
46 |
+
# Get qualitys
|
47 |
+
access_token = twitchdl.twitch.get_access_token(video_id, None)
|
48 |
+
playlists_m3u8 = twitchdl.twitch.get_playlists(video_id, access_token)
|
49 |
+
playlists = list(_parse_playlists(playlists_m3u8))
|
50 |
+
qualitys = [name for (name, _, _) in playlists]
|
51 |
+
|
52 |
+
# Select quality
|
53 |
+
if type == DOWNLOAD_VIDEO:
|
54 |
+
args.quality = qualitys[0]
|
55 |
+
args.format = DOWNLOAD_VIDEO_FORMAT
|
56 |
+
args.output = f'{VIDEO_FOLDER}/{DOWNLOAD_VIDEO_NAME}.{args.format}'
|
57 |
+
elif type == DOWNLOAD_AUDIO:
|
58 |
+
args.quality = qualitys[-1]
|
59 |
+
args.format = "mkv"
|
60 |
+
args.output = f'{AUDIO_FOLDER}/{DOWNLOAD_AUDIO_NAME}.{args.format}'
|
61 |
+
|
62 |
+
# Download
|
63 |
+
twitch_downloader.download(args)
|
64 |
+
if type == DOWNLOAD_AUDIO:
|
65 |
+
os.system(f'ffmpeg -i {AUDIO_FOLDER}/{DOWNLOAD_AUDIO_NAME}.{args.format} -c:a libmp3lame -b:a 192k -stats -loglevel warning {AUDIO_FOLDER}/{DOWNLOAD_AUDIO_NAME}.{DOWNLOAD_AUDIO_FORMAT}')
|
66 |
+
os.system(f'rm {AUDIO_FOLDER}/{DOWNLOAD_AUDIO_NAME}.{args.format}')
|
67 |
+
|
68 |
+
def download_youtube_video(url):
|
69 |
+
command = f"yt-dlp -o '{VIDEO_FOLDER}/{DOWNLOAD_VIDEO_NAME}.{DOWNLOAD_VIDEO_FORMAT}' -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]' '{url}'"
|
70 |
+
os.system(command)
|
71 |
+
|
72 |
+
def download_youtube_audio(url):
|
73 |
+
command = f"yt-dlp '{url}' -o '{AUDIO_FOLDER}/{DOWNLOAD_AUDIO_NAME}.{DOWNLOAD_AUDIO_FORMAT}' --extract-audio --audio-format mp3 --audio-quality 0"
|
74 |
+
os.system(command)
|
75 |
+
|
76 |
+
def download_youtube(url, type):
|
77 |
+
if type == DOWNLOAD_VIDEO:
|
78 |
+
download_youtube_video(url)
|
79 |
+
elif type == DOWNLOAD_AUDIO:
|
80 |
+
download_youtube_audio(url)
|
81 |
+
else:
|
82 |
+
print('Unknown youtube download type')
|
83 |
+
|
84 |
+
def main(args):
|
85 |
+
url = args.url
|
86 |
+
num_works = 2
|
87 |
+
download_progress_bar = tqdm(total=num_works, desc='Downloading video and audio progress')
|
88 |
+
if 'twitch' in url.lower():
|
89 |
+
download_twitch(url, DOWNLOAD_VIDEO)
|
90 |
+
download_progress_bar.update(1)
|
91 |
+
download_twitch(url, DOWNLOAD_AUDIO)
|
92 |
+
download_progress_bar.update(1)
|
93 |
+
elif 'youtube' in url.lower() or 'youtu.be' in url.lower():
|
94 |
+
download_youtube(url, DOWNLOAD_VIDEO)
|
95 |
+
download_progress_bar.update(1)
|
96 |
+
download_youtube(url, DOWNLOAD_AUDIO)
|
97 |
+
download_progress_bar.update(1)
|
98 |
+
else:
|
99 |
+
print('Unknown video source')
|
100 |
+
|
101 |
+
if __name__ == "__main__":
|
102 |
+
argparser = argparse.ArgumentParser(description='Download video from URL')
|
103 |
+
argparser.add_argument('url', help='URL of video')
|
104 |
+
args = argparser.parse_args()
|
105 |
+
main(args)
|
icons/delete.svg
ADDED
|
icons/paste.svg
ADDED
|