1
0
mirror of https://github.com/ytdl-org/youtube-dl.git synced 2026-06-11 07:00:15 +00:00

Compare commits

..

41 Commits

Author SHA1 Message Date
Ricardo Garcia 0ddf38df18 Bump version string 2010-10-31 11:23:31 +01:00
Ricardo Garcia 65cd34c5d7 Add initial version of postprocessing framework 2010-10-31 11:23:31 +01:00
Ricardo Garcia 5352678576 Improve a couple of detection strings to avoid i18n-related bugs 2010-10-31 11:23:31 +01:00
Ricardo Garcia a7d06f400c Increase version number 2010-10-31 11:23:31 +01:00
Ricardo Garcia b1a1f8ea8f Improve error message regarding output templates and charsets 2010-10-31 11:23:31 +01:00
Ricardo Garcia f807dc157e Update webpage to reflect changes 2010-10-31 11:23:31 +01:00
Ricardo Garcia 97accc0ece Simplify a statement 2010-10-31 11:23:31 +01:00
Ricardo Garcia 76a7f36400 Make the most prominent output strings Unicode and fix Unicode title bug 2010-10-31 11:23:31 +01:00
Ricardo Garcia 0c2dc87d9e Add YoutubePlaylistIE class 2010-10-31 11:23:31 +01:00
Ricardo Garcia 020f7150aa Add metacafe.com support and minor changse 2010-10-31 11:23:31 +01:00
Ricardo Garcia 3af1e17284 Fix directory creation not working with absolute paths 2010-10-31 11:23:31 +01:00
Ricardo Garcia acd3d84298 Add --rate-limit program option 2010-10-31 11:23:31 +01:00
Ricardo Garcia 7337efbfe4 Modify ignore filters 2010-10-31 11:23:31 +01:00
Ricardo Garcia 3c53b78720 Strip newline from version 2010-10-31 11:23:31 +01:00
Ricardo Garcia bb02834692 Add script to regenerate index.html 2010-10-31 11:23:30 +01:00
Ricardo Garcia 3e1cabc338 Add old version of webpage 2010-10-31 11:23:30 +01:00
Ricardo Garcia d7bc253bb8 Fix video format command line option parsing and set version number 2010-10-31 11:23:26 +01:00
Ricardo Garcia bafa5cd969 Convert direct uses of .to_stdout() into methods to ease inheritation 2008-07-22 22:40:50 +02:00
Ricardo Garcia 90663284b2 Add --mobile-version program option 2008-07-22 22:18:51 +02:00
Ricardo Garcia 2130d8f6a4 Make the downloader print the name of the destination file 2008-07-22 15:53:22 +02:00
Ricardo Garcia e5bf0f551b Add a couple of exceptions to avoid exiting from class methods 2008-07-22 15:52:56 +02:00
Ricardo Garcia 38acff0e77 Minor comment 2008-07-22 15:26:32 +02:00
Ricardo Garcia 1c5e23021e Improve program documentation 2008-07-22 11:41:25 +02:00
Ricardo Garcia 0086d1ec32 Add .trouble() method to downloader to ease tracing retcode 2008-07-22 11:33:41 +02:00
Ricardo Garcia bb681b88df Unify uppercase criteria in errors and trace return code 2008-07-22 11:16:32 +02:00
Ricardo Garcia 98a1fe038c Replace the directory separator in title 2008-07-22 11:05:22 +02:00
Ricardo Garcia e8e20c76f2 Fix not taking into account outs.outtmpl 2008-07-22 11:00:38 +02:00
Ricardo Garcia ae2e6e461c Remove useless comments 2008-07-22 10:57:19 +02:00
Ricardo Garcia 209e9e27e7 Add command line parse code 2008-07-22 10:56:54 +02:00
Ricardo Garcia f9f1e79879 Minor improvements and changes 2008-07-22 10:14:13 +02:00
Ricardo Garcia 656a7dc973 Modify ignore list 2008-07-22 10:01:37 +02:00
Ricardo Garcia 05a84b35cc Add forceurl and forcetitle downloader options 2008-07-22 10:00:43 +02:00
Ricardo Garcia 7414bdf11d Update and fix User-Agent header 2008-07-22 09:55:32 +02:00
Ricardo Garcia f97c8db74e Improve some unicode regular expressions 2008-07-22 09:53:05 +02:00
Ricardo Garcia 22899cea59 Improve fixed template checks 2008-07-22 09:45:49 +02:00
Ricardo Garcia 14c300687e Detect malformed output template 2008-07-22 09:21:43 +02:00
Ricardo Garcia 7e5cab6730 Add .to_stderr() to downloaders 2008-07-22 00:07:07 +02:00
Ricardo Garcia b609fd549f Add support for the "simulate" option 2008-07-21 23:58:16 +02:00
Ricardo Garcia 9fcd8355e5 Correct a few formatting bugs and add .to_stdout() to downloader 2008-07-21 23:53:06 +02:00
Ricardo Garcia b46347267a Check the output name is not fixed when there are several videos to download 2008-07-21 23:29:06 +02:00
Ricardo Garcia 4fa74b5252 Create initial preview version of the new youtube-dl 2008-07-21 23:12:31 +02:00
23 changed files with 1161 additions and 30124 deletions
-20
View File
@@ -1,20 +0,0 @@
updates_key.pem
*~
*.pyc
*.pyo
*.DS_Store
wine-py2exe/
py2exe.log
*.kate-swp
build/
dist/
MANIFEST
README.txt
youtube-dl.1
youtube-dl.bash-completion
youtube-dl
youtube-dl.exe
youtube-dl.tar.gz
.coverage
cover/
youtube_dl.egg-info/
+4
View File
@@ -0,0 +1,4 @@
syntax: glob
index.html
youtube-dl-*
.*.swp
-50
View File
@@ -1,50 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>About youtube-dl</title>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<table class="heading"><tr>
<td class="title"><a href="index.html">youtube-dl</a></td>
<td class="subtitle">About the program</td>
</tr></table>
<h1>What is it?</h1>
<p><em>youtube-dl</em> is a command-line program to download videos from YouTube.com and a few <a href="supportedsites.html" style="color: blue; text-decoration: underline;">more sites</a>. It requires the <a href="http://www.python.org/">Python interpreter</a>, version 2.6, 2.7, or 3.2+, and it is not platform specific. It should work in your Unix box, in Windows or in Mac OS X. It is released to the public domain, which means you can modify it, redistribute it or use it however you like. The project is currently being developed at <a href="https://github.com/ytdl-org/youtube-dl/"><strong>GitHub</strong></a>.</p>
<h1>Authors</h1>
<p>Core team:</p>
<ul>
<li><a href="https://github.com/remitamine">Remita Amine</a>: core developer.</li>
</ul>
<p>Core team (inactive):</p>
<ul>
<li><a href="https://github.com/rg3">Ricardo Garcia Gonzalez</a>: original author, program core.</li>
<li><a href="https://github.com/phihag">Philipp Hagemeister</a>: core developer, maintainer 2011-2016.</li>
<li><a href="https://github.com/FiloSottile">Filippo Valsorda</a>: core developer, Windows build, testing.</li>
<li><a href="https://github.com/jaimeMF">Jaime Marquínez Ferrándiz</a>: core developer.</li>
<li><a href="https://github.com/yan12125">Yen Chi Hsuan</a>: core developer.</li>
<li><a href="https://github.com/naglis">Naglis Jonaitis</a>: core developer.</li>
<li><a href="https://github.com/pulpe">pulpe</a>: core developer.</li>
<li><a href="https://github.com/dstftw">Sergey M.</a>: core developer, maintainer 2016-2021.</li>
</ul>
<p>Many <a href="https://github.com/ytdl-org/youtube-dl/blob/master/AUTHORS">other people</a> contributing patches, code, ideas and kind messages. <a href="https://github.com/ytdl-org/youtube-dl/graphs/contributors">Too many</a> to be listed here. You know who you are. Thank you very much.</p>
<div class="note">
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_US">
<img alt="Creative Commons License" style="border-width:0"
src="https://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a><br />
Copyright © 2006-2011 Ricardo Garcia Gonzalez<br />
Copyright © 2011-2021 youtube-dl developers
</div>
</body>
</html>
-7
View File
@@ -1,7 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="refresh" content="0; url=https://github.com/ytdl-org/youtube-dl/blob/master/README.md#readme">
</head>
</html>
-77
View File
@@ -1,77 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>youtube-dl: Download Page</title>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<table class="heading"><tr>
<td class="title"><a href="index.html">youtube-dl</a></td>
<td class="subtitle">Download Page</td>
</tr></table>
<p>Remember <em>youtube-dl</em> requires <a href="http://www.python.org/">Python</a> version 2.6, 2.7, or 3.2+ to work except for Windows exe.</p>
<p><a href="https://yt-dl.org/downloads/2021.12.17/youtube-dl.exe">Windows exe</a> requires <a href="https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x86.exe">Microsoft Visual C++ 2010 Service Pack 1 Redistributable Package (x86)</a> and does not require Python that is already embedded into the binary.</p>
<h2><a href="https://yt-dl.org/downloads/2021.12.17/youtube-dl">2021.12.17</a> (<a href="https://yt-dl.org/downloads/2021.12.17/youtube-dl.sig">sig</a>)</h2>
<p><strong>SHA256</strong>: 7880e01abe282c7fd596f429c35189851180d6177302bb215be1cdec78d6d06d</p>
<p>
<a href="https://yt-dl.org/downloads/2021.12.17/youtube-dl.exe">Windows exe</a> (<a href="https://yt-dl.org/downloads/2021.12.17/youtube-dl.exe.sig">sig</a> - SHA256 26e5c00c35c5c3edc86dfc0a720aed109a13b1b7c67ac654a0ce8ff82a1f2c16)<br>
<a href="https://yt-dl.org/downloads/2021.12.17/youtube-dl-2021.12.17.tar.gz">Full source + docs + binary tarball</a> (<a href="https://yt-dl.org/downloads/2021.12.17/youtube-dl-2021.12.17.tar.gz.sig">sig</a> - SHA256 9f3b99c8b778455165b4525f21505e86c7ff565f3ac319e19733d810194135df)
</p>
<p>To install it right away for all UNIX users (Linux, OS X, etc.), type:
<code class="commands">sudo curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl<br/>
sudo chmod a+rx /usr/local/bin/youtube-dl</code>
</p>
<p>If you do not have curl, you can alternatively use a recent wget:
<code class="commands">sudo wget https://yt-dl.org/downloads/latest/youtube-dl -O /usr/local/bin/youtube-dl<br/>
sudo chmod a+rx /usr/local/bin/youtube-dl</code>
</p>
<p>You can also use pip:
<code class="commands">sudo pip install --upgrade youtube_dl</code>
</p>
<p>
This command will update youtube-dl if you have already installed it.
See the <a href="https://pypi.python.org/pypi/youtube_dl">pypi page</a> for more information.
</p>
<p>You can use Homebrew if you have it:
<code class="commands">brew install youtube-dl</code>
</p>
<p>To check the signature, type:
<code class="commands">sudo wget https://yt-dl.org/downloads/latest/youtube-dl.sig -O youtube-dl.sig<br/>gpg --verify youtube-dl.sig /usr/local/bin/youtube-dl<br/>rm youtube-dl.sig</code>
</p>
<p>The following GPG keys will be used to sign the binaries and the git tags:</p>
<ul>
<li>Sergey M. <code class="fingerprint"><a href="https://dstftw.github.io/keys/18A9236D.asc">ED7F 5BF4 6B3B BED8 1C87 368E 2C39 3E0F 18A9 236D</a></code></li>
</ul>
<p>Older releases are also signed with one of:
<ul>
<li>Philipp Hagemeister <code class="fingerprint"><a href="https://phihag.de/keys/A4826A18.asc">7D33 D762 FD6C 3513 0481 347F DB4B 54CB A482 6A18</a></code> (until 2016-05-30)</li>
<li>Philipp Hagemeister <code class="fingerprint">0600 E1DB 6FB5 3A5D 95D8 FC0D F5EA B582 FAFB 085C</code> (until 2013-06-01)</li>
</li>
<li>Filippo Valsorda <code class="fingerprint">428D F5D6 3EF0 7494 BB45 5AC0 EBF0 1804 BCF0 5F6B</code> (until 2014)</li>
</p>
<div class="note">
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_US">
<img alt="Creative Commons License" style="border-width:0"
src="https://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a><br />
Copyright © 2006-2011 Ricardo Garcia Gonzalez<br />
Copyright © 2011-2021 youtube-dl developers
</div>
</body>
</html>
-77
View File
@@ -1,77 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>youtube-dl: Download Page</title>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<table class="heading"><tr>
<td class="title"><a href="index.html">youtube-dl</a></td>
<td class="subtitle">Download Page</td>
</tr></table>
<p>Remember <em>youtube-dl</em> requires <a href="http://www.python.org/">Python</a> version 2.6, 2.7, or 3.2+ to work except for Windows exe.</p>
<p><a href="@EXE_URL@">Windows exe</a> requires <a href="https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x86.exe">Microsoft Visual C++ 2010 Service Pack 1 Redistributable Package (x86)</a> and does not require Python that is already embedded into the binary.</p>
<h2><a href="@PROGRAM_URL@">@PROGRAM_VERSION@</a> (<a href="@PROGRAM_URL@.sig">sig</a>)</h2>
<p><strong>SHA256</strong>: @PROGRAM_SHA256SUM@</p>
<p>
<a href="@EXE_URL@">Windows exe</a> (<a href="@EXE_URL@.sig">sig</a> - SHA256 @EXE_SHA256SUM@)<br>
<a href="@TAR_URL@">Full source + docs + binary tarball</a> (<a href="@TAR_URL@.sig">sig</a> - SHA256 @TAR_SHA256SUM@)
</p>
<p>To install it right away for all UNIX users (Linux, OS X, etc.), type:
<code class="commands">sudo curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl<br/>
sudo chmod a+rx /usr/local/bin/youtube-dl</code>
</p>
<p>If you do not have curl, you can alternatively use a recent wget:
<code class="commands">sudo wget https://yt-dl.org/downloads/latest/youtube-dl -O /usr/local/bin/youtube-dl<br/>
sudo chmod a+rx /usr/local/bin/youtube-dl</code>
</p>
<p>You can also use pip:
<code class="commands">sudo pip install --upgrade youtube_dl</code>
</p>
<p>
This command will update youtube-dl if you have already installed it.
See the <a href="https://pypi.python.org/pypi/youtube_dl">pypi page</a> for more information.
</p>
<p>You can use Homebrew if you have it:
<code class="commands">brew install youtube-dl</code>
</p>
<p>To check the signature, type:
<code class="commands">sudo wget https://yt-dl.org/downloads/latest/youtube-dl.sig -O youtube-dl.sig<br/>gpg --verify youtube-dl.sig /usr/local/bin/youtube-dl<br/>rm youtube-dl.sig</code>
</p>
<p>The following GPG keys will be used to sign the binaries and the git tags:</p>
<ul>
<li>Sergey M. <code class="fingerprint"><a href="https://dstftw.github.io/keys/18A9236D.asc">ED7F 5BF4 6B3B BED8 1C87 368E 2C39 3E0F 18A9 236D</a></code></li>
</ul>
<p>Older releases are also signed with one of:
<ul>
<li>Philipp Hagemeister <code class="fingerprint"><a href="https://phihag.de/keys/A4826A18.asc">7D33 D762 FD6C 3513 0481 347F DB4B 54CB A482 6A18</a></code> (until 2016-05-30)</li>
<li>Philipp Hagemeister <code class="fingerprint">0600 E1DB 6FB5 3A5D 95D8 FC0D F5EA B582 FAFB 085C</code> (until 2013-06-01)</li>
</li>
<li>Filippo Valsorda <code class="fingerprint">428D F5D6 3EF0 7494 BB45 5AC0 EBF0 1804 BCF0 5F6B</code> (until 2014)</li>
</p>
<div class="note">
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_US">
<img alt="Creative Commons License" style="border-width:0"
src="https://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a><br />
Copyright © 2006-2011 Ricardo Garcia Gonzalez<br />
Copyright © 2011-2021 youtube-dl developers
</div>
</body>
</html>
-7
View File
@@ -1,7 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="refresh" content="0; url=https://github.com/ytdl-org/youtube-dl/blob/master/README.md#faq">
</head>
</html>
Executable
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env python
import hashlib
import subprocess
template = file('index.html.in', 'r').read()
version = subprocess.Popen(['./youtube-dl', '--version'], stdout=subprocess.PIPE).communicate()[0].strip()
data = file('youtube-dl', 'rb').read()
md5sum = hashlib.md5(data).hexdigest()
sha1sum = hashlib.sha1(data).hexdigest()
sha256sum = hashlib.sha256(data).hexdigest()
template = template.replace('@PROGRAM_VERSION@', version)
template = template.replace('@PROGRAM_MD5SUM@', md5sum)
template = template.replace('@PROGRAM_SHA1SUM@', sha1sum)
template = template.replace('@PROGRAM_SHA256SUM@', sha256sum)
file('index.html', 'w').write(template)
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 435 B

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 B

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 B

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 B

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

-37
View File
@@ -1,37 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>youtube-dl</title>
<link rel="stylesheet" href="style.css" type="text/css" />
<link rel="alternate" type="application/atom+xml" title="youtube-dl releases" href="update/releases.atom" />
</head>
<body>
<table class="heading"><tr>
<td class="title"><a href="index.html">youtube-dl</a></td>
<td class="subtitle">Download videos from YouTube
(and <a href="supportedsites.html" style="color: blue; text-decoration: underline;">more sites</a>)</td>
</tr></table>
<p><em>youtube-dl</em> is a command-line program to download videos from YouTube.com and a few <a href="supportedsites.html" style="color: blue; text-decoration: underline;">more sites</a>. It requires the <a href="http://www.python.org/">Python interpreter</a> (2.6, 2.7, or 3.2+), and it is not platform specific. We also provide a <a href="https://yt-dl.org/latest/youtube-dl.exe">Windows executable</a> that includes Python. youtube-dl should work in your Unix box, in Windows or in Mac OS X. It is released to the public domain, which means you can modify it, redistribute it or use it however you like.</p>
<table border="0" id="rgb">
<tr><td><a class="button" id="r" href="https://github.com/ytdl-org/youtube-dl/blob/master/README.md#readme">Documentation</a></td></tr>
<tr><td><a class="button" id="g" href="download.html">Download</a></td></tr>
<tr><td><a class="button" id="main-support" href="https://github.com/ytdl-org/youtube-dl/issues/new/choose">Support</a></td></tr>
<tr><td><a class="button" id="y" href="https://github.com/ytdl-org/youtube-dl/">Develop</a></td></tr>
<tr><td><a class="button" id="b" href="about.html">About</a></td></tr>
</table>
<p>You can also contact us on the irc channel <a href="irc://chat.freenode.net/#youtube-dl">#youtube-dl</a> (<a href="http://webchat.freenode.net/?randomnick=1&amp;channels=youtube-dl">webchat</a>) on freenode.</p>
<div class="note">
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_US">
<img alt="Creative Commons License" style="border-width:0"
src="https://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a><br />
Copyright © 2006-2011 Ricardo Garcia Gonzalez<br />
Copyright © 2011-2021 youtube-dl developers
</div>
</body>
</html>
+212
View File
@@ -0,0 +1,212 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
<title>youtube-dl: Download videos from YouTube.com</title>
<style type="text/css"><!--
body {
font-family: sans-serif;
font-size: small;
}
h1 {
text-align: center;
text-decoration: underline;
color: #006699;
}
h2 {
color: #006699;
}
p {
text-align: justify;
margin-left: 5%;
margin-right: 5%;
}
ul {
margin-left: 5%;
margin-right: 5%;
list-style-type: square;
}
li {
margin-bottom: 0.5ex;
}
.smallnote {
font-size: x-small;
text-align: center;
}
--></style>
</head>
<body>
<h1>youtube-dl: Download videos from YouTube.com</h1>
<p class="smallnote">(and more...)</p>
<h2>What is it?</h2>
<p><em>youtube-dl</em> is a small command-line program to download videos
from YouTube.com. It requires the <a href="http://www.python.org/">Python
interpreter</a>, version 2.4 or later, and it's not platform specific.
It should work in your Unix box, in Windows or in Mac OS X. The latest version
is <strong>@PROGRAM_VERSION@</strong>. It's released to the public domain,
which means you can modify it, redistribute it or use it however you like.</p>
<p>I'll try to keep it updated if YouTube.com changes the way you access
their videos. After all, it's a simple and short program. However, I can't
guarantee anything. If you detect it stops working, check for new versions
and/or inform me about the problem, indicating the program version you
are using. If the program stops working and I can't solve the problem but
you have a solution, I'd like to know it. If that happens and you feel you
can maintain the program yourself, tell me. My contact information is
at <a href="http://freshmeat.net/~rg3/">freshmeat.net</a>.</p>
<p>Thanks for all the feedback received so far. I'm glad people find my
program useful.</p>
<h2>Usage instructions</h2>
<p>In Windows, once you have installed the Python interpreter, save the
program with the <em>.py</em> extension and put it somewhere in the PATH.
Try to follow the
<a href="http://rg03.wordpress.com/youtube-dl-under-windows-xp/">guide to
install youtube-dl under Windows XP</a>.</p>
<p>In Unix, download it, give it execution permission and copy it to one
of the PATH directories (typically, <em>/usr/local/bin</em>).</p>
<p>After that, you should be able to call it from the command line as
<em>youtube-dl</em> or <em>youtube-dl.py</em>. I will use <em>youtube-dl</em>
in the following examples. Usage instructions are easy. Use <em>youtube-dl</em>
followed by a video URL or identifier. Example: <em>youtube-dl
"http://www.youtube.com/watch?v=foobar"</em>. The video will be saved
to the file <em>foobar.flv</em> in that example. As YouTube.com
videos are in Flash Video format, their extension should be <em>flv</em>.
In Linux and other unices, video players using a recent version of
<em>ffmpeg</em> can play them. That includes MPlayer, VLC, etc. Those two
work under Windows and other platforms, but you could also get a
specific FLV player of your taste.</p>
<p>If you try to run the program and you receive an error message containing the
keyword <em>SyntaxError</em> near the end, it means your Python interpreter
is too old.</p>
<h2>More usage tips</h2>
<ul>
<li>You can change the file name of the video using the -o option, like in
<em>youtube-dl -o vid.flv "http://www.youtube.com/watch?v=foobar"</em>.
Read the <em>Output template</em> section for more details on this.</li>
<li>Some videos require an account to be downloaded, mostly because they're
flagged as mature content. You can pass the program a username and password
for a YouTube.com account with the -u and -p options, like <em>youtube-dl
-u myusername -p mypassword "http://www.youtube.com/watch?v=foobar"</em>.</li>
<li>The account data can also be read from the user .netrc file by indicating
the -n or --netrc option. The machine name is <em>youtube</em> in that
case.</li>
<li>The <em>simulate mode</em> (activated with -s or --simulate) can be used
to just get the real video URL and use it with a download manager if you
prefer that option.</li>
<li>The <em>quiet mode</em> (activated with -q or --quiet) can be used to
supress all output messages. This allows, in systems featuring /dev/stdout
and other similar special files, outputting the video data to standard output
in order to pipe it to another program without interferences.</li>
<li>The program can be told to simply print the final video URL to standard
output using the -g or --get-url option.</li>
<li>In a similar line, the -e or --get-title option tells the program to print
the video title.</li>
<li>The default filename is <em>video_id.flv</em>. But you can also use the
video title in the filename with the -t or --title option, or preserve the
literal title in the filename with the -l or --literal option.</li>
<li>You can make the program append <em>&amp;fmt=something</em> to the URL
by using the -f or --format option. This makes it possible to download high
quality versions of the videos when available.</li>
<li><em>youtube-dl</em> can attempt to download the best quality version of
a video by using the -b or --best-quality option.</li>
<li><em>youtube-dl</em> can attempt to download the mobile quality version of
a video by using the -m or --mobile-version option.</li>
<li>Normally, the program will stop on the first error, but you can tell it
to attempt to download every video with the -i or --ignore-errors option.</li>
<li><em>youtube-dl</em> honors the <em>http_proxy</em> environment variable
if you want to use a proxy. Set it to something like
<em>http://proxy.example.com:8080</em>, and do not leave the <em>http://</em>
prefix out.</li>
<li>You can get the program version by calling it as <em>youtube-dl
-v</em> or <em>youtube-dl --version</em>.</li>
<li>For usage instructions, use <em>youtube-dl -h</em> or <em>youtube-dl
--help.</em></li>
<li>You can cancel the program at any time pressing Ctrl+C. It may print
some error lines saying something about <em>KeyboardInterrupt</em>.
That's ok.</li>
</ul>
<h2>Download it</h2>
<p>Note that if you directly click on these hyperlinks, your web browser will
most likely display the program contents. It's usually better to
right-click on it and choose the appropriate option, normally called <em>Save
Target As</em> or <em>Save Link As</em>, depending on the web browser you
are using.</p>
<p><a href="youtube-dl">@PROGRAM_VERSION@</a></p>
<ul>
<li><strong>MD5</strong>: @PROGRAM_MD5SUM@</li>
<li><strong>SHA1</strong>: @PROGRAM_SHA1SUM@</li>
<li><strong>SHA256</strong>: @PROGRAM_SHA256SUM@</li>
</ul>
<h2>Output template</h2>
<p>The -o option allows users to indicate a template for the output file names.
The basic usage is not to set any template arguments when downloading a single
file, like in <em>youtube-dl -o funny_video.flv 'http://some/video'</em>.
However, it may contain special sequences that will be replaced when
downloading each video. The special sequences have the format
<strong>%(NAME)s</strong>. To clarify, that's a percent symbol followed by a
name in parenthesis, followed by a lowercase S. Allowed names are:</p>
<ul>
<li><em>id</em>: The sequence will be replaced by the video identifier.</li>
<li><em>url</em>: The sequence will be replaced by the video URL.</li>
<li><em>uploader</em>: The sequence will be replaced by the nickname of the
person who uploaded the video.</li>
<li><em>title</em>: The sequence will be replaced by the literal video
title.</li>
<li><em>stitle</em>: The sequence will be replaced by a simplified video
title.</li>
<li><em>ext</em>: The sequence will be replaced by the appropriate
extension.</li>
</ul>
<p>As you may have guessed, the default template is <em>%(id)s.%(ext)s</em>.
When some command line options are used, it's replaced by other templates like
<em>%(title)s-%(id)s.%(ext)s</em>. You can specify your own.</p>
<h2>Authors</h2>
<ul>
<li>Ricardo Garcia Gonzalez: program core, YouTube.com InfoExtractor,
metacafe.com InfoExtractor and YouTube playlist InfoExtractor.</li>
<li>Many other people contributing patches, code, ideas and kind messages. Too
many to be listed here. You know who you are. Thank you very much.</li>
</ul>
<p class="smallnote">Copyright &copy; 2006-2007 Ricardo Garcia Gonzalez</p>
</body>
</html>
-1
View File
@@ -1 +0,0 @@
../update/LATEST_VERSION
-156
View File
@@ -1,156 +0,0 @@
body {
font-family: sans-serif;
margin-left: 10%;
margin-right: 10%;
margin-top: 2ex;
margin-bottom: 3ex;
background-color: white;
color: black;
/*background-color: #fff1db;*/
background-color: white;
/*
background-image: url("gradient.png");
background-repeat: repeat-x;
*/
/*
background-image: url("gradient2.png");
background-repeat: repeat-y;
*/
/*
background-image: url("gradient3.png");
background-repeat: repeat-x;
*/
/*
background-image: url("gradient4.png");
background-repeat: repeat-y;
*/
background-image: url("gradient5.png");
background-repeat: repeat-x;
}
.heading {
border: 0;
color: black;
font-size: xx-large;
font-weight: bold;
padding-bottom: 1ex;
border-bottom: 1px solid black;
margin-bottom: 2ex;
width: 100%;
}
.heading tr {
border: 0;
}
.heading td {
border: 0;
}
.heading a {
text-decoration: none;
color: black;
}
.title {
text-align: left;
}
.subtitle {
text-align: right;
}
.toc {
padding-left: 2ex;
border: 1px solid #aaaaaa;
background-color: white;
padding-bottom: 1ex;
border-radius: 10px;
-moz-border-radius: 10px;
}
.toc ul {
margin: 0; list-style-type: none;
}
hr {
margin-top: 3ex;
margin-bottom: 3ex;
width: 50%;
}
.note {
margin-top: 10ex;
text-align: center;
font-size: x-small;
}
h1 {
font-size: x-large;
margin-top: 2ex;
color: black;
margin-left: 2%;
margin-right: 2%;
}
h2 {
font-size: large;
margin-left: 5%;
margin-right: 5%;
}
p {
margin-left: 5%;
margin-right: 5%;
}
ul {
margin-left: 5%;
margin-right: 5%;
}
li {
margin-left: 3%;
margin-top: 0.5ex;
margin-bottom: 0.5ex;
}
tt {
padding-left: 0.5ex;
padding-right: 0.5ex;
background: #dddddd;
}
#rgb {
width: 33%;
margin: 3ex auto;
}
.button {
color: white;
font-weight: bold;
font-size: x-large;
text-decoration: none;
text-align: center;
display: block;
padding: 2ex;
border-radius: 10px;
-moz-border-radius: 10px;
}
#r {
background-color: #884444;
border: 2px solid #880000;
}
#g {
background-color: #448844;
border: 2px solid #006600;
}
#b {
background-color: #444488;
border: 2px solid #000088;
}
#y {
background-color: #888844;
border: 2px solid #666600;
}
#main-support {
background-color: #448888;
border: 2px solid #008888;
}
code.commands {
display:block;
margin-top: 0.4em;
padding: 0.7em;
background: #ccc;
background: rgba(200, 200, 200, 0.4);
white-space: pre;
}
code.fingerprint {
font-weight: bold;
font-size: 140%;
}
-1252
View File
File diff suppressed because it is too large Load Diff
-28
View File
@@ -1,28 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>youtube-dl: Supported sites</title>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<table class="heading"><tr>
<td class="title"><a href="index.html">youtube-dl</a></td>
<td class="subtitle">Supported sites</td>
</tr></table>
<p>Here's is the list of all the supported sites, ordered alphabetically:</p>
<ul>
@SITES@
</ul>
<div class="note">
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_US">
<img alt="Creative Commons License" style="border-width:0"
src="https://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a><br />
Copyright © 2006-2011 Ricardo Garcia Gonzalez<br />
Copyright © 2011-2021 youtube-dl developers
</div>
</body>
</html>
-1
View File
@@ -1 +0,0 @@
2021.12.17
-14693
View File
File diff suppressed because it is too large Load Diff
-13718
View File
File diff suppressed because it is too large Load Diff
Executable
+930
View File
@@ -0,0 +1,930 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: Ricardo Garcia Gonzalez
# License: Public domain code
import htmlentitydefs
import httplib
import math
import netrc
import os
import os.path
import re
import socket
import string
import sys
import time
import urllib
import urllib2
std_headers = {
'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1',
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
'Accept': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
'Accept-Language': 'en-us,en;q=0.5',
}
simple_title_chars = string.ascii_letters.decode('ascii') + string.digits.decode('ascii')
class DownloadError(Exception):
"""Download Error exception.
This exception may be thrown by FileDownloader objects if they are not
configured to continue on errors. They will contain the appropriate
error message.
"""
pass
class SameFileError(Exception):
"""Same File exception.
This exception will be thrown by FileDownloader objects if they detect
multiple files would have to be downloaded to the same file on disk.
"""
pass
class PostProcessingError(Exception):
"""Post Processing exception.
This exception may be raised by PostProcessor's .run() method to
indicate an error in the postprocessing task.
"""
pass
class FileDownloader(object):
"""File Downloader class.
File downloader objects are the ones responsible of downloading the
actual video file and writing it to disk if the user has requested
it, among some other tasks. In most cases there should be one per
program. As, given a video URL, the downloader doesn't know how to
extract all the needed information, task that InfoExtractors do, it
has to pass the URL to one of them.
For this, file downloader objects have a method that allows
InfoExtractors to be registered in a given order. When it is passed
a URL, the file downloader handles it to the first InfoExtractor it
finds that reports being able to handle it. The InfoExtractor returns
all the information to the FileDownloader and the latter downloads the
file or does whatever it's instructed to do.
File downloaders accept a lot of parameters. In order not to saturate
the object constructor with arguments, it receives a dictionary of
options instead. These options are available through the get_params()
method for the InfoExtractors to use. The FileDownloader also registers
itself as the downloader in charge for the InfoExtractors that are
added to it, so this is a "mutual registration".
Available options:
username: Username for authentication purposes.
password: Password for authentication purposes.
usenetrc: Use netrc for authentication instead.
quiet: Do not print messages to stdout.
forceurl: Force printing final URL.
forcetitle: Force printing title.
simulate: Do not download the video files.
format: Video format code.
outtmpl: Template for output names.
ignoreerrors: Do not stop on download errors.
ratelimit: Download speed limit, in bytes/sec.
"""
_params = None
_ies = []
_pps = []
def __init__(self, params):
"""Create a FileDownloader object with the given options."""
self._ies = []
self._pps = []
self.set_params(params)
@staticmethod
def pmkdir(filename):
"""Create directory components in filename. Similar to Unix "mkdir -p"."""
components = filename.split(os.sep)
aggregate = [os.sep.join(components[0:x]) for x in xrange(1, len(components))]
aggregate = ['%s%s' % (x, os.sep) for x in aggregate] # Finish names with separator
for dir in aggregate:
if not os.path.exists(dir):
os.mkdir(dir)
@staticmethod
def format_bytes(bytes):
if bytes is None:
return 'N/A'
if bytes == 0:
exponent = 0
else:
exponent = long(math.log(float(bytes), 1024.0))
suffix = 'bkMGTPEZY'[exponent]
converted = float(bytes) / float(1024**exponent)
return '%.2f%s' % (converted, suffix)
@staticmethod
def calc_percent(byte_counter, data_len):
if data_len is None:
return '---.-%'
return '%6s' % ('%3.1f%%' % (float(byte_counter) / float(data_len) * 100.0))
@staticmethod
def calc_eta(start, now, total, current):
if total is None:
return '--:--'
dif = now - start
if current == 0 or dif < 0.001: # One millisecond
return '--:--'
rate = float(current) / dif
eta = long((float(total) - float(current)) / rate)
(eta_mins, eta_secs) = divmod(eta, 60)
if eta_mins > 99:
return '--:--'
return '%02d:%02d' % (eta_mins, eta_secs)
@staticmethod
def calc_speed(start, now, bytes):
dif = now - start
if bytes == 0 or dif < 0.001: # One millisecond
return '%10s' % '---b/s'
return '%10s' % ('%s/s' % FileDownloader.format_bytes(float(bytes) / dif))
@staticmethod
def best_block_size(elapsed_time, bytes):
new_min = max(bytes / 2.0, 1.0)
new_max = min(max(bytes * 2.0, 1.0), 4194304) # Do not surpass 4 MB
if elapsed_time < 0.001:
return int(new_max)
rate = bytes / elapsed_time
if rate > new_max:
return int(new_max)
if rate < new_min:
return int(new_min)
return int(rate)
@staticmethod
def parse_bytes(bytestr):
"""Parse a string indicating a byte quantity into a long integer."""
matchobj = re.match(r'(?i)^(\d+(?:\.\d+)?)([kMGTPEZY]?)$', bytestr)
if matchobj is None:
return None
number = float(matchobj.group(1))
multiplier = 1024.0 ** 'bkmgtpezy'.index(matchobj.group(2).lower())
return long(round(number * multiplier))
def set_params(self, params):
"""Sets parameters."""
if type(params) != dict:
raise ValueError('params: dictionary expected')
self._params = params
def get_params(self):
"""Get parameters."""
return self._params
def add_info_extractor(self, ie):
"""Add an InfoExtractor object to the end of the list."""
self._ies.append(ie)
ie.set_downloader(self)
def add_post_processor(self, pp):
"""Add a PostProcessor object to the end of the chain."""
self._pps.append(pp)
pp.set_downloader(self)
def to_stdout(self, message, skip_eol=False):
"""Print message to stdout if not in quiet mode."""
if not self._params.get('quiet', False):
print u'%s%s' % (message, [u'\n', u''][skip_eol]),
sys.stdout.flush()
def to_stderr(self, message):
"""Print message to stderr."""
print >>sys.stderr, message
def fixed_template(self):
"""Checks if the output template is fixed."""
return (re.search(ur'(?u)%\(.+?\)s', self._params['outtmpl']) is None)
def trouble(self, message=None):
"""Determine action to take when a download problem appears.
Depending on if the downloader has been configured to ignore
download errors or not, this method may throw an exception or
not when errors are found, after printing the message. If it
doesn't raise, it returns an error code suitable to be returned
later as a program exit code to indicate error.
"""
if message is not None:
self.to_stderr(message)
if not self._params.get('ignoreerrors', False):
raise DownloadError(message)
return 1
def slow_down(self, start_time, byte_counter):
"""Sleep if the download speed is over the rate limit."""
rate_limit = self._params.get('ratelimit', None)
if rate_limit is None or byte_counter == 0:
return
now = time.time()
elapsed = now - start_time
if elapsed <= 0.0:
return
speed = float(byte_counter) / elapsed
if speed > rate_limit:
time.sleep((byte_counter - rate_limit * (now - start_time)) / rate_limit)
def report_destination(self, filename):
"""Report destination filename."""
self.to_stdout(u'[download] Destination: %s' % filename)
def report_progress(self, percent_str, data_len_str, speed_str, eta_str):
"""Report download progress."""
self.to_stdout(u'\r[download] %s of %s at %s ETA %s' %
(percent_str, data_len_str, speed_str, eta_str), skip_eol=True)
def report_finish(self):
"""Report download finished."""
self.to_stdout(u'')
def download(self, url_list):
"""Download a given list of URLs."""
retcode = 0
if len(url_list) > 1 and self.fixed_template():
raise SameFileError(self._params['outtmpl'])
for url in url_list:
suitable_found = False
for ie in self._ies:
if not ie.suitable(url):
continue
# Suitable InfoExtractor found
suitable_found = True
all_results = ie.extract(url)
results = [x for x in all_results if x is not None]
if len(results) != len(all_results):
retcode = self.trouble()
if len(results) > 1 and self.fixed_template():
raise SameFileError(self._params['outtmpl'])
for result in results:
# Forced printings
if self._params.get('forcetitle', False):
print result['title']
if self._params.get('forceurl', False):
print result['url']
# Do nothing else if in simulate mode
if self._params.get('simulate', False):
continue
try:
filename = self._params['outtmpl'] % result
self.report_destination(filename)
except (ValueError, KeyError), err:
retcode = self.trouble('ERROR: invalid output template or system charset: %s' % str(err))
continue
try:
self.pmkdir(filename)
except (OSError, IOError), err:
retcode = self.trouble('ERROR: unable to create directories: %s' % str(err))
continue
try:
outstream = open(filename, 'wb')
except (OSError, IOError), err:
retcode = self.trouble('ERROR: unable to open for writing: %s' % str(err))
continue
try:
self._do_download(outstream, result['url'])
outstream.close()
except (OSError, IOError), err:
retcode = self.trouble('ERROR: unable to write video data: %s' % str(err))
continue
except (urllib2.URLError, httplib.HTTPException, socket.error), err:
retcode = self.trouble('ERROR: unable to download video data: %s' % str(err))
continue
try:
self.post_process(filename, result)
except (PostProcessingError), err:
retcode = self.trouble('ERROR: postprocessing: %s' % str(err))
continue
break
if not suitable_found:
retcode = self.trouble('ERROR: no suitable InfoExtractor: %s' % url)
return retcode
def post_process(self, filename, ie_info):
"""Run the postprocessing chain on the given file."""
info = dict(ie_info)
info['filepath'] = filename
for pp in self._pps:
info = pp.run(info)
if info is None:
break
def _do_download(self, stream, url):
request = urllib2.Request(url, None, std_headers)
data = urllib2.urlopen(request)
data_len = data.info().get('Content-length', None)
data_len_str = self.format_bytes(data_len)
byte_counter = 0
block_size = 1024
start = time.time()
while True:
# Progress message
percent_str = self.calc_percent(byte_counter, data_len)
eta_str = self.calc_eta(start, time.time(), data_len, byte_counter)
speed_str = self.calc_speed(start, time.time(), byte_counter)
self.report_progress(percent_str, data_len_str, speed_str, eta_str)
# Download and write
before = time.time()
data_block = data.read(block_size)
after = time.time()
data_block_len = len(data_block)
if data_block_len == 0:
break
byte_counter += data_block_len
stream.write(data_block)
block_size = self.best_block_size(after - before, data_block_len)
# Apply rate limit
self.slow_down(start, byte_counter)
self.report_finish()
if data_len is not None and str(byte_counter) != data_len:
raise ValueError('Content too short: %s/%s bytes' % (byte_counter, data_len))
class InfoExtractor(object):
"""Information Extractor class.
Information extractors are the classes that, given a URL, extract
information from the video (or videos) the URL refers to. This
information includes the real video URL, the video title and simplified
title, author and others. It is returned in a list of dictionaries when
calling its extract() method. It is a list because a URL can refer to
more than one video (think of playlists). The dictionaries must include
the following fields:
id: Video identifier.
url: Final video URL.
uploader: Nickname of the video uploader.
title: Literal title.
stitle: Simplified title.
ext: Video filename extension.
Subclasses of this one should re-define the _real_initialize() and
_real_extract() methods, as well as the suitable() static method.
Probably, they should also be instantiated and added to the main
downloader.
"""
_ready = False
_downloader = None
def __init__(self, downloader=None):
"""Constructor. Receives an optional downloader."""
self._ready = False
self.set_downloader(downloader)
@staticmethod
def suitable(url):
"""Receives a URL and returns True if suitable for this IE."""
return False
def initialize(self):
"""Initializes an instance (authentication, etc)."""
if not self._ready:
self._real_initialize()
self._ready = True
def extract(self, url):
"""Extracts URL information and returns it in list of dicts."""
self.initialize()
return self._real_extract(url)
def set_downloader(self, downloader):
"""Sets the downloader for this IE."""
self._downloader = downloader
def to_stdout(self, message):
"""Print message to stdout if downloader is not in quiet mode."""
if self._downloader is None or not self._downloader.get_params().get('quiet', False):
print message
def to_stderr(self, message):
"""Print message to stderr."""
print >>sys.stderr, message
def _real_initialize(self):
"""Real initialization process. Redefine in subclasses."""
pass
def _real_extract(self, url):
"""Real extraction process. Redefine in subclasses."""
pass
class YoutubeIE(InfoExtractor):
"""Information extractor for youtube.com."""
_VALID_URL = r'^((?:http://)?(?:\w+\.)?youtube\.com/(?:(?:v/)|(?:(?:watch(?:\.php)?)?\?(?:.+&)?v=)))?([0-9A-Za-z_-]+)(?(1).+)?$'
_LOGIN_URL = 'http://www.youtube.com/login?next=/'
_AGE_URL = 'http://www.youtube.com/verify_age?next_url=/'
_NETRC_MACHINE = 'youtube'
@staticmethod
def suitable(url):
return (re.match(YoutubeIE._VALID_URL, url) is not None)
def report_login(self):
"""Report attempt to log in."""
self.to_stdout(u'[youtube] Logging in')
def report_age_confirmation(self):
"""Report attempt to confirm age."""
self.to_stdout(u'[youtube] Confirming age')
def report_webpage_download(self, video_id):
"""Report attempt to download webpage."""
self.to_stdout(u'[youtube] %s: Downloading video webpage' % video_id)
def report_information_extraction(self, video_id):
"""Report attempt to extract video information."""
self.to_stdout(u'[youtube] %s: Extracting video information' % video_id)
def report_video_url(self, video_id, video_real_url):
"""Report extracted video URL."""
self.to_stdout(u'[youtube] %s: URL: %s' % (video_id, video_real_url))
def _real_initialize(self):
if self._downloader is None:
return
username = None
password = None
downloader_params = self._downloader.get_params()
# Attempt to use provided username and password or .netrc data
if downloader_params.get('username', None) is not None:
username = downloader_params['username']
password = downloader_params['password']
elif downloader_params.get('usenetrc', False):
try:
info = netrc.netrc().authenticators(self._NETRC_MACHINE)
if info is not None:
username = info[0]
password = info[2]
else:
raise netrc.NetrcParseError('No authenticators for %s' % self._NETRC_MACHINE)
except (IOError, netrc.NetrcParseError), err:
self.to_stderr(u'WARNING: parsing .netrc: %s' % str(err))
return
# No authentication to be performed
if username is None:
return
# Log in
login_form = {
'current_form': 'loginForm',
'next': '/',
'action_login': 'Log In',
'username': username,
'password': password,
}
request = urllib2.Request(self._LOGIN_URL, urllib.urlencode(login_form), std_headers)
try:
self.report_login()
login_results = urllib2.urlopen(request).read()
if re.search(r'(?i)<form[^>]* name="loginForm"', login_results) is not None:
self.to_stderr(u'WARNING: unable to log in: bad username or password')
return
except (urllib2.URLError, httplib.HTTPException, socket.error), err:
self.to_stderr(u'WARNING: unable to log in: %s' % str(err))
return
# Confirm age
age_form = {
'next_url': '/',
'action_confirm': 'Confirm',
}
request = urllib2.Request(self._AGE_URL, urllib.urlencode(age_form), std_headers)
try:
self.report_age_confirmation()
age_results = urllib2.urlopen(request).read()
except (urllib2.URLError, httplib.HTTPException, socket.error), err:
self.to_stderr(u'ERROR: unable to confirm age: %s' % str(err))
return
def _real_extract(self, url):
# Extract video id from URL
mobj = re.match(self._VALID_URL, url)
if mobj is None:
self.to_stderr(u'ERROR: invalid URL: %s' % url)
return [None]
video_id = mobj.group(2)
# Downloader parameters
format_param = None
if self._downloader is not None:
params = self._downloader.get_params()
format_param = params.get('format', None)
# Extension
video_extension = {'18': 'mp4', '17': '3gp'}.get(format_param, 'flv')
# Normalize URL, including format
normalized_url = 'http://www.youtube.com/watch?v=%s' % video_id
if format_param is not None:
normalized_url = '%s&fmt=%s' % (normalized_url, format_param)
request = urllib2.Request(normalized_url, None, std_headers)
try:
self.report_webpage_download(video_id)
video_webpage = urllib2.urlopen(request).read()
except (urllib2.URLError, httplib.HTTPException, socket.error), err:
self.to_stderr(u'ERROR: unable to download video webpage: %s' % str(err))
return [None]
self.report_information_extraction(video_id)
# "t" param
mobj = re.search(r', "t": "([^"]+)"', video_webpage)
if mobj is None:
self.to_stderr(u'ERROR: unable to extract "t" parameter')
return [None]
video_real_url = 'http://www.youtube.com/get_video?video_id=%s&t=%s' % (video_id, mobj.group(1))
if format_param is not None:
video_real_url = '%s&fmt=%s' % (video_real_url, format_param)
self.report_video_url(video_id, video_real_url)
# uploader
mobj = re.search(r"var watchUsername = '([^']+)';", video_webpage)
if mobj is None:
self.to_stderr(u'ERROR: unable to extract uploader nickname')
return [None]
video_uploader = mobj.group(1)
# title
mobj = re.search(r'(?im)<title>YouTube - ([^<]*)</title>', video_webpage)
if mobj is None:
self.to_stderr(u'ERROR: unable to extract video title')
return [None]
video_title = mobj.group(1).decode('utf-8')
video_title = re.sub(ur'(?u)&(.+?);', lambda x: unichr(htmlentitydefs.name2codepoint[x.group(1)]), video_title)
video_title = video_title.replace(os.sep, u'%')
# simplified title
simple_title = re.sub(ur'(?u)([^%s]+)' % simple_title_chars, ur'_', video_title)
simple_title = simple_title.strip(ur'_')
# Return information
return [{
'id': video_id.decode('utf-8'),
'url': video_real_url.decode('utf-8'),
'uploader': video_uploader.decode('utf-8'),
'title': video_title,
'stitle': simple_title,
'ext': video_extension.decode('utf-8'),
}]
class MetacafeIE(InfoExtractor):
"""Information Extractor for metacafe.com."""
_VALID_URL = r'(?:http://)?(?:www\.)?metacafe\.com/watch/([^/]+)/([^/]+)/.*'
_DISCLAIMER = 'http://www.metacafe.com/disclaimer'
_youtube_ie = None
def __init__(self, youtube_ie, downloader=None):
InfoExtractor.__init__(self, downloader)
self._youtube_ie = youtube_ie
@staticmethod
def suitable(url):
return (re.match(MetacafeIE._VALID_URL, url) is not None)
def report_disclaimer(self):
"""Report disclaimer retrieval."""
self.to_stdout(u'[metacafe] Retrieving disclaimer')
def report_age_confirmation(self):
"""Report attempt to confirm age."""
self.to_stdout(u'[metacafe] Confirming age')
def report_download_webpage(self, video_id):
"""Report webpage download."""
self.to_stdout(u'[metacafe] %s: Downloading webpage' % video_id)
def report_extraction(self, video_id):
"""Report information extraction."""
self.to_stdout(u'[metacafe] %s: Extracting information' % video_id)
def _real_initialize(self):
# Retrieve disclaimer
request = urllib2.Request(self._DISCLAIMER, None, std_headers)
try:
self.report_disclaimer()
disclaimer = urllib2.urlopen(request).read()
except (urllib2.URLError, httplib.HTTPException, socket.error), err:
self.to_stderr(u'ERROR: unable to retrieve disclaimer: %s' % str(err))
return
# Confirm age
disclaimer_form = {
'allowAdultContent': '1',
'submit': "Continue - I'm over 18",
}
request = urllib2.Request('http://www.metacafe.com/watch/', urllib.urlencode(disclaimer_form), std_headers)
try:
self.report_age_confirmation()
disclaimer = urllib2.urlopen(request).read()
except (urllib2.URLError, httplib.HTTPException, socket.error), err:
self.to_stderr(u'ERROR: unable to confirm age: %s' % str(err))
return
def _real_extract(self, url):
# Extract id and simplified title from URL
mobj = re.match(self._VALID_URL, url)
if mobj is None:
self.to_stderr(u'ERROR: invalid URL: %s' % url)
return [None]
video_id = mobj.group(1)
# Check if video comes from YouTube
mobj2 = re.match(r'^yt-(.*)$', video_id)
if mobj2 is not None:
return self._youtube_ie.extract('http://www.youtube.com/watch?v=%s' % mobj2.group(1))
simple_title = mobj.group(2).decode('utf-8')
video_extension = 'flv'
# Retrieve video webpage to extract further information
request = urllib2.Request('http://www.metacafe.com/watch/%s/' % video_id)
try:
self.report_download_webpage(video_id)
webpage = urllib2.urlopen(request).read()
except (urllib2.URLError, httplib.HTTPException, socket.error), err:
self.to_stderr(u'ERROR: unable retrieve video webpage: %s' % str(err))
return [None]
# Extract URL, uploader and title from webpage
self.report_extraction(video_id)
mobj = re.search(r'(?m)"mediaURL":"(http.*?\.flv)"', webpage)
if mobj is None:
self.to_stderr(u'ERROR: unable to extract media URL')
return [None]
mediaURL = mobj.group(1).replace('\\', '')
mobj = re.search(r'(?m)"gdaKey":"(.*?)"', webpage)
if mobj is None:
self.to_stderr(u'ERROR: unable to extract gdaKey')
return [None]
gdaKey = mobj.group(1)
video_url = '%s?__gda__=%s' % (mediaURL, gdaKey)
mobj = re.search(r'(?im)<meta name="title" content="Metacafe - ([^"]+)"', webpage)
if mobj is None:
self.to_stderr(u'ERROR: unable to extract title')
return [None]
video_title = mobj.group(1).decode('utf-8')
mobj = re.search(r'(?m)<li id="ChnlUsr">.*?Submitter:<br />(.*?)</li>', webpage)
if mobj is None:
self.to_stderr(u'ERROR: unable to extract uploader nickname')
return [None]
video_uploader = re.sub(r'<.*?>', '', mobj.group(1))
# Return information
return [{
'id': video_id.decode('utf-8'),
'url': video_url.decode('utf-8'),
'uploader': video_uploader.decode('utf-8'),
'title': video_title,
'stitle': simple_title,
'ext': video_extension.decode('utf-8'),
}]
class YoutubePlaylistIE(InfoExtractor):
"""Information Extractor for YouTube playlists."""
_VALID_URL = r'(?:http://)?(?:\w+\.)?youtube.com/view_play_list\?p=(.+)'
_TEMPLATE_URL = 'http://www.youtube.com/view_play_list?p=%s&page=%s'
_VIDEO_INDICATOR = r'/watch\?v=(.+?)&'
_MORE_PAGES_INDICATOR = r'/view_play_list?p=%s&amp;page=%s'
_youtube_ie = None
def __init__(self, youtube_ie, downloader=None):
InfoExtractor.__init__(self, downloader)
self._youtube_ie = youtube_ie
@staticmethod
def suitable(url):
return (re.match(YoutubePlaylistIE._VALID_URL, url) is not None)
def report_download_page(self, playlist_id, pagenum):
"""Report attempt to download playlist page with given number."""
self.to_stdout(u'[youtube] PL %s: Downloading page #%s' % (playlist_id, pagenum))
def _real_initialize(self):
self._youtube_ie.initialize()
def _real_extract(self, url):
# Extract playlist id
mobj = re.match(self._VALID_URL, url)
if mobj is None:
self.to_stderr(u'ERROR: invalid url: %s' % url)
return [None]
# Download playlist pages
playlist_id = mobj.group(1)
video_ids = []
pagenum = 1
while True:
self.report_download_page(playlist_id, pagenum)
request = urllib2.Request(self._TEMPLATE_URL % (playlist_id, pagenum), None, std_headers)
try:
page = urllib2.urlopen(request).read()
except (urllib2.URLError, httplib.HTTPException, socket.error), err:
self.to_stderr(u'ERROR: unable to download webpage: %s' % str(err))
return [None]
# Extract video identifiers
ids_in_page = set()
for mobj in re.finditer(self._VIDEO_INDICATOR, page):
ids_in_page.add(mobj.group(1))
video_ids.extend(list(ids_in_page))
if (self._MORE_PAGES_INDICATOR % (playlist_id, pagenum + 1)) not in page:
break
pagenum = pagenum + 1
information = []
for id in video_ids:
information.extend(self._youtube_ie.extract('http://www.youtube.com/watch?v=%s' % id))
return information
class PostProcessor(object):
"""Post Processor class.
PostProcessor objects can be added to downloaders with their
add_post_processor() method. When the downloader has finished a
successful download, it will take its internal chain of PostProcessors
and start calling the run() method on each one of them, first with
an initial argument and then with the returned value of the previous
PostProcessor.
The chain will be stopped if one of them ever returns None or the end
of the chain is reached.
PostProcessor objects follow a "mutual registration" process similar
to InfoExtractor objects.
"""
_downloader = None
def __init__(self, downloader=None):
self._downloader = downloader
def to_stdout(self, message):
"""Print message to stdout if downloader is not in quiet mode."""
if self._downloader is None or not self._downloader.get_params().get('quiet', False):
print message
def to_stderr(self, message):
"""Print message to stderr."""
print >>sys.stderr, message
def set_downloader(self, downloader):
"""Sets the downloader for this PP."""
self._downloader = downloader
def run(self, information):
"""Run the PostProcessor.
The "information" argument is a dictionary like the ones
returned by InfoExtractors. The only difference is that this
one has an extra field called "filepath" that points to the
downloaded file.
When this method returns None, the postprocessing chain is
stopped. However, this method may return an information
dictionary that will be passed to the next postprocessing
object in the chain. It can be the one it received after
changing some fields.
In addition, this method may raise a PostProcessingError
exception that will be taken into account by the downloader
it was called from.
"""
return information # by default, do nothing
### MAIN PROGRAM ###
if __name__ == '__main__':
try:
# Modules needed only when running the main program
import getpass
import optparse
# General configuration
urllib2.install_opener(urllib2.build_opener(urllib2.ProxyHandler()))
urllib2.install_opener(urllib2.build_opener(urllib2.HTTPCookieProcessor()))
socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
# Parse command line
parser = optparse.OptionParser(
usage='Usage: %prog [options] url...',
version='2008.08.09',
conflict_handler='resolve',
)
parser.add_option('-h', '--help',
action='help', help='print this help text and exit')
parser.add_option('-v', '--version',
action='version', help='print program version and exit')
parser.add_option('-u', '--username',
dest='username', metavar='UN', help='account username')
parser.add_option('-p', '--password',
dest='password', metavar='PW', help='account password')
parser.add_option('-o', '--output',
dest='outtmpl', metavar='TPL', help='output filename template')
parser.add_option('-q', '--quiet',
action='store_true', dest='quiet', help='activates quiet mode', default=False)
parser.add_option('-s', '--simulate',
action='store_true', dest='simulate', help='do not download video', default=False)
parser.add_option('-t', '--title',
action='store_true', dest='usetitle', help='use title in file name', default=False)
parser.add_option('-l', '--literal',
action='store_true', dest='useliteral', help='use literal title in file name', default=False)
parser.add_option('-n', '--netrc',
action='store_true', dest='usenetrc', help='use .netrc authentication data', default=False)
parser.add_option('-g', '--get-url',
action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
parser.add_option('-e', '--get-title',
action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
parser.add_option('-f', '--format',
dest='format', metavar='FMT', help='video format code')
parser.add_option('-b', '--best-quality',
action='store_const', dest='format', help='alias for -f 18', const='18')
parser.add_option('-m', '--mobile-version',
action='store_const', dest='format', help='alias for -f 17', const='17')
parser.add_option('-i', '--ignore-errors',
action='store_true', dest='ignoreerrors', help='continue on download errors', default=False)
parser.add_option('-r', '--rate-limit',
dest='ratelimit', metavar='L', help='download rate limit (e.g. 50k or 44.6m)')
(opts, args) = parser.parse_args()
# Conflicting, missing and erroneous options
if len(args) < 1:
sys.exit(u'ERROR: you must provide at least one URL')
if opts.usenetrc and (opts.username is not None or opts.password is not None):
sys.exit(u'ERROR: using .netrc conflicts with giving username/password')
if opts.password is not None and opts.username is None:
sys.exit(u'ERROR: account username missing')
if opts.outtmpl is not None and (opts.useliteral or opts.usetitle):
sys.exit(u'ERROR: using output template conflicts with using title or literal title')
if opts.usetitle and opts.useliteral:
sys.exit(u'ERROR: using title conflicts with using literal title')
if opts.username is not None and opts.password is None:
opts.password = getpass.getpass(u'Type account password and press return:')
if opts.ratelimit is not None:
numeric_limit = FileDownloader.parse_bytes(opts.ratelimit)
if numeric_limit is None:
sys.exit(u'ERROR: invalid rate limit specified')
opts.ratelimit = numeric_limit
# Information extractors
youtube_ie = YoutubeIE()
metacafe_ie = MetacafeIE(youtube_ie)
youtube_pl_ie = YoutubePlaylistIE(youtube_ie)
# File downloader
fd = FileDownloader({
'usenetrc': opts.usenetrc,
'username': opts.username,
'password': opts.password,
'quiet': (opts.quiet or opts.geturl or opts.gettitle),
'forceurl': opts.geturl,
'forcetitle': opts.gettitle,
'simulate': (opts.simulate or opts.geturl or opts.gettitle),
'format': opts.format,
'outtmpl': ((opts.outtmpl is not None and opts.outtmpl.decode())
or (opts.usetitle and u'%(stitle)s-%(id)s.%(ext)s')
or (opts.useliteral and u'%(title)s-%(id)s.%(ext)s')
or u'%(id)s.%(ext)s'),
'ignoreerrors': opts.ignoreerrors,
'ratelimit': opts.ratelimit,
})
fd.add_info_extractor(youtube_pl_ie)
fd.add_info_extractor(metacafe_ie)
fd.add_info_extractor(youtube_ie)
retcode = fd.download(args)
sys.exit(retcode)
except DownloadError:
sys.exit(1)
except SameFileError:
sys.exit(u'ERROR: fixed output name but more than one file to download')
except KeyboardInterrupt:
sys.exit(u'\nERROR: Interrupted by user')