Compare commits

...

686 Commits

Author SHA1 Message Date
Filippo Valsorda
2a298b72eb Release 2012.12.11 2012-12-11 17:00:13 +01:00
Filippo Valsorda
9789a05c20 fix playlist pagination and add YT playlist tests (closes #569) 2012-12-11 16:58:36 +01:00
Philipp Hagemeister
d050de77f9 Merge pull request #580 from FiloSottile/master
The new shiny build system
2012-12-11 07:52:44 -08:00
Filippo Valsorda
95eb771dcd Merge branch 'master' into fork_master
Conflicts:
	.travis.yml
2012-12-11 12:15:16 +01:00
Filippo Valsorda
4fb1acc212 use the new --test option to speed up tests (fetch only first 10K)
now all tests working and passing
2012-12-11 12:12:02 +01:00
Filippo Valsorda
d3d3199870 gentests: allow test-specific FileDownloader params override from tests.json 2012-12-11 12:09:22 +01:00
Filippo Valsorda
1ca63e3ae3 the test didn't load our Gzip opener
this was blocking the Vimeo test

+ some more gentest fixes
2012-12-11 11:33:15 +01:00
Filippo Valsorda
59ce201915 print traceback on trouble if --verbose (why didn't I think of this before!?) 2012-12-11 11:02:21 +01:00
Filippo Valsorda
8d5d3a5d00 exposing the test mode as --test (hidden and undocumented) 2012-12-11 09:57:40 +01:00
Filippo Valsorda
37c8fd4842 added a test mode to FileDownloader that fetches only first 10K 2012-12-11 09:49:27 +01:00
Filippo Valsorda
3c6ffbaedb Merge 'rg3/master' into fork_master 2012-12-08 01:57:43 +01:00
Filippo Valsorda
c7287a3caf ATTENTION DO NOT USE THESE: new binaries in the Downloads section
placed fake binaries that update themselves where old versions updating will search for the new version
2012-12-08 01:52:39 +01:00
Filippo Valsorda
5a304a7637 new updating scheme, based on GH downloads; also, check if not updateable (pip installed) 2012-12-08 00:48:07 +01:00
Filippo Valsorda
4c1d273e88 it's curious but bash-completion is with - and not _ 2012-12-08 00:37:26 +01:00
gcmalloc
a9d2f7e894 making the script compatible with python3 2012-12-07 22:01:02 +01:00
gcmalloc
682407f2d5 little correction on the readme 2012-12-07 21:40:06 +01:00
gcmalloc
bdff345529 adding a proper bash-completion generation 2012-12-07 21:38:45 +01:00
Filippo Valsorda
23109d6a9c youtube-dl.tar.gz make target 2012-12-07 14:46:14 +01:00
Filippo Valsorda
4bb028f48e devscripts/make_readme.py in place of all that sedding, that has porting problems 2012-12-07 14:45:16 +01:00
Filippo Valsorda
fec89790b1 and now, also py2exe compiles fine :) (on Windows) 2012-12-07 12:04:52 +01:00
Filippo Valsorda
a5741a3f5e pip installs fine! 2012-12-07 11:39:08 +01:00
Philipp Hagemeister
863baa16ec SoundCloud IDs have changed, fix tests 2012-12-07 01:34:40 +01:00
Philipp Hagemeister
c7214f9a6f Use Soundcloud API (Closes #579) 2012-12-07 01:30:03 +01:00
Philipp Hagemeister
8fd3afd56c More work on soundcloud IE 2012-12-07 01:24:51 +01:00
Philipp Hagemeister
f9b2f2b955 Correct accidental rename 2012-12-07 00:57:06 +01:00
Philipp Hagemeister
633b4a5ff6 Mark SoundCloud IE as nonfunctional for now (#579) 2012-12-07 00:50:56 +01:00
Philipp Hagemeister
b4cd069d5e Better error reporting for SoundCloud IE 2012-12-07 00:40:13 +01:00
Philipp Hagemeister
0f8d03f81c Let YoutubeDLHandler (transparent gzip) handle HTTPS URLs as well (Needed for #579) 2012-12-07 00:39:44 +01:00
Philipp Hagemeister
077174f4ed Add an example to the -o documentation (#573) 2012-12-04 11:05:38 +01:00
Philipp Hagemeister
e387eb5aba Let youtube IE handle IDs starting with PL (Closes #572) 2012-12-04 10:59:38 +01:00
Philipp Hagemeister
4083bf81a0 Correct metacafe test filename (happens to start with an underscore) 2012-12-03 20:17:47 +01:00
Philipp Hagemeister
796173d08b Keep video IDs verbatim if possible (Closes #571) 2012-12-03 15:36:41 +01:00
Philipp Hagemeister
e575b6821e Improve execution tests 2012-12-01 15:52:34 +01:00
Philipp Hagemeister
d78be7e331 Add test for Youku (Mentioned in #314) 2012-11-30 08:42:11 +01:00
Philipp Hagemeister
15c8d83358 Fix Soundcloud IE (+ Python3 support) 2012-11-29 20:40:12 +01:00
Philipp Hagemeister
e91d2338d8 Fix MD5 calculation 2012-11-29 20:38:16 +01:00
Philipp Hagemeister
4b235346d6 Add irc channel notice 2012-11-29 19:45:07 +01:00
Philipp Hagemeister
ad348291bb Enable travis notifications 2012-11-29 19:41:09 +01:00
Filippo Valsorda
2f1765c4ea setup.py Python3 fix, PyPi classifiers 2012-11-29 19:21:19 +01:00
Philipp Hagemeister
3c5b63d2d6 Merge branch 'master' of github.com:rg3/youtube-dl 2012-11-29 18:14:43 +01:00
Filippo Valsorda
cc51a7d4e0 New repo skeleton, getting ready for PyPi 2012-11-29 16:51:55 +01:00
Philipp Hagemeister
8af4ed7b4f Fix 2.6 nosetests 2012-11-29 16:35:57 +01:00
Filippo Valsorda
8192ebe1f8 Merge remote-tracking branch 'origin/master' into fork_master
New tests - merged with md5 correction
2012-11-29 15:38:07 +01:00
Filippo Valsorda
20ba04267c removed __main__.py from the root of the repo 2012-11-29 15:20:20 +01:00
Philipp Hagemeister
743b28ce11 Allow youtube_dl/__main__.py to be called directly 2012-11-29 15:11:24 +01:00
gcmalloc
caaa47d372 adding the script hook 2012-11-29 14:12:06 +01:00
gcmalloc
10f100ac8a cleaning binaries 2012-11-28 19:38:37 +01:00
Philipp Hagemeister
8176041605 Check during test runtime instead of test generation for _WORKING, and add 2.6 compat 2012-11-28 19:03:11 +01:00
gcmalloc
87bec4c715 getting version from git or failing 2012-11-28 18:49:56 +01:00
gcmalloc
190e8e27d8 removing the zip option, this can be done with python setup.py bdist --format=zip 2012-11-28 18:33:58 +01:00
gcmalloc
4efe62a016 moving to setup.py 2012-11-28 18:24:16 +01:00
gcmalloc
c64de2c980 correction on the test 2012-11-28 18:21:53 +01:00
Philipp Hagemeister
6ad98fb3fd Correct exception raising 2012-11-28 18:21:06 +01:00
Philipp Hagemeister
b08e09c370 Mark broken IEs in --list-extractors 2012-11-28 17:58:55 +01:00
Philipp Hagemeister
cdab8aa389 Update download tests 2012-11-28 15:09:56 +01:00
Philipp Hagemeister
3cd69a54b2 Merge branch 'master' of github.com:rg3/youtube-dl 2012-11-28 12:59:55 +01:00
Philipp Hagemeister
627dcfff39 Restrict more characters (Closes #566) 2012-11-28 12:59:27 +01:00
Filippo Valsorda
df5cff3751 make tests skip on not _WORKING 2012-11-28 11:54:20 +01:00
Filippo Valsorda
79ae0a06d5 comment out 3.3 testing until Travis implements it 2012-11-28 11:46:56 +01:00
gcmalloc
2d2fa229ec making the metacafe test pass 2012-11-28 11:46:03 +01:00
Filippo Valsorda
5a59fd6392 new .travis.yml with notifications and 3.3 2012-11-28 11:46:03 +01:00
Filippo Valsorda
0eb0faa26f Mark CollegeHumorIE not working until phihag finishes 2012-11-28 11:43:35 +01:00
Filippo Valsorda
32761d863c fix YouTubeIE on 2.6, sorry 2012-11-28 11:28:59 +01:00
Philipp Hagemeister
799c076384 collegehumor: able to download a single f4f file (not yet playable) 2012-11-28 04:51:27 +01:00
Philipp Hagemeister
f1cb5bcad2 Make __main__ work in all scenarios with relative imports 2012-11-28 03:55:35 +01:00
Philipp Hagemeister
9e8056d5a7 Use relative imports 2012-11-28 03:34:40 +01:00
Philipp Hagemeister
c6f3620859 Drop 2.5 support 2012-11-28 03:30:35 +01:00
Philipp Hagemeister
59ae15a507 Convert all tabs to 4 spaces (PEP8) 2012-11-28 02:04:46 +01:00
Philipp Hagemeister
40b35b4aa6 hack for apparently broken parse_qs in python2 2012-11-28 02:01:09 +01:00
Philipp Hagemeister
be0f77d075 test import 2012-11-28 02:00:45 +01:00
Philipp Hagemeister
0f00efed4c Woooohooo! python3 youtube_dl BaW_jenozKc -t works! 2012-11-28 00:56:20 +01:00
Philipp Hagemeister
e6137fd61d Remove superfluous encodings 2012-11-28 00:53:09 +01:00
Philipp Hagemeister
8cd10ac4ef Fix printing title etc. 2012-11-28 00:46:21 +01:00
Philipp Hagemeister
64a57846d3 correct to_stderr 2012-11-28 00:33:38 +01:00
Philipp Hagemeister
72f976701a youtube IE: Correct bytes vs str 2012-11-28 00:31:59 +01:00
Philipp Hagemeister
5bd9cc7a6a typo 2012-11-28 00:22:55 +01:00
Philipp Hagemeister
f660c89d51 Use list comprehension instead of map 2012-11-28 00:19:24 +01:00
Philipp Hagemeister
73dce4b2e4 Import from the correct module 2012-11-28 00:17:59 +01:00
Philipp Hagemeister
9f37a95941 Py2/3 parse_qs compatibility 2012-11-28 00:17:12 +01:00
Philipp Hagemeister
a130bc6d02 One more except..as 2012-11-28 00:13:40 +01:00
Philipp Hagemeister
348d0a7a18 Py2/3 compatibility for http.client 2012-11-28 00:13:00 +01:00
Philipp Hagemeister
03f9daab34 Use io.BytesIO instead of StringIO 2012-11-28 00:09:17 +01:00
Philipp Hagemeister
a8156c1d2e Python 3 version of HTMLParser 2012-11-28 00:06:28 +01:00
Philipp Hagemeister
3e669f369f Py3 compat for unichr and htmlentitydefs 2012-11-28 00:02:55 +01:00
Philipp Hagemeister
da779b4924 Fall back to urllib instead of urllib2 for Python 3 urllib.parse 2012-11-27 23:58:47 +01:00
Philipp Hagemeister
89fb51dd2d Remove ur references for Python 3.3 support 2012-11-27 23:56:10 +01:00
Philipp Hagemeister
01ba00ca42 Prepare urllib references for 2/3 compatibility 2012-11-27 23:54:09 +01:00
Philipp Hagemeister
e08bee320e Use except .. as everywhere (#180) 2012-11-27 23:31:55 +01:00
Philipp Hagemeister
96731798db Rename util.u to util.compat_str 2012-11-27 23:29:18 +01:00
Philipp Hagemeister
c116339ddb Merge branch 'master' of github.com:rg3/youtube-dl 2012-11-27 23:23:37 +01:00
Filippo Valsorda
e643e2c6b7 Merge pull request #563 from FiloSottile/IE_cleanup
General IE docs and return dicts cleanup
2012-11-27 14:22:40 -08:00
Filippo Valsorda
c63cc10ffa Merge remote-tracking branch 'origin/master' into IE_cleanup
Conflicts:
	youtube_dl/FileDownloader.py
2012-11-27 23:20:32 +01:00
Philipp Hagemeister
dae7c920f6 Make test_utils.py run on Python 3 2012-11-27 23:20:29 +01:00
Filippo Valsorda
f462df021a Use None on missing required info_dict fields 2012-11-27 23:15:33 +01:00
Philipp Hagemeister
1a84d8675b Use u instead of str in Python 2 2012-11-27 23:11:44 +01:00
Philipp Hagemeister
18ea0cefc3 Merge pull request #560 from phihag/fix-to_screen-mode
to_screen: Only encode when output stream is binary
2012-11-27 13:10:57 -08:00
Philipp Hagemeister
c806f804d8 Only encode when output stream is binary 2012-11-27 21:07:25 +01:00
Filippo Valsorda
03c5b0fbd4 IE._WORKING attribute in order to warn the users and skip the tests on broken IEs 2012-11-27 19:30:09 +01:00
Philipp Hagemeister
95649b3936 Replace long with int (see PEP 237) 2012-11-27 19:05:03 +01:00
Philipp Hagemeister
3aeb78ea4e Better formatting (PEP 8) 2012-11-27 19:03:37 +01:00
Philipp Hagemeister
dd109dee8e Remove mentions of unicode 2012-11-27 19:02:37 +01:00
Philipp Hagemeister
b514df2034 Clean up with the help of pep8 2012-11-27 18:55:35 +01:00
Philipp Hagemeister
0969bdd305 unify spacing 2012-11-27 18:49:18 +01:00
Philipp Hagemeister
1a9c655e3b Merge remote-tracking branch 'Asido/master' 2012-11-27 18:48:43 +01:00
Philipp Hagemeister
88db5ef279 2012.11.29 2012-11-27 18:36:43 +01:00
Philipp Hagemeister
f8d8b39bba Prepare 2012.11.29 release 2012-11-27 18:30:34 +01:00
Philipp Hagemeister
dcd60025f8 Fix filename sanitation (Closes #555) 2012-11-27 18:27:46 +01:00
Filippo Valsorda
7e4674830e document info_dict['subtitles'] and info_dict['urlhandle'] 2012-11-27 18:08:07 +01:00
Filippo Valsorda
9ce5d9ee75 make all IEs return 'upload_date' and 'uploader', even if only u'NA' 2012-11-27 17:57:12 +01:00
Filippo Valsorda
b49e75ff9a info_dict['upload_date'] is documented in --output, IEs MUST specify it 2012-11-27 17:38:22 +01:00
Filippo Valsorda
abe7a3ac2a info_dict['player_url'] is used only for rtmpdump, indicate it as optional in the info_dict 2012-11-27 17:32:25 +01:00
Filippo Valsorda
717b1f72ed default info_dict['format'] to info_dict['ext'] and make the YT one more verbose 2012-11-27 17:20:25 +01:00
Philipp Hagemeister
26396311b5 Add Christian Albrecht (Arte.tv IE) to authors 2012-11-27 17:16:49 +01:00
Philipp Hagemeister
dffe658bac Remove exclamation mark in --restrict-filenames mode 2012-11-27 17:15:33 +01:00
Philipp Hagemeister
33d94a6c99 Merge remote-tracking branch 'alab1001101/master' 2012-11-27 17:14:29 +01:00
Philipp Hagemeister
4d47921c9e ignore kate swap files 2012-11-27 17:01:12 +01:00
Philipp Hagemeister
d94adc2638 Actually fix manpage (#473) 2012-11-27 16:58:50 +01:00
Philipp Hagemeister
5c5d06d31d Merge pull request #473 from grimreaper/master
fix mdoc nits
2012-11-27 07:52:58 -08:00
Philipp Hagemeister
cc872b68a8 Actually merge #379 2012-11-27 16:42:50 +01:00
Philipp Hagemeister
17cb14a336 Merge remote-tracking branch 'joelverhagen/master' 2012-11-27 16:41:16 +01:00
Philipp Hagemeister
877f4c45d3 Fix output format doc 2012-11-27 16:28:29 +01:00
Philipp Hagemeister
02531431f2 Extended documentation for output format in README (Closes #268) 2012-11-27 16:27:35 +01:00
Philipp Hagemeister
e02066e7ff Windows build for 2012.11.28 2012-11-27 16:15:15 +01:00
Philipp Hagemeister
c9128b353d Bump version number to a numeric-only one to appease py2exe 2012-11-27 16:12:08 +01:00
Philipp Hagemeister
e7c6f1a2dc Bump version number 2012-11-27 16:08:39 +01:00
Philipp Hagemeister
1a911e60a4 Add test for asian characters (#551) 2012-11-27 16:07:52 +01:00
Philipp Hagemeister
46cbda0be4 Minor filename encoding improvement in a common case 2012-11-27 15:07:10 +01:00
Philipp Hagemeister
fa59f4b6a9 Merge remote-tracking branch 'chrisjrn/master' 2012-11-27 14:55:18 +01:00
Christopher Neugebauer
4a702f3819 Fixes the InfoExtractor for the Colbert Report. 2012-11-27 23:54:43 +11:00
Philipp Hagemeister
6bac102a4d Fix spacing in comedycentral IE 2012-11-27 13:24:10 +01:00
Philipp Hagemeister
958a22b7cf Merge remote-tracking branch 'chrisjrn/master' 2012-11-27 13:19:18 +01:00
Philipp Hagemeister
97cd3afc75 warn if %(stitle)s is being used 2012-11-27 13:11:06 +01:00
Philipp Hagemeister
aa2a94ed81 Encode the entire filename 2012-11-27 13:01:32 +01:00
Philipp Hagemeister
c7032546f1 Clean up test 2012-11-27 12:46:27 +01:00
Philipp Hagemeister
56781d3d2e Switch back to underline for invalid characters, and make restricted ASCII-only 2012-11-27 12:46:09 +01:00
Christopher Neugebauer
feb22fe5fe Fixed indentation error 2012-11-27 22:32:24 +11:00
Christopher Neugebauer
d8dddb7c02 Removes extranous debugging info :) 2012-11-27 22:30:07 +11:00
Christopher Neugebauer
4408d996fb Adds format listing/selection support to the Comedy Central extractor. 2012-11-27 22:28:16 +11:00
Philipp Hagemeister
ed7516c69d Merge remote-tracking branch 'chrisjrn/master' 2012-11-27 12:25:51 +01:00
Christopher Neugebauer
89af8e9d32 Removes extraneous debug message. 2012-11-27 21:51:30 +11:00
Christopher Neugebauer
36a9c0b5ff Points the ComedyCentral extractor at a CDN which works with more RTMPDump versions. 2012-11-27 21:49:27 +11:00
Philipp Hagemeister
9fb3bfb45a Merge remote-tracking branch 'gcmalloc/master' 2012-11-27 00:42:47 +01:00
Filippo Valsorda
d479e34043 release 2012.11.27 2012-11-27 00:22:39 +01:00
Philipp Hagemeister
240089e5df remove accidental remnants 2012-11-27 00:14:12 +01:00
Philipp Hagemeister
1c469a9480 New optoin --restrict-filenames 2012-11-26 23:58:46 +01:00
Philipp Hagemeister
71f36332dd Remove redundancy in instructions 2012-11-26 23:40:51 +01:00
Philipp Hagemeister
8179d2ba74 Merge branch 'master' of github.com:rg3/youtube-dl 2012-11-26 23:25:04 +01:00
Philipp Hagemeister
df4bad3245 Document configuration 2012-11-26 23:24:55 +01:00
Filippo Valsorda
a7b5c8d6a8 fix FAQ on how to compile (also, starnge fix in the Makefile) 2012-11-26 22:35:12 +01:00
Philipp Hagemeister
92b91c1878 Use character instead of byte strings 2012-11-26 04:23:20 +01:00
Philipp Hagemeister
7ec1a206ea Remove longs (int does the right thing since Python 2.2, see PEP 237) 2012-11-26 04:13:43 +01:00
Philipp Hagemeister
51937c0869 Add some parentheses around print for #180 2012-11-26 04:05:54 +01:00
Philipp Hagemeister
6b50761222 Merge pull request #538 from zejn/patch-1
Also enable album URLs on Vimeo.
2012-11-25 18:04:11 -08:00
Philipp Hagemeister
6571408dc6 Merge pull request #545 from FiloSottile/alias
Kill (alias) --literal and %(title)
2012-11-25 15:57:57 -08:00
Filippo Valsorda
b6fab35b9f alias %(title)s to %(stitle)s 2012-11-25 20:39:42 +01:00
Filippo Valsorda
baec15387c aliased --literal to --title 2012-11-25 20:28:49 +01:00
zejn
297d7fd9c0 Also enable album URLs on Vimeo. 2012-11-21 13:24:14 +01:00
Filippo Valsorda
5002aea371 release 2012.11.17 2012-11-17 14:02:31 +01:00
Filippo Valsorda
74033a662d Reworked Vimeo file selection logic (quality, codec) - closes #530 2012-11-13 21:53:18 +01:00
Filippo Valsorda
0526e4f55a Merge pull request #522 from art-zhitnik/master
--(match|reject)-title utf8 fix
2012-11-11 06:22:10 -08:00
Art Zhitnik
39973a0236 Solve the bug of parsing titles with unicode (cyrillic) 2012-11-11 14:09:12 +10:00
Filippo Valsorda
5d40a470a2 quiet the HTMLParser debug info - closes #517 2012-11-09 12:32:07 +01:00
Filippo Valsorda
4cc391461a fix DailyMotion official users videos - closes #281 - by @yvestan 2012-11-07 14:44:10 +01:00
Filippo Valsorda
bf95333e5e fixed MetacafeIE (uploader nickname regex) - closes #515 2012-11-06 23:08:10 +01:00
Philipp Hagemeister
b7a34316d2 -x for --extract-audio, one of the most popular options 2012-10-30 17:41:38 +01:00
Philipp Hagemeister
74e453bdea New --id option for the old default filename pattern 2012-10-30 17:37:53 +01:00
Philipp Hagemeister
156a59e7a9 Additional tests in file name sanitation 2012-10-29 08:19:54 +01:00
Philipp Hagemeister
aeca861f22 Merge pull request #502 from FiloSottile/new_sanitize_filename
My sanitize_filename proposal
2012-10-28 15:33:59 -07:00
Filippo Valsorda
42cb53fcfa modified filename escaping to a "smarter" one 2012-10-28 22:47:02 +01:00
Filippo Valsorda
fe4d68e196 slight change to Dailymotion uploader regex (fix) 2012-10-28 21:43:43 +01:00
Philipp Hagemeister
25b7fd9c01 Merge pull request #491 from tyll/master
Update install target
2012-10-26 01:10:25 -07:00
Till Maas
e79e8b7dc4 Update install target
- Allow to configure destination directories to fulfill the needs of
  different distributions
- Support DESTDIR variable for staging installation when packaging
- Do not set user/group to root. It requires 'make install' to run as
  root, but then this is the default behaviour anyways.
2012-10-25 21:19:13 +02:00
Filippo Valsorda
965a8b2bc4 Merge pull request #488 from Tailszefox/local
Fix audio bitrate quality for ffmpeg/avconv (closes #487)
2012-10-24 11:42:31 -07:00
gcmalloc
a8ac2f8664 adding second vimeo url 2012-10-24 15:57:19 +02:00
gcmalloc
fb0e99b884 skipping vimeo for the moment 2012-10-24 00:32:23 +02:00
gcmalloc
9c6e9a4532 adding xnxx test 2012-10-24 00:13:16 +02:00
gcmalloc
67af74992e adding collegehumor test 2012-10-24 00:05:45 +02:00
gcmalloc
103c508ffa adding stanford open class courses 2012-10-23 23:59:12 +02:00
gcmalloc
2876773381 adding test for vimeo, xvideo and soundcloud 2012-10-23 23:53:33 +02:00
Tailszefox
f06eaa873e Fix audio bitrate quality for ffmpeg/avconv 2012-10-23 16:37:12 +02:00
Philipp Hagemeister
ece34e8951 Merge pull request #486 from Tailszefox/local
Added duration for YouTube videos
2012-10-23 05:53:28 -07:00
Tailszefox
2262a32dd7 Added duration for YouTube videos 2012-10-22 18:32:42 +02:00
Philipp Hagemeister
c6c0e23a32 Support raw playlist parameters (Closes #482) 2012-10-22 13:01:36 +02:00
Philipp Hagemeister
02b324a23d Restore 2.5 compat by activating with_statement future 2012-10-22 12:51:20 +02:00
Filippo Valsorda
b8005afc20 handle YT urls with #/ redirects (closes #484) 2012-10-22 09:15:27 +02:00
Philipp Hagemeister
073522bc6c Don't use 2.7+ check_output 2012-10-19 23:28:37 +02:00
Philipp Hagemeister
9248cb0549 Merge pull request #472 from gcmalloc/master
Test proposal
2012-10-19 05:48:12 -07:00
gcmalloc
6b41b61119 correcting travis 2012-10-19 12:53:20 +02:00
gcmalloc
591bbe9c90 changing test from md5 to filesize, the file changed between download 2012-10-19 12:53:20 +02:00
gcmalloc
fc7376016c cleaning the test that doesn't work with the api for the moment 2012-10-19 12:53:20 +02:00
gcmalloc
97a37c2319 some assertion on the file downloaded 2012-10-19 12:53:20 +02:00
gcmalloc
3afed78a6a removing testing video 2012-10-19 12:53:20 +02:00
gcmalloc
4279a0ca98 correcting test to be compatible with python2.6 2012-10-19 12:53:20 +02:00
gcmalloc
edcc7d2dd3 StringIO used by nosetests do not merge with the way youtube-dl handle sys.stdout and sys.stderr 2012-10-19 12:53:19 +02:00
gcmalloc
7f60b5aa40 correction on the test 2012-10-19 12:53:19 +02:00
Eitan Adler
65adb79fb6 Fix mandoc nits 2012-10-15 21:45:56 -04:00
gcmalloc
aeeb29a356 adding travis support 2012-10-15 10:58:35 +02:00
Filippo Valsorda
902b2a0a45 New IE: YouTube channels (closes #396) 2012-10-14 13:48:18 +02:00
gcmalloc
6d9c22cd26 correcting the makefile according to the new one 2012-10-12 20:30:01 +02:00
gcmalloc
729baf58b2 removing extended globbing for the find utility 2012-10-12 20:25:22 +02:00
gcmalloc
4c9afeca34 adding xvideo 2012-10-12 20:25:22 +02:00
gcmalloc
6da7877bf5 adding facebook test 2012-10-12 20:25:22 +02:00
gcmalloc
b4e5de51ec adding photobucket test 2012-10-12 20:25:22 +02:00
gcmalloc
a4b5f22554 adding metacafe test 2012-10-12 20:25:22 +02:00
gcmalloc
ff08984246 adding dailymotion test 2012-10-12 20:25:22 +02:00
gcmalloc
137c5803c3 some changes to keep the same standard 2012-10-12 20:25:22 +02:00
gcmalloc
3eec021a1f removing unused global modifier 2012-10-12 20:25:22 +02:00
gcmalloc
5a33b73309 correcting the makefile 2012-10-12 20:25:22 +02:00
gcmalloc
0b4e98490b changing test video 2012-10-12 20:24:58 +02:00
gcmalloc
80a846e119 correction on the test for the utils.py 2012-10-12 20:24:58 +02:00
gcmalloc
434d60cd95 adding clean rule in the makefile 2012-10-12 20:24:58 +02:00
gcmalloc
efe8902f0b adding download test with md5 check 2012-10-12 20:24:58 +02:00
gcmalloc
44fb345437 adding TestCase class and corresponding test 2012-10-12 20:24:58 +02:00
gcmalloc
9993976ae4 correction on the sanitize title method, change in title resulting 2012-10-12 20:24:58 +02:00
gcmalloc
b387fb0385 adding test rule in the Makefile 2012-10-12 20:24:58 +02:00
Filippo Valsorda
10daa766a1 support EDU YouTube playlists (closes #407) 2012-10-11 08:27:19 +02:00
Filippo Valsorda
7b107eea51 release 2012.10.09 2012-10-09 15:53:20 +02:00
Filippo Valsorda
646b885cbf Added missing dependencies to Makefile 2012-10-09 15:49:24 +02:00
Filippo Valsorda
0bfd0b598a Re-engineered Dailymotion qualities selection (thanks @knagano, sort of merges #176) 2012-10-09 12:28:44 +02:00
Filippo Valsorda
fd873c69a4 Merge PR #422 from 'kevinamadeus/master'
Add InfoExtractor for Google Plus video
(with fixes)
2012-10-09 10:48:49 +02:00
Filippo Valsorda
d64db7409b Merge pull request #458 from grimreaper/patch-1
There is nothing bash specific in release.sh, switch to /bin/sh
2012-10-09 01:16:40 -07:00
Philipp Hagemeister
27fec0e3bd Merge branch 'master' of github.com:rg3/youtube-dl 2012-10-08 22:14:28 +02:00
Philipp Hagemeister
65f934dc93 Correct detect_executables on Windows (Closes #447, #457) 2012-10-08 22:14:19 +02:00
grimreaper
d51d784f85 There is nothing bash specific here
/bin/bash is always wrong. Since there is nothing bash specific here, switch to /bin/sh
2012-10-06 10:00:40 -03:00
Filippo Valsorda
aa85963987 Merge pull request #452 from Tailszefox/local
Added uploaded date for Dailymotion
2012-10-03 11:29:51 -07:00
Tailszefox
413575f7a5 Added uploaded date for Dailymotion 2012-10-03 10:57:46 +02:00
Philipp Hagemeister
b7b4796bf2 Fix docs 2012-10-01 18:39:24 +02:00
Philipp Hagemeister
fcbc8c830e Merge branch 'master' of github.com:rg3/youtube-dl 2012-10-01 18:38:19 +02:00
Philipp Hagemeister
f48ce130c7 Fix doc of extractor field 2012-10-01 18:38:10 +02:00
Filippo Valsorda
13e69f546c Merged, modified and compiled Dailymotion pull request #446 by @Steap 2012-09-30 21:45:43 +02:00
Cyril Roelandt
63ec7b7479 DailymotionIE: There is not necessarily an underscore in a Dailymotion URL. 2012-09-30 15:47:37 +02:00
Cyril Roelandt
7b6d7001d8 DailymotionIE: some videos do not use the "hqURL", "sdURL", "ldURL" keywords. In this case, the "video_url" keyword should be looked for. 2012-09-30 15:47:29 +02:00
Filippo Valsorda
39ce6e79e7 Updated youtube-dl.exe 2012-09-29 19:12:56 +02:00
Filippo Valsorda
5c961d89df Merge pull request #403 from FiloSottile/re_VERBOSE 2012-09-29 17:05:40 +02:00
Filippo Valsorda
3c4d6c9eba Not all Dailymotion videos have an hqURL, now downloads highest quality available 2012-09-29 16:53:06 +02:00
Filippo Valsorda
349e2e3e21 Fixed DailymotionIE, now downloads high-def mp4s, which might be too much (?) 2012-09-29 16:38:38 +02:00
Filippo Valsorda
551fa9dfbf adding new --output replacements. Thanks @danut007ro (closes #442) 2012-09-29 15:49:10 +02:00
Filippo Valsorda
ce3674430b added new FAQ on exe dependency 2012-09-29 15:35:07 +02:00
Filippo Valsorda
5cdfaeb37b New FAQ: What is this binary file? (+ small fix to other one) 2012-09-28 19:55:18 +02:00
Philipp Hagemeister
38612b4edc update default UA string (Closes #390) 2012-09-27 23:38:11 +02:00
Philipp Hagemeister
6c5b442a9b Add recent breakage to FAQ (Closes #433) 2012-09-27 23:30:17 +02:00
Philipp Hagemeister
5a5523698d Add new field "extractor" to the info dictionary 2012-09-27 20:48:16 +02:00
Philipp Hagemeister
05a2c206be Merge pull request #425 from danut007ro/master
Provider (youtube, etc) is now saved in info_dict
2012-09-27 11:45:07 -07:00
Philipp Hagemeister
8ca21983d8 Merge pull request #432 from cryzed/master
Fixed YouTube playlist parsing
2012-09-27 11:42:58 -07:00
Philipp Hagemeister
20326b8b1b Let Makefile use youtube-dl source code instead of compiled binary 2012-09-27 20:21:20 +02:00
Philipp Hagemeister
5d534e2fe6 Improve option definitions 2012-09-27 20:19:27 +02:00
Philipp Hagemeister
234e230c87 Merge remote-tracking branch 'FiloSottille/vbr'
Conflicts:
	youtube-dl
	youtube-dl.exe
2012-09-27 20:18:29 +02:00
Philipp Hagemeister
34ae0f9d20 Merge branch 'master' of github.com:rg3/youtube-dl 2012-09-27 19:56:29 +02:00
Philipp Hagemeister
df09e5f9e1 Merge pull request #405 from hdclark/master
Support for custom user agent
2012-09-27 10:56:25 -07:00
cryzed
3af2f7656c Fixed YouTube playlist parsing 2012-09-27 19:48:29 +02:00
Philipp Hagemeister
74e716bb64 original test video 2012-09-27 19:44:44 +02:00
Philipp Hagemeister
85f76ac90b Merge remote-tracking branch 'FiloSottille/automation' 2012-09-27 19:41:51 +02:00
Philipp Hagemeister
7f36e39676 Merge remote-tracking branch 'FiloSottille/supports'
Conflicts:
	youtube-dl
2012-09-27 19:24:41 +02:00
Philipp Hagemeister
ebe3f89ea4 Merge xnxx.com Support (NSFW). Test URL (SFW): http://video.xnxx.com/video1443330/youtube-dl_testvid_a_and_9829_._and_amp_and_38_ 2012-09-27 18:55:56 +02:00
Philipp Hagemeister
b5de8af234 Release 2012.09.27 2012-09-27 11:25:46 +02:00
Philipp Hagemeister
eb817499b0 Compile updated youtube-dl 2012-09-27 11:23:44 +02:00
Philipp Hagemeister
e2af9232b2 Merge pull request #428 from virtulis/master
A quick fix to #427
2012-09-27 02:22:05 -07:00
Danko Alexeyev
9ca667065e Add 'signature' to YouTube URLs, fixes #427 2012-09-27 09:44:49 +03:00
danut007ro
ae16f68f4a Provider (youtube, etc) is now saved in info_dict, so template filename can be something like %(provider)s_%(id)s.%(ext)s
This can be useful because videos should also be identified by their providers since id's can be the same on multiple providers.
2012-09-27 00:35:31 +03:00
danut007ro
3cd98c7894 Removed provider (mistake) and add provider parameter to process_info 2012-09-27 00:07:20 +03:00
danut007ro
2866e68838 Merge branch 'master' of https://github.com/rg3/youtube-dl 2012-09-26 21:09:44 +03:00
danut007ro
be8786a6a4 Every extractor also return it's name. 2012-09-26 21:00:28 +03:00
Filippo Valsorda
0e841bdc54 add PREFIX option to make install 2012-09-26 00:10:39 +02:00
Filippo Valsorda
225dceb046 moved make release to devscripts/release.sh 2012-09-25 23:56:01 +02:00
Philipp Hagemeister
b0d4f95899 Merge pull request #391 from rbrito/support-tube.majestyc.net
Support downloading Youtube videos via tube.majestyc.net
2012-09-25 14:17:13 -07:00
Kevin Kwan
d443aca863 Add InfoExtractor for Google Plus video 2012-09-25 16:21:02 +08:00
Christian Albrecht
2ebc6e6a92 Make youtube-dl 2012-08-26 09:57:49 +02:00
Christian Albrecht
f2ad10a97d Add arte.tv Info Extractor 2012-08-26 09:47:19 +02:00
hdclark
ea46fe2dd4 Added support for custom user agents.
Added a few simple lines to add support for the flag "--user-agent" to pass a custom string to std_header['User-Agent'].
2012-08-22 23:40:35 -07:00
Filippo Valsorda
202e76cfb0 Made the YouTubeIE regex verbose/commented 2012-08-20 00:58:10 +02:00
Filippo Valsorda
3a68d7b467 tweaked the --audio-quality input validation/specification 2012-08-19 23:25:16 +02:00
Filippo Valsorda
795cc5059a Re-engineered XNXXIE to actually exit on ERRORs even with -i 2012-08-19 18:46:23 +02:00
Filippo Valsorda
5dc846fad0 Merge pull request #398 from tempname/master 2012-08-19 18:39:43 +02:00
Filippo Valsorda
d5c4c4c10e bugfix and standarize the youku.com support 2012-08-19 17:44:34 +02:00
Filippo Valsorda
1ac3e3315e Merge pull request #395 from thesues/master 2012-08-19 17:08:39 +02:00
Filippo Valsorda
0e4dc2fc74 Merge 'rbrito/support-tube.majestyc.net' (PR #391) with small fix 2012-08-19 17:00:20 +02:00
Filippo Valsorda
9bb8dc8e42 Python 2.6 compatibility fix. Thanks @Jamesc359 - closes #400 2012-08-19 16:06:33 +02:00
tempname
154b55dae3 added InfoExtractor for XNXX 2012-08-15 20:57:27 -03:00
tempname
6de7ef9b8d added InfoExtractor for XNXX 2012-08-15 20:54:03 -03:00
dongmao zhang
392105265c Merge branch 'master' of github.com:thesues/youtube-dl
Conflicts:
	youtube-dl
	youtube_dl/InfoExtractors.py
2012-08-10 18:32:28 +08:00
dongmao zhang
51661d8600 add www.youku.com support 2012-08-09 13:54:19 +08:00
dongmao zhang
b5809a68bf merge 2012-08-09 12:26:26 +08:00
dongmao zhang
7733d455c8 fix 0a->0A bug 2012-08-09 03:14:02 +08:00
dongmao zhang
0a98b09bc2 youku default to download hd2 video 2012-08-09 02:53:21 +08:00
dongmao zhang
302efc19ea add youku support 2012-08-09 02:04:02 +08:00
Rogério Brito
55a1fa8a56 Support downloading Youtube videos via tube.majestyc.net
A user requested (in Debian's bug tracking system) that support for
tube.majestyc.net, a frontend for Youtube with accessibility functions
(and other support for other assistive technologies), be added.

This patch adds support for this.

Signed-off-by: Rogério Brito <rbrito@ime.usp.br>
2012-08-05 23:37:33 -03:00
Filippo Valsorda
dce1088450 A more "make-esque" Makefile with file targets and dependencies 2012-08-03 20:10:54 +02:00
Philipp Hagemeister
a171dbfc27 Merge pull request #386 from FiloSottile/blip
Blip.tv
2012-08-01 12:26:00 -07:00
Filippo Valsorda
11a141dec9 BlipTVUserIE fix 2012-08-01 21:11:04 +02:00
Filippo Valsorda
818282710b moved the User-Agent workaround to the BlipTV IE 2012-08-01 20:51:56 +02:00
Filippo Valsorda
7a7c093ab0 added one-step realese script 'make release version=nn' - closes #158 2012-08-01 18:40:27 +02:00
Filippo Valsorda
ce7b2a40d0 added automatically generated bash-completion; closes #191 2012-08-01 17:26:50 +02:00
Filippo Valsorda
cfcec69331 auto-generating manpage from README.md (closes #151); redesigned Makefile 2012-08-01 11:54:27 +02:00
Filippo Valsorda
91645066e2 Merge branch 'joehillen/master' - pull request #381 2012-08-01 11:35:04 +02:00
Filippo Valsorda
dee5d76923 changed YouTube closed captions URL; closes #382 2012-07-31 15:56:35 +02:00
Filippo Valsorda
363a4e1114 xvideos patch by @pocoimporta - closes #370 2012-07-31 01:40:29 +02:00
joehillen
ef0c08cdfe Added install target to Makefile. 2012-07-22 13:36:22 -07:00
Philipp Hagemeister
3210735c49 Fix EscapistMagazine IE 2012-07-18 21:17:51 +02:00
Joel Verhagen
aab4fca422 Updated --no-resize-buffer docs, removed -b option 2012-07-16 10:59:21 -04:00
Joel Verhagen
891d7f2329 Added options to set download buffer size and disable automatic buffer resizing. 2012-07-14 16:47:19 -04:00
Filippo Valsorda
b24676ce88 changed --audio-quality behaviour to support both CBR and VBR 2012-07-14 19:43:24 +02:00
Filippo Valsorda
cca4828ac9 fixed a logic bug in post-processing 2012-07-14 14:35:57 +02:00
Arvydas Sidorenko
bae611f216 Simplified preferredencoding()
Not sure what is the point to use yield to return encoding, thus
it will simplify the whole function.

Signed-off-by: Arvydas Sidorenko <asido4@gmail.com>
2012-07-01 18:21:27 +02:00
Filippo Valsorda
d4e16d3e97 YouTube playlist fix; closes #365 and #331 2012-06-30 15:04:30 +02:00
Filippo Valsorda
65dc7d0272 Merge pull request #363 from chalet16/master
Change a number of subtitle sequence to begin with one - closes #362
2012-06-26 05:35:37 -07:00
Witchakorn Kamolpornwijit
5404179338 Change a number of subtitle sequence to begin with one (instead of zero) for ffmpeg,avcodec, and Matroska compatibility 2012-06-26 19:24:30 +07:00
Filippo Valsorda
7df97fb59f display a meaningful error message on rental videos (#359) 2012-06-22 13:57:17 +02:00
Filippo Valsorda
3187e42a23 Merge pull requests #356 #357 #358 by jcarlosgarciasegovia 2012-06-06 20:51:29 +02:00
Juan Carlos Garcia Segovia
f1927d71e4 Some blip.tv URLs use Unicode characters. urllib2 breaks when passing a Unicode string. it needs a UTF-8 byte buffer 2012-06-06 16:24:29 +00:00
Juan Carlos Garcia Segovia
eeeb4daabc Information Extractor for blip.tv users 2012-06-06 16:16:16 +00:00
Juan Carlos Garcia Segovia
3c4fc580bb Use an User-Agent that will allow downloading from blip.tv fixes #325 2012-06-06 13:24:12 +00:00
Filippo Valsorda
17f3c40a31 Merge pull request #353 from FiloSottile/avconv
check for avconv and ffmpeg, use as available; closes #344
2012-06-03 03:39:16 -07:00
Filippo Valsorda
505ed3088f normalize ffmpeg/avconv names printing 2012-06-03 12:11:39 +02:00
Filippo Valsorda
0b976545c7 check for avconv and ffmpeg, use as available; closes #344 2012-06-03 12:10:15 +02:00
Philipp Hagemeister
a047951477 Merge pull request #352 from chocolateboy/decontaminate_stdout
don't corrupt stdout (-o -) in verbose mode
2012-05-31 00:04:32 -07:00
chocolateboy
6ab92c8b62 don't corrupt stdout (-o -) in verbose mode 2012-05-30 11:50:13 +01:00
Filippo Valsorda
f36cd07685 fixed a couple of Windows exe update bugs 2012-05-27 23:03:45 +02:00
Philipp Hagemeister
668d975039 quiet zip in make compile 2012-05-23 19:19:53 +02:00
Philipp Hagemeister
9ab3406ddb Fix Escapist IE 2012-05-23 19:19:31 +02:00
Philipp Hagemeister
1b91a2e2cf Merge pull request #342 from FiloSottile/master
Re-organized code and a lot of other stuff.
2012-05-22 04:35:59 -07:00
Filippo Valsorda
2c288bda42 reorganized the titles sanitizing: now title is the untouched title
and stitle is created in process_info() and is cross-filesystem sanitized by sanitize_filename();
closes #164
2012-05-09 14:47:28 +02:00
Filippo Valsorda
0b8c922da9 Introduced Trouble(Exception) for more elegant non-fatal errors handling 2012-05-09 09:43:11 +00:00
Filippo Valsorda
3fe294e4ef merge upstream 2012-05-01 18:22:08 +02:00
Filippo Valsorda
921a145592 dropped the support for Python 2.5
let's elaborate the decision: Python 2.5 is a 6 years old release
and "under the current release policy, no security issues in Python
2.5 will be fixed anymore" (!!); also, it doesn't support the new
zipfile distribution format.
2012-05-01 17:01:51 +02:00
Philipp Hagemeister
0c24eed73a merge #336 2012-04-19 09:46:01 +02:00
Philipp Hagemeister
29ce2c1201 Merge git://git.jankratochvil.net/youtube-dl 2012-04-19 09:44:25 +02:00
Jan Kratochvil
532c74ae86 Add format #46 - WebM 1920x1080. 2012-04-16 17:13:01 +02:00
Filippo Valsorda
9beb5af82e some HTMLParser bugfixes 2012-04-13 22:09:24 +02:00
Filippo Valsorda
9e6dd23876 merged unescapeHTML branch; removed lxml dependency 2012-04-11 00:22:51 +02:00
Filippo Valsorda
7a8501e307 ignore parsing errors in get_element_by_id() 2012-04-10 23:08:53 +02:00
Filippo Valsorda - Campagna
781cc523af removed the undocumented HTMLParser.unescape, replaced with _unescapeHTML; fixed a bug in the use of _unescapeHTML (missing _, from d6a9615347) 2012-04-10 18:54:40 +02:00
Filippo Valsorda - Campagna
c6f45d4314 removed dependency from lxml: added IDParser 2012-04-10 18:21:00 +02:00
Filippo Valsorda - Campagna
d11d05d07a better naming for the sub-modules 2012-04-10 16:46:36 +02:00
Filippo Valsorda - Campagna
e179aadfdf moved trivialjson to a separate file 2012-04-10 16:37:40 +02:00
Filippo Valsorda - Campagna
d6a9615347 standardized the use of unescapeHTML; added clean_html() 2012-04-10 16:31:46 +02:00
Filippo Valsorda - Campagna
c6306eb798 wine-py2exe.sh to create the exe under linux (!!) 2012-04-07 20:07:42 +02:00
Filippo Valsorda
bcfde70d73 py2exe -U fix for Windows XP 2012-03-31 01:27:47 +02:00
Filippo Valsorda
53e893615d corrected -U to support new zipfile and exe (#153) formats 2012-03-31 01:19:30 +02:00
Filippo Valsorda
303692b5ed 's/ /\t/' 2012-03-30 23:54:16 +02:00
Filippo Valsorda
58ca755f40 moved increment_downloads and process_info calls from IEs to FD.download (#296) (follows current doclines); a small step towards importability #217 2012-03-30 23:45:27 +02:00
Filippo Valsorda
770234afa2 Added py2exe script 2012-03-25 23:48:53 +02:00
Filippo Valsorda
d77c3dfd02 Split code as a package, compiled into an executable zip 2012-03-25 03:07:37 +02:00
Filippo Valsorda
c23d8a74dc Merge branch 'next-url' 2012-03-25 01:07:47 +01:00
Filippo Valsorda
74a5ff5f43 transplant ceba827e9a, d891ff9fd9, 69d3b2d824, 071940680f 2012-03-24 01:23:19 +01:00
Filippo Valsorda
071940680f Always extract original URL from next_url (#318) 2012-03-24 01:17:36 +01:00
Witold Baryluk
69d3b2d824 Extract original URL from next_url parameter of verify_age page, before actual extract 2012-03-23 06:17:29 +01:00
Witold Baryluk
d891ff9fd9 Ignore leading spaces (and trailing also) in all URL from url list or command line 2012-03-23 06:15:57 +01:00
Filippo Valsorda
6af22cf0ef added support for HTTP redirects. Closes #315 2012-03-18 22:15:58 +01:00
Philipp Hagemeister
fff24d5e35 Clean up superfluous whitespace 2012-03-15 20:52:35 +01:00
Philipp Hagemeister
ceba827e9a Credit Filippo Valsorda 2012-03-15 20:47:27 +01:00
Filippo Valsorda
a0432a1e80 added --srt-lang; updated README; extended the -g FAQ 2012-03-15 14:56:08 +01:00
Filippo Valsorda
cfcf32d038 Merge branch 'master' of git://github.com/rg3/youtube-dl into closed-captions 2012-03-15 14:05:34 +01:00
Philipp Hagemeister
a67bdc34fa transplant gist of 7151f63a5f 2012-03-15 08:36:31 +01:00
Philipp Hagemeister
b3a653c245 Merge commit '7151f63a5f3820a322ba8bf61eebe8d9f75d6ee5' 2012-03-15 08:26:44 +01:00
Philipp Hagemeister
4a34b7252e transplant 2934c2ce43 and afbaa80b8b 2012-03-15 08:05:21 +01:00
Philipp Hagemeister
7e45ec57a8 transplant 0f6e296a8e 2012-03-15 07:56:32 +01:00
Filippo Valsorda
afbaa80b8b switched ytsearch to more robust Youtube Data API (fixes #307) 2012-03-14 22:44:45 +01:00
Filippo Valsorda
115d243428 added youtube closed captions .srt support (see #90) 2012-03-13 23:49:33 +01:00
cryzed
7151f63a5f Fixed downloading of unrelated videos when downloading a YouTube playlist 2012-03-09 22:05:35 +01:00
Filippo Valsorda
597e7b1805 Vimeo: Added support for flv only videos 2012-03-07 21:02:12 +01:00
Filippo Valsorda
2934c2ce43 Switch Vimeo to scraping: fixes #285 2012-03-05 17:51:16 +01:00
Filippo Valsorda
0f6e296a8e Fixed gvsearch 2012-03-02 00:35:56 +01:00
Philipp Hagemeister
9c228928b6 release 2012.02.27 2012-02-27 20:19:28 +01:00
Philipp Hagemeister
ff3a2b8eab Always determine youtube description 2012-02-27 20:19:03 +01:00
Philipp Hagemeister
c4105fa035 release 2012.02.26 2012-02-27 00:42:26 +01:00
Philipp Hagemeister
871dbd3c92 Output RTMP command line if verbose is set 2012-02-27 00:41:53 +01:00
Philipp Hagemeister
c9ed14e6d6 Move imports to top (Closes #283) 2012-02-26 23:53:56 +01:00
Philipp Hagemeister
1ad85e5061 Set default continue behavior to true, no breakage observed in the wild 2012-02-26 23:44:32 +01:00
Philipp Hagemeister
09fbc6c952 verbose flag, and output proxies if it is set 2012-02-26 23:33:19 +01:00
Philipp Hagemeister
895ec266bb Merge pull request #292 from rbrito/fixes/vimeo-ie
VimeoIE: Allow matches taken from embedded videos.
2012-02-26 14:25:12 -08:00
Rogério Brito
d85448f3bb VimeoIE: Allow matches taken from embedded videos.
With this change, I can directly cut and paste URLs embedded in 3rd-party
pages as `youtube-dl`'s arguments.

Signed-off-by: Rogério Brito <rbrito@ime.usp.br>
2012-02-24 07:12:21 -02:00
Philipp Hagemeister
99d46e8c27 Merge pull request #275 from grawity/winnt-unicode
Support Unicode in file names on Windows NT
2012-01-16 03:23:22 -08:00
Mantas Mikulėnas
4afdff39d7 Support Unicode in file names on Windows NT 2012-01-16 12:08:01 +02:00
Philipp Hagemeister
661a807c65 Release 2012.01.08b 2012-01-08 17:23:10 +01:00
Philipp Hagemeister
6d58c4546e correct to_screen prints 2012-01-08 17:22:48 +01:00
Philipp Hagemeister
38ffbc0222 Release 2012.01.08 2012-01-08 17:20:55 +01:00
Philipp Hagemeister
fefb166c52 Leave out characters the filesystem cannot encode (Closes: #264) 2012-01-08 17:20:18 +01:00
Philipp Hagemeister
dcb3c22e0b MTV IE 2012-01-07 01:30:30 +01:00
Philipp Hagemeister
47a53c9e46 release 2012.01.05 2012-01-05 11:08:50 +01:00
Philipp Hagemeister
1413cd87eb Correct distinction between unicode and bytes (Closes: #257) 2012-01-05 10:46:21 +01:00
Philipp Hagemeister
c92e184f75 Correct comedycentral flash URL regex 2012-01-05 00:39:47 +01:00
Philipp Hagemeister
3906e6ce60 correct epydoc 2012-01-05 00:36:47 +01:00
Philipp Hagemeister
c7d3c3db0d Fix tds RTMP url extraction 2012-01-04 14:08:17 +01:00
Philipp Hagemeister
d6639d05c2 release 2011.12.18 2011-12-17 01:35:05 +01:00
Philipp Hagemeister
633cf7cbad Add wav audio output 2011-12-17 01:32:28 +01:00
Philipp Hagemeister
a5647b79ce Only skip download if files exists; convert audio 2011-12-16 23:33:46 +01:00
Philipp Hagemeister
ba5059dd66 Release 2011.12.15 2011-12-15 20:32:37 +01:00
Philipp Hagemeister
bb8abbbbae Dailymotion: Use og:title instead of <title> to find title (Closes: #253) 2011-12-15 20:32:05 +01:00
Philipp Hagemeister
561504fffa Release 2011.12.08 2011-12-08 21:39:13 +01:00
Philipp Hagemeister
23e6b8adc8 --prefer-free-formats (Closes #231) 2011-12-08 21:38:28 +01:00
Philipp Hagemeister
3e0ea7d07a m4a: aac in mp4 container (Closes #240) 2011-12-08 21:21:25 +01:00
Philipp Hagemeister
94fd3201b2 Abort when --max-downloads is reached. 2011-12-08 20:59:02 +01:00
Philipp Hagemeister
0b3f3e1ad9 Merge pull request #245 from rbrito/fix-makefile
Makefile: Don't use `echo`'s `-e` option for portability.
2011-12-08 11:39:56 -08:00
Philipp Hagemeister
a05d2a0c05 Merge branch 'master' of github.com:rg3/youtube-dl 2011-12-08 20:39:22 +01:00
Philipp Hagemeister
0b14e0b367 OpenClassRoom IE (Closes: #234) 2011-12-08 20:36:00 +01:00
Rogério Brito
66e8777769 Makefile: Don't use echo's -e option for portability.
Many systems (including Debian, Ubuntu and derivatives like Linux Mint) use
Dash as a noninteractive version of `/bin/sh`, invoked by `make`.

Dash's `echo` command doesn't understand the `-e` option and this generates
spurious output when running `make`.  See [a bugreport][0] for one of the
many instances of this bug/feature in action.

[0]: https://bugs.launchpad.net/ubuntu/+source/dash/+bug/72167
2011-12-08 13:18:29 -02:00
Philipp Hagemeister
348486ced4 Merge pull request #238 from rbrito/add-to-gitignore
Add list of files to ignore for `youtube-dl`.
2011-11-30 11:45:17 -08:00
Rogério Brito
f1f300e629 Add list of files to ignore for youtube-dl. 2011-11-30 14:17:20 -02:00
Philipp Hagemeister
dd17922afc OpenClassRoom videos (#234) 2011-11-30 10:52:04 +01:00
Philipp Hagemeister
40fd4cb86a Move merged code to dev version 2011-11-30 10:00:36 +01:00
Philipp Hagemeister
9e9b75ae4d Merge pull request #236 from lra/dailymotion-fix
Fix the DailymotionIE to parse the new title of a webpage
2011-11-30 00:57:09 -08:00
Laurent Raufaste
8abf76ddb9 Fix the DailymotionIE to parse the new title of a webpage 2011-11-29 22:30:42 -05:00
Philipp Hagemeister
c95da745bc Mention -o - in doc (Closes #204) 2011-11-29 20:22:27 +01:00
Philipp Hagemeister
0cd235eef6 Use freedesktop.org mandated user config file location (Suggested by Tyll in #231) 2011-11-29 20:13:13 +01:00
Philipp Hagemeister
77315556f1 Do not count unmatched or skipped videos towards max-downloads (Closes #232) 2011-11-29 20:08:01 +01:00
Philipp Hagemeister
c379c181e0 Preliminary implementation of configuration files 2011-11-28 01:29:46 +01:00
Philipp Hagemeister
31a2ec2d88 Document -o %(upload_date)s (Closes #228) 2011-11-28 01:00:01 +01:00
Philipp Hagemeister
b88a52504e --max-downloads option (Closes #230) 2011-11-28 00:55:44 +01:00
Philipp Hagemeister
a95567af99 Credit shizeeg for Mixcloud IE 2011-11-24 18:58:19 +01:00
Philipp Hagemeister
849edab8ec Move MixcloudIE to __init__.py 2011-11-24 18:02:12 +01:00
sh!zeeg
b158a1d946 Mixcloud IE 2011-11-24 20:45:14 +04:00
Philipp Hagemeister
fa2672f9fc Release 2011.11.23 2011-11-23 10:36:52 +01:00
Philipp Hagemeister
28e3614bc0 Fix vimeo error (Closes #224) 2011-11-23 10:35:55 +01:00
Philipp Hagemeister
208e095f72 Correct simplify_title call in ComedyCentral IE 2011-11-22 21:26:38 +01:00
Philipp Hagemeister
0ae7abe57c Release 2011.11.22 2011-11-22 15:32:53 +01:00
Philipp Hagemeister
dc0a294a73 Make exception handling 2.5-compatible (Closes #223) 2011-11-22 15:31:30 +01:00
Philipp Hagemeister
468c99257c Release 2011.11.21 2011-11-21 21:51:24 +01:00
Philipp Hagemeister
af8e8d63f9 Allow non-ASCII characters in simplified titles(Closes #220) 2011-11-21 21:50:39 +01:00
Philipp Hagemeister
e092418d8b Simplify simplify_title 2011-11-21 20:17:07 +01:00
Philipp Hagemeister
e33e3045c6 First tests 2011-11-21 20:07:24 +01:00
Philipp Hagemeister
cb6568bf21 Use the dev version in Makefile 2011-11-21 20:00:54 +01:00
Philipp Hagemeister
235b3ba479 Move code into a separate Python module 2011-11-21 19:59:59 +01:00
Philipp Hagemeister
5b3330e0cf Remove empty real_initialize defs 2011-11-21 19:31:20 +01:00
Philipp Hagemeister
aab771fbdf Credit authors of Soundclound and InfoQ extractors 2011-11-16 09:33:03 +01:00
Philipp Hagemeister
00f95a93f5 InfoQ IE (Closes #216) 2011-11-15 23:00:31 +01:00
Philipp Hagemeister
1724e7c461 Merge remote-tracking branch 'ngokevin/soundcloud' 2011-11-15 22:37:49 +01:00
Ori Avtalion
3b98a5ddac InfoQ IE 2011-11-15 23:20:29 +02:00
Philipp Hagemeister
8b59cc93d5 Merge pull request #211 from techtonik/patch-1
Fix duplicated downloads from YouTube user page where watch URLs are not. Thanks to anatoly techtonik.
2011-11-15 01:39:17 -08:00
Philipp Hagemeister
c3e4e7c182 Fix youtube playlist IE match (Closes: #210) 2011-11-15 10:35:59 +01:00
Kevin Ngo
38348005b3 removed weird indent 2011-11-12 17:28:26 -08:00
Kevin Ngo
208c4b9128 added whitespace below soundcloudIE class 2011-11-12 17:10:21 -08:00
Kevin Ngo
ec574c2c41 extracts full title from source 2011-11-12 17:08:40 -08:00
Kevin Ngo
871be928a8 now downloads soundcloud songs, need to polish title grabbing and file naming 2011-11-12 16:48:43 -08:00
Kevin Ngo
b20d4f8626 changed spaces to tabs (by yt-dl standards), fixed bugs, but still won't download. need to figure out how the whole process works to integrate correctly 2011-11-10 01:04:33 -08:00
Kevin Ngo
073d7a5985 extracted all of the soundcloud information including description (not tested), need to hook into filedownloader 2011-11-09 01:52:36 -08:00
Kevin Ngo
40306424b1 work on soundcloud information extractor...need to talk to youtube-dl guys 2011-11-08 00:03:35 -08:00
Kevin Ngo
ecb3bfe543 going home and need to upload what little i did 2011-11-07 18:02:10 -08:00
anatoly techtonik
abeac45abe Fix duplicated downloads from YouTube user page where watch URLs are not always end with &. Stop scan on closing bracker prevents regexp to capture two links instead of one. 2011-11-06 16:42:43 +03:00
Philipp Hagemeister
0fca93ac60 Merge pull request #206 from rbrito/fixes/facebook-ie-2
FacebookIE: Fix regexp to recognize videos that weren't considered.
2011-11-02 10:56:18 -07:00
Rogério Brito
857e5f329a FacebookIE: Fix regexp to recognize videos that weren't considered. 2011-11-01 12:07:05 -02:00
Rogério Brito
053419cd24 FacebookIE: The date doesn't seem to be available anymore.
The current regular expression is likely to match a lot of stuff, as each
comment that a video has has one of those and it is not clear which one is
the date of the video *upload* itself.
2011-10-20 20:28:34 -02:00
Rogério Brito
99e207bab0 FacebookIE: Fix extraction of title as Facebook has changed stuff. 2011-10-20 20:27:48 -02:00
Rogério Brito
0067bbe7a7 FacebookIE: Not all videos are available in all formats. 2011-10-20 20:26:42 -02:00
Philipp Hagemeister
45aa690868 Release 2011.10.19 2011-10-19 00:40:13 +02:00
Philipp Hagemeister
beb245e92f Merge branch 'vimeo' of https://github.com/rbrito/youtube-dl 2011-10-19 00:38:41 +02:00
Rogério Brito
c424df0d2f vimeo: Add the ability to detect if a video is available in HD. (Closes: #194) 2011-10-19 00:37:45 +02:00
Rogério Brito
87929e4b35 vimeo: Add the ability to detect if a video is available in HD. 2011-10-18 19:47:19 -02:00
Philipp Hagemeister
d76736fc5e Merge pull request #195 from rbrito/xvideos
Fixes for the xvideos IE
2011-10-18 13:49:21 -07:00
Rogério Brito
0f9b77223e xvideos: Capture only the video title, not the name of the site. 2011-10-18 18:42:01 -02:00
Rogério Brito
9f47175a40 xvideos: Fix misleading error message when extracting the URL.
We said that we were trying to extract the title of the video.
2011-10-18 18:41:02 -02:00
Rogério Brito
a1a8713aad xvideos: Normalize the URL or it will fail with some inputs.
For instance, if we give it <www.xvideos.com/video1384059>, we would
end up passing that to urllib2, which would complain about an unknown
URL type.
2011-10-18 18:38:17 -02:00
Rogério Brito
6501a06d46 Quick and dirty IE for xvideos.com. 2011-10-13 16:44:20 -03:00
Philipp Hagemeister
8d89fbae5a CollegeHumor IE 2011-10-12 21:13:43 +02:00
Philipp Hagemeister
7a2cf5455c Fix recognition of http://www.youtube.com/course?list=PL41FDABC6AA085E78&category=University/Mathematics/Topology%20%26%20Geometry 2011-10-04 03:25:20 +02:00
Rogério Brito
7125a7ca8b Support "newstyle" Youtube playlist IDs.
Many playlists reported by Youtube now have the form like in:

    http://www.youtube.com/playlist?list=PL520044A3524F5E5D

where `PL` is prefixed to what youtube-dl used to use as playlist IDs. So,
while matching it, we adapt the regular expression so as to discard the `PL`
and only get the `520044A3524F5E5D` in the case of the playlist of the
example.
2011-10-03 21:41:33 -03:00
Philipp Hagemeister
54d47874f7 release 2011.09.30 2011-09-30 09:07:59 +02:00
Philipp Hagemeister
2761012f69 Cosmetic changes to --list-formats 2011-09-30 09:07:36 +02:00
Francois du Toit
3de2a1e635 Added option -L to list available formats 2011-09-28 01:28:37 +02:00
Philipp Hagemeister
1eff9ac0c5 Release 2011.09.27 2011-09-27 21:42:52 +02:00
Philipp Hagemeister
54f329fe93 blip.tv: Handle direct URLs (Thanks to Bahman) 2011-09-27 21:42:15 +02:00
Philipp Hagemeister
9baa2ef53b Update REAMDE.md 2011-09-26 20:29:15 +02:00
Philipp Hagemeister
6bde5972c3 Mention ext in output template help 2011-09-26 20:26:40 +02:00
Philipp Hagemeister
36f6cb369b Add help to output template 2011-09-26 20:24:43 +02:00
Osama Khalid
b845d58b04 Adding HTTPS support for YouTube playlists and users; Escaping dots. 2011-09-25 21:28:20 +03:00
Philipp Hagemeister
efb113c736 Simplify test 2011-09-21 18:49:08 +02:00
Philipp Hagemeister
3ce59dae88 Update README with new options 2011-09-21 18:48:51 +02:00
Philipp Hagemeister
f0b0caa3fa Merge pull request #172 from richardc/master
add support for vorbis to  --extract-audio
2011-09-21 09:47:11 -07:00
Richard Clamp
58384838c3 Add support for vorbis files to --extract-audio
Add Ogg Vorbis as a file type when extracting the audio from a
file.  This can be the 'best' codec if the source clip is a webm
container.
2011-09-21 17:29:25 +01:00
Philipp Hagemeister
abb870d1ad Clarify --cookies option 2011-09-18 18:50:23 +02:00
Philipp Hagemeister
daa982bc01 release 2011.09.18c: Prefer mp4 over webm 2011-09-17 00:58:44 +02:00
Philipp Hagemeister
767414a292 Prefer format 18 over 43 2011-09-17 00:58:14 +02:00
Philipp Hagemeister
7b417b388a Add youtube format 44 2011-09-17 00:51:25 +02:00
Philipp Hagemeister
44424ceee9 Prefer mp4 over webm - seems to work better for most users 2011-09-17 00:39:51 +02:00
Philipp Hagemeister
08a5b7f800 Release 2011.09.18b 2011-09-16 22:33:08 +02:00
Philipp Hagemeister
1cde6f1d52 Prevent youtube IE from taking youtube playlists 2011-09-16 22:31:31 +02:00
Philipp Hagemeister
2d8acd8039 Fix escapist URL match 2011-09-15 20:25:22 +02:00
Philipp Hagemeister
67035ede49 Fix progress message when Content-Length is not set 2011-09-15 20:24:21 +02:00
Philipp Hagemeister
eb6c37da43 Clarified "restart" 2011-09-15 20:10:27 +02:00
Philipp Hagemeister
2736595628 Do not update if already up-to-date (Closes #166) 2011-09-15 20:09:30 +02:00
Philipp Hagemeister
7b1a2bbe17 release 2011.09.18 2011-09-15 19:29:16 +02:00
Philipp Hagemeister
c25303c3d5 Set continue to false again; we need to send to actually send a HEAD request to determine whether we can continue or not 2011-09-15 19:27:21 +02:00
Philipp Hagemeister
cc025e1226 release 2011.09.17 2011-09-15 19:23:52 +02:00
Philipp Hagemeister
eca1b76f01 Update README 2011-09-15 19:23:17 +02:00
Philipp Hagemeister
366cbfb04a Fix _do_download signature 2011-09-15 19:22:18 +02:00
Philipp Hagemeister
18bb3d1e35 Make --continue the default and provide --no-continue (Closes #119) 2011-09-15 19:12:04 +02:00
Michael Haggerty
10e7194db1 If --continue is not enabled, set resume_len to zero.
This corrects the reporting of download progress (which previously
started at a value greater than zero).
2011-09-15 19:07:53 +02:00
Philipp Hagemeister
ef357c4bf2 Bump version number 2011-09-15 18:48:29 +02:00
Philipp Hagemeister
5260e68f64 Add format fallback 2011-09-15 18:47:36 +02:00
Philipp Hagemeister
6a1ca41e17 Update README 2011-09-15 12:45:56 +02:00
Philipp Hagemeister
c99dcbd2d6 Merge remote-tracking branch 'rmanola/master' (Closes: #124)
Add option to specify mp3 quality and prevent the video from being deleted
2011-09-15 12:43:27 +02:00
Dominik Heidler
da0db53a75 added option to get the available formats for a video (Closes #106) 2011-09-15 12:20:03 +02:00
Kegan
c52b01f326 Added ability to download worst quality video file only. (Closes #113) 2011-09-15 12:14:16 +02:00
Kegan
36597dc40f Updated to stamp extracted audio file with HTTP last modified date. 2011-09-15 12:04:54 +02:00
Philipp Hagemeister
9b4556c469 New option --skip-download (Closes #162) 2011-09-15 11:36:49 +02:00
Philipp Hagemeister
f3098c4d8a --list-extractors (Closes #161) 2011-09-15 11:03:29 +02:00
Philipp Hagemeister
bdb3f7a769 Simplify suitable 2011-09-15 10:06:14 +02:00
Philipp Hagemeister
afb5b55de6 Proper warning if xml.etree.ElementTree is not available 2011-09-15 09:59:03 +02:00
Philipp Hagemeister
c23cec29a3 Update LATEST_VERSION (and wait for a script to do it so I do not forget ;) ) 2011-09-14 23:03:01 +02:00
Philipp Hagemeister
e5b9fac281 Bump version number 2011-09-14 22:55:26 +02:00
Philipp Hagemeister
08c1d0d3bc Update README 2011-09-14 22:55:09 +02:00
Anand Babu Periasamy
20e91e8375 Add --match-title and --reject-title (Closes #132) 2011-09-14 22:54:51 +02:00
Philipp Hagemeister
f9c6878714 Support for The Escapist 2011-09-14 22:26:53 +02:00
Philipp Hagemeister
8c5dc3ad40 Simplify IE index 2011-09-14 21:39:41 +02:00
Philipp Hagemeister
1d2e86aed9 Decapitalize options in README for consistency with youtube-dl --help 2011-09-14 21:20:23 +02:00
Philipp Hagemeister
a2f7e3a5bb Clarify usage 2011-09-14 21:19:33 +02:00
Philipp Hagemeister
f2a3a3522c typo in README 2011-09-14 21:18:22 +02:00
Philipp Hagemeister
b487ef0833 Fully implement comedycentral downloader 2011-09-14 21:17:05 +02:00
Philipp Hagemeister
d0922f29a3 Update LATEST_VERSION (oops) 2011-09-14 00:04:46 +02:00
Philipp Hagemeister
b90bcbe79e Bump version number 2011-09-13 23:58:46 +02:00
Philipp Hagemeister
8236e85178 s#phihag#rg3 2011-09-13 23:58:31 +02:00
Philipp Hagemeister
803abae206 Do not claim copyright in README (Closes #157) 2011-09-13 23:54:50 +02:00
Philipp Hagemeister
50bdd8a9e7 Merge remote-tracking branch 'knagano/master' 2011-09-13 23:50:44 +02:00
Philipp Hagemeister
34554a7ad4 Merge remote-tracking branch 'CaptainPatate/master' 2011-09-13 23:35:48 +02:00
Philipp Hagemeister
93e1659586 Bump version number (remove -phihag) 2011-09-13 22:39:20 +02:00
Philipp Hagemeister
b576abb457 Automatically generate LATEST_VERSION (Closes #16) 2011-09-13 22:29:50 +02:00
Philipp Hagemeister
f166bccc8f Allow downloading current thedailyshow episode with youtube-dl :tds 2011-09-13 21:51:44 +02:00
Philipp Hagemeister
5a2ba45e09 Clarify README 2011-09-13 21:51:06 +02:00
Philipp Hagemeister
e133e1213f README: More bug filing instructions 2011-09-09 08:47:00 +02:00
Philipp Hagemeister
454d6691d8 Include ERROR: no fmt_url_map or conn information found in video info in FAQ 2011-09-09 08:41:52 +02:00
Philipp Hagemeister
d793aebaed comedycentral: 1 seems to be the constant correct offset 2011-09-09 08:14:01 +02:00
Philipp Hagemeister
5991ddfd7a comedycentral: Use media number instead of act number as ID 2011-09-08 18:49:28 +02:00
Philipp Hagemeister
a88bc6bbd3 Temporarily fix dailyshow+colbertnation media IDs 2011-09-07 23:15:26 +02:00
Philipp Hagemeister
46c8c43266 Switch around act and episode title (makes -t nicer) 2011-09-07 22:42:33 +02:00
Philipp Hagemeister
fedf9f3902 Basic comedycentral (The Daily Show) support (Will work as soon as rtmpdump gets fixed) 2011-09-07 22:06:09 +02:00
Philipp Hagemeister
0f862ea18c comedycentral: include player URL (still broken) 2011-09-07 21:43:19 +02:00
Philipp Hagemeister
c8e30044b8 Rudimentary support for comedycentral (rtmpdump currently broken) 2011-09-07 21:36:06 +02:00
Philipp Hagemeister
cec3a53cbd Do not try to re-encode unicode filenames (Closes #13) 2011-09-07 09:35:22 +02:00
Philipp Hagemeister
6fc5b0bb17 Credit Sören Schulze for myvideo support 2011-09-06 23:58:00 +02:00
Philipp Hagemeister
9b0a8bc198 myvideo.de support 2011-09-06 23:56:32 +02:00
Philipp Hagemeister
e5e74ffb97 Fix os.makedirs in Windows 2011-09-06 17:56:05 +02:00
Philipp Hagemeister
eb99a7ee5f Bump version to 2011.09.06 2011-09-06 17:42:45 +02:00
Philipp Hagemeister
50891fece7 Use os.makedirs instead of homebrewn pmkdir 2011-09-06 17:32:22 +02:00
Philipp Hagemeister
ef53099e35 merged pep8_whitespace 2011-09-06 17:19:23 +02:00
FND
c0a10ca8dc fixed PEP8 whitespace issues
mostly vertical whitespace and mixed spaces and tabs
2011-09-05 09:46:56 +02:00
Philipp Hagemeister
8f88eb1fa7 Update Makefile to new README format 2011-09-04 11:47:58 +02:00
Philipp Hagemeister
447b1d7170 Added FAQ to README 2011-09-04 11:41:54 +02:00
Philipp Hagemeister
dbddab2799 Robust error handling in downloading code 2011-09-03 11:32:05 +02:00
Philipp Hagemeister
802622ac1c Merge remote-tracking branch 'wise86/master' 2011-08-31 21:28:44 +02:00
Philipp Hagemeister
e0e56865a0 Remove stable from help wording (There will be only one main branch for now) 2011-08-31 21:28:40 +02:00
Philipp Hagemeister
eb11aaccbb Update bug reporting to this fork, so that vimeo/blip.tv issues are reported at phihag/issues instead of rg3/issues (Closes #5) 2011-08-28 23:44:23 +02:00
Philipp Hagemeister
d207e7cf88 Update update mechanism (Closes #4) 2011-08-28 23:38:40 +02:00
Philipp Hagemeister
36cf7bccde Merge remote-tracking branch 'rbrito/prefer-webm' 2011-08-28 23:23:42 +02:00
Philipp Hagemeister
5fd5ce0838 Add default make target 2011-08-28 23:17:32 +02:00
Philipp Hagemeister
6ae796b1ee Credit Rogério Brito for Vimeo support 2011-08-28 23:17:18 +02:00
Philipp Hagemeister
9c3e23fb64 Merge Vimeo support 2011-08-28 22:57:50 +02:00
Philipp Hagemeister
5f9f2b7396 Update: Write downloaded file without modification (allows hashsums) 2011-08-28 22:10:03 +02:00
Philipp Hagemeister
4618f3da74 Makefile to recreate README 2011-08-25 00:09:28 +02:00
Philipp Hagemeister
eb0387a848 Fix stty detection 2011-08-25 00:08:59 +02:00
Philipp Hagemeister
fe6dc08b79 Merge git://github.com/dbb/youtube-dl 2011-08-24 23:34:09 +02:00
Philipp Hagemeister
4f2a5e06da Use subprocess to call stty size when COLUMNS is not set 2011-08-24 23:28:30 +02:00
Philipp Hagemeister
2c8d32de33 merge gvalkov/optparse-refactor (minus stty) 2011-08-24 23:21:55 +02:00
Philipp Hagemeister
2b70537d7b merge upstream 2011-08-24 23:04:10 +02:00
Georgi Valkov
6a4f0a114d Use stty size to find terminal width if we're on linux and COLUMNS is not exported 2011-08-23 17:03:28 +03:00
Georgi Valkov
5adcaa4385 Refactor main function 2011-08-23 16:48:08 +03:00
Georgi Valkov
51c8e53ffe Set help formatter width to terminal width (prevents wrapping) 2011-08-23 16:42:51 +03:00
Georgi Valkov
4f9f96f646 Option parsing refactoring ; Moved version string to __version__
Brings terser option formatting to youtube-dl:
  from: -u USERNAME, --username USERNAME
  to:   -u, --username USERNAME
2011-08-23 15:53:36 +03:00
Georgi Valkov
5fb3df4aff Move update_self out of __main__ for clarity 2011-08-23 15:37:35 +03:00
Georgi Valkov
7a9054ec79 Fix small indentation inconsistencies 2011-08-23 15:01:51 +03:00
Georgi Valkov
2770590d5a Use module metadata variables instead of comments 2011-08-23 14:58:22 +03:00
Georgi Valkov
e9cb9c2811 Add vim modeline 2011-08-23 14:45:26 +03:00
Philipp Hagemeister
1cab2c6dcf Fix blip.tv regular expression to not match blipXtv 2011-08-18 09:31:36 +02:00
Philipp Hagemeister
86e709d3de Fix youtu.be links (Closes #142) 2011-08-07 13:01:09 +02:00
Philipp Hagemeister
8519c32d25 Use parse_qs instead of homebrewn parsing 2011-08-07 00:29:25 +02:00
Philipp Hagemeister
f3dc18d874 youtube: Better error messages 2011-08-07 00:02:50 +02:00
Philipp Hagemeister
1293ce58ac Fix Python 2.4 compatibility 2011-08-06 12:16:07 +02:00
Philipp Hagemeister
0a3c8b6291 Use alternative imports for Python 2.4 (Closes #138) 2011-08-06 11:48:45 +02:00
Philipp Hagemeister
134cff47ab Remove debugging information 2011-08-06 11:20:28 +02:00
Philipp Hagemeister
f137bef973 Fix RTMP streams and ignore url-less entries 2011-08-06 11:05:57 +02:00
Daniel Bolton
2bf94b3116 Remove horizontal rules from README.md 2011-08-05 19:15:57 -04:00
Daniel Bolton
6bcd846b52 Add README.md (markdown file) 2011-08-05 19:14:13 -04:00
Rogério Brito
2fb47e073a Merge https://github.com/rg3/youtube-dl into vimeo 2011-08-04 17:44:49 -03:00
Rogério Brito
05b4029662 Merge https://github.com/rg3/youtube-dl into prefer-webm 2011-08-04 17:44:33 -03:00
Ricardo Garcia
33d507f1fe Bump version number 2011-08-04 19:15:14 +02:00
Ricardo Garcia
c44b9ee95e Update User-Agent string 2011-08-04 19:14:19 +02:00
Ricardo Garcia
8126094cf1 Fix YouTube downloads (code by Philipp Hagemeister) 2011-08-04 19:13:02 +02:00
Philipp Hagemeister
0ac22e4f5a Fix youtube downloads (Closes #135) 2011-08-04 00:04:55 +02:00
Philipp Hagemeister
c31b124d7a Suppport for youtube video streams (Mentioned in #108) 2011-07-31 18:09:53 +02:00
Philipp Hagemeister
47b8dab29e Removed inaccurate warning 2011-07-22 15:28:42 +02:00
Philipp Hagemeister
91e6a3855b Be lenient about download URLs (Closes #108) 2011-07-18 19:43:21 +02:00
Philipp Hagemeister
5623100e43 remove debugging code 2011-07-10 23:41:19 +02:00
Philipp Hagemeister
6eb08fbf8b + --write-info-json 2011-07-10 21:39:36 +02:00
Philipp Hagemeister
437d76c19a blip.tv support for python 2.5 with trivialjson 2011-07-10 17:31:54 +02:00
Giovanni Visentini
2152ee8601 Update youtube playlist for use playlist?list=id format 2011-07-09 14:05:36 +00:00
Philipp Hagemeister
a1cab7cead call increment_downloads in blip.tv extractor 2011-07-07 14:10:25 +02:00
Philipp Hagemeister
8b95c38707 --writedescription option 2011-07-07 12:47:36 +02:00
Philipp Hagemeister
c6b55a8d48 Full youtube video descriptions, including special characters (2.6+, with fallback for older Pythons) 2011-07-07 12:12:20 +02:00
Philipp Hagemeister
aded78d9e2 Support for blip.tv/file URLs 2011-06-25 19:26:29 +02:00
Philipp Hagemeister
7745f5d881 Basic blip.tv support 2011-06-21 22:24:58 +02:00
rmanola
18b7f87409 Added option to allow different audio encoding qualities and to allow specify whether erase or not the video when it's need to extract the audio. 2011-06-06 17:46:37 -07:00
knagano
62a29bbf7b Fixed download from Dailymotion.
Fetches FLV URL from "sdURL" in addVariable("sequence") JSON,
instead of addVariable("video") which doesnot exist today.
Supports new title, uploader nickname format.
2011-05-07 22:53:37 +09:00
Rogério Brito
2fc31a4872 vimeo: Apparently, all videos in vimeo are served in ISO containers. 2011-04-20 21:29:29 -03:00
Rogério Brito
44c636df89 vimeo: Tweak the regexp to allow some extended URLs from vimeo.
This, in particular, lets me grab the videos from the beginners channel with
URLs like:

    http://vimeo.com/groups/fivebyfive/videos/22648611

Note that the regexp *will* break for other URLs that we don't know about
and that's on purpose: we don't want to accidentally grab videos that would
be passed on to other information extractors.
2011-04-20 21:21:01 -03:00
Rogério Brito
1e055db69c vimeo: Ignore if we are using HTTP/S or not. 2011-04-20 21:15:57 -03:00
Rogério Brito
0ecedbdb03 vimeo: Remove clutter in some messages.
We should make a unified way of printing messages, but let's follow suit and
do what the main YoutubeIE does here.
2011-04-20 21:08:01 -03:00
Rogério Brito
43c0a396a2 Merge branch 'master' into prefer-webm 2011-04-20 21:01:31 -03:00
Rogério Brito
00f3977f77 Merge branch 'master' into vimeo 2011-04-20 21:00:46 -03:00
Amaury Gauthier
e26005adea Deletes duplicate entry in process_info dictionary of YahooIE 2011-04-07 23:25:47 +02:00
Ricardo Garcia
4b0d9eed45 Bump version number 2011-03-29 20:32:07 +02:00
Ricardo Garcia
3efa45c3a2 Fix upload date regexp (closes #93) 2011-03-15 20:12:10 +01:00
Ricardo Garcia
2727dbf78d Split a couple of lines to make the code more readable 2011-03-15 20:04:20 +01:00
Ricardo Garcia
e3f7e05c27 Avoid crash reported in issue #86 2011-03-15 20:03:52 +01:00
Ricardo Garcia
da54ed4412 Support youtube.com/e/ URLs (closes #88) 2011-02-28 19:37:58 +01:00
Rogério Brito
d8edbf3a93 Merge branch 'master' into vimeo 2011-02-26 10:09:36 -03:00
Rogério Brito
a62db07f58 Merge branch 'master' into prefer-webm 2011-02-26 10:09:18 -03:00
Ricardo Garcia
b58faab5e7 Bump version number 2011-02-26 00:47:29 +01:00
Rogério Brito
854cad639e Merge branch 'master' into prefer-webm 2011-02-25 20:08:33 -03:00
Rogério Brito
cb25a0e30c Merge branch 'master' into vimeo 2011-02-25 20:08:12 -03:00
Idan Kamara
377086af3d Use '--' to separate the file argument from the options when calling ffmpeg
This is to avoid a potential issue if the file name begins with a hyphen since ffmpeg will interpret it as an option
2011-02-25 23:24:58 +02:00
Ricardo Garcia
820eedcb50 Bump version number 2011-02-25 21:54:16 +01:00
Ricardo Garcia
da273188f3 Catch possible exceptions when running ffprobe 2011-02-25 21:53:26 +01:00
Idan Kamara
1bd9258272 Fix stderr print when ffmpeg fails 2011-02-25 22:30:22 +02:00
Ricardo Garcia
c076845454 Bump version number 2011-02-25 20:12:32 +01:00
Ricardo Garcia
afd233c05c Update User-Agent string 2011-02-25 20:11:53 +01:00
Ricardo Garcia
3072fab115 Add an audio extracting PostProcessor using ffmpeg (closes #2) 2011-02-25 19:06:58 +01:00
Ricardo Garcia
87cbd21323 Fix date parsing for YouTube (patch by Drake Wyrm) 2011-02-25 19:05:35 +01:00
Rogério Brito
3b84a43076 Merge branch 'prefer-webm' of ssh://github.com/rbrito/youtube-dl into prefer-webm 2011-02-21 21:59:23 -03:00
Rogério Brito
2c8bedd12c Merge branch 'master' into prefer-webm 2011-02-21 21:59:08 -03:00
Rogério Brito
1a3fe4212f Merge branch 'vimeo' of ssh://github.com/rbrito/youtube-dl into vimeo 2011-02-21 21:52:48 -03:00
Rogério Brito
c4cfbdf5a5 Merge branch 'master' into vimeo 2011-02-21 21:51:33 -03:00
Ricardo Garcia
ef9f8451c8 Add Gergely Imreh to the author list 2011-02-20 18:01:57 +01:00
Gergely Imreh
9f5f960213 Facebook info extractor
This IE should be full-featured.

Public videos can be downloaded without login, e.g:
https://www.facebook.com/video/video.php?v=696729990595

Private videos need login, and subject to login rate limit of
a couple of tries / minute.
2011-02-20 23:57:50 +08:00
Rogério Brito
a4a590b5b1 Merge branch 'master' into prefer-webm 2011-02-17 09:19:12 -02:00
Rogério Brito
7f69fd3b39 Merge branch 'master' into vimeo 2011-02-17 09:17:30 -02:00
Rogério Brito
a7e5259c33 vimeo: Make regexp more robust.
This change makes the VimeoIE work with http://player.vimeo.com/video/19267888
2011-02-17 08:25:45 -02:00
Gergely Imreh
7cc3c6fd62 Fix possible missing parameter in playlist url extraction
The "playlist_prefix" parameter was missing when parsing playlist urls
that match the recently added format, e.g.:
http://www.youtube.com/user/stanforduniversity#g/c/9D558D49CA734A02
For these URLs (basically, for every playlist type so far, except the
artist list) playlist_prefix has to be equal to "p" for correct
exctraction.
2011-02-13 19:02:56 +08:00
Ricardo Garcia
d119b54df6 Support more common YouTube playlist URLs 2011-02-12 20:19:20 +01:00
Rogério Brito
8cc98b2358 vimeo: Also accept URLs prefixed by www.
I hope that this doesn't break anything. `:)`
2011-02-04 06:15:27 -02:00
Rogério Brito
f24c674b04 Make some of the comments more descriptive. 2011-02-04 04:02:29 -02:00
Rogério Brito
58b53721af Merge branch 'master' into vimeo 2011-02-04 03:51:16 -02:00
Gergely Imreh
f74e22ae28 Enable artist playlists in YoutubePlaylistIE
Artist playlist pages have different format compared to user playlists,
thus more format checking is needed to construct the correct URL.

From the artist playlist this method downloads all listed below the
"Videos by [Artist Name]" header, plus usually there's one more
video on the side, titled "Youtube Mix for [Artist Name]", which
has a link format that currently cannot be distinguished from the other
videos in the list.
2011-01-31 19:00:51 +08:00
Ricardo Garcia
16c73c2e51 Fix SyntaxError problem (oops) 2011-01-30 13:03:15 +01:00
Ricardo Garcia
5776c3295b Update User-Agent string 2011-01-30 12:59:18 +01:00
Ricardo Garcia
9e0dd8692e Bump version number 2011-01-30 12:58:01 +01:00
Ricardo Garcia
5aba6ea4fe Add YoutubeUserIE (code courtesy of Paweł Paprota) 2011-01-29 11:55:20 +01:00
Rogério Brito
c5a088d341 Use non-greedy regexps, for safety.
Since I was very lazy when I coded this, I took the fastest route.  Luckily,
Vasyl' Vavrychuk pointed this out and I went (after many months) and just
did some minor changes.
2011-01-29 04:13:54 -02:00
Rogério Brito
92743d423a Preliminary downloading from vimeo 2011-01-29 03:59:18 -02:00
Rogério Brito
9e1ee3364a Merge branch 'master' into prefer-webm 2011-01-29 03:45:21 -02:00
Rogério Brito
e0edf1e041 Give preference to WebM formats.
This patch gives preference to formats that are Free.
2011-01-29 03:41:44 -02:00
Ricardo Garcia
6025795d95 Split some long lines 2011-01-28 19:59:47 +01:00
Ricardo Garcia
e30189021d Make the file timestamp feature optional 2011-01-28 19:59:18 +01:00
Gergely Imreh
09bd408c28 Set downloaded file's time stamp from last-modified header
This file stamp setting is very relaxed. If there's any problem
along the way (no last-modified header, bad time string format,
no time set privileges,...) or if nothing is downloaded (e.g. using
resumed download but the file was already complete) then nothing
is done.
2011-01-28 09:46:06 +08:00
Gergely Imreh
9f7963468b New option --get-filename to print output filename
When using youtube-dl within scripts, it is very useful to know
what will be the final output filename with all the title settings and
filename templates applied. Add option to the quiet mode operations
to print that info.

For this I had to move the filename-generation into its own function.
As much as I can tell it should work almost always well, ie. not to
break things if one not actually interested in the title, like in
case of other forced printing. That is, unless there's an invalid
system charset or the user specified a wrong output template. In that
case probably could be assumed that the user does have a problem
(the former) or did want to mess with the filename (the latter).

Signed-off-by: Gergely Imreh <imrehg@gmail.com>
2011-01-26 09:15:00 +08:00
Lionel Elie Mamane
b940c84a24 Support for youtube.com/embed/XXXXX URLs (closes #63) 2011-01-25 19:00:45 +01:00
Ricardo Garcia
0f7099a59b Fix omission of Witold Baryluk as the Dailymotion InfoExtractor author 2011-01-21 18:18:48 +01:00
Ricardo Garcia
c02d8e4040 Fix dailymotion support (closes #60) 2011-01-21 18:16:33 +01:00
Ricardo Garcia
0f6b00b587 Improve addinfourl_wrapper for compatibility with older Python versions 2011-01-20 20:36:42 +01:00
Ricardo Garcia
7b531c0be6 Wrap call to addinfourl for compatibility with Python 2.4 2011-01-18 20:52:37 +01:00
Ricardo Garcia
0d14e225fa Omit code argument in addinfourl for Python 2.4 2011-01-18 19:16:42 +01:00
Ricardo Garcia
0fe64c04f8 Make the self-updating function a bit more robust 2011-01-12 21:07:56 +01:00
Ricardo Garcia
0d8d9877ad Add support for embedded YouTube playist URLs (closes #54) 2011-01-12 20:22:37 +01:00
Ricardo Garcia
8cc42e7c1a Fix "unable to rename file" Windows error (closes #56) 2011-01-12 20:21:43 +01:00
Ricardo Garcia
1987c2325a Add proper support for "gzip" and "deflate" encodings 2011-01-12 20:20:37 +01:00
Ricardo Garcia
aac3fe0f4a Fix bug in regular expression for youtu.be links 2011-01-10 12:54:14 +01:00
Ricardo Garcia
3fb2c487c0 Add --no-part option (closes #48) 2011-01-07 10:23:18 +01:00
Ricardo Garcia
d3975459d1 Remove trailing whitespace 2011-01-07 10:22:01 +01:00
Mantas Mikulėnas
ccbd296bee Added --console-title to display download progress in console window title.
This uses SetConsoleTitle() Win32 API for Windows Console, and Xterm escape sequence otherwise.
2011-01-04 17:32:35 +02:00
Ricardo Garcia
e7cf18cb6b Add --dump-user-agent option (patch provided by Benjamin Johnson) 2011-01-03 11:47:23 +01:00
Ricardo Garcia
09cc744c90 Print new line before a few error messages 2011-01-03 11:22:49 +01:00
Ricardo Garcia
a57ed21f6d Request page compression by default, like Firefox does 2010-12-16 07:09:58 +01:00
Ricardo Garcia
975a91d0ac Take into account resume_len when calculating speed and ETA 2010-12-15 21:42:11 +01:00
Ricardo Garcia
b905e5f583 Fix erroneous "content too short" error message 2010-12-12 19:21:09 +01:00
Ricardo Garcia
ef4f4544a2 Remove deprecated -b option and nonworking -m option (closes #39) 2010-12-11 11:35:45 +01:00
Ricardo Garcia
5c1327931a Stop attempting to use get_video and detect missing formats ourselves 2010-12-11 11:34:10 +01:00
Ricardo Garcia
106d091e80 Do not use 0% as the starting point in resumed downloads (closes #40) 2010-12-11 11:32:33 +01:00
Ricardo Garcia
f83ae7816b Fix problem when requesting an existing format explicitly with -f 2010-12-09 19:57:39 +01:00
Ricardo Garcia
f148ea4473 Bump version number 2010-12-09 19:37:07 +01:00
Ricardo Garcia
7d950ca1d6 Improve temporary filename handling of special cases 2010-12-09 19:33:04 +01:00
Ricardo Garcia
d157d2597a Fix YoutubeIE after recent YouTube changes (closes #34) 2010-12-09 19:22:32 +01:00
Ricardo Garcia
e567ef93d8 Add Vasyl' Vavrychuk to the list of authors 2010-12-08 11:04:22 +01:00
Vasyl' Vavrychuk
27179cfdba Implemented depositfiles.com support 2010-12-08 11:18:33 +02:00
Vasyl' Vavrychuk
6f0ff3bab9 Fixed failure of os.rename after receiving file finished due to file not being closed.
Following error happen while at the end of _do_download called try_rename
WindowsError: [Error 32] The process cannot access the file because it is being used by another process
2010-12-05 20:57:46 +02:00
Vasyl' Vavrychuk
a9806fd83d report_extraction was never called for GenericIE 2010-12-05 20:56:27 +02:00
Ricardo Garcia
62cf7aaf9a Use a temporary filename to download files 2010-12-04 10:38:53 +01:00
Nevar Angelo
a1f03c7b06 Reworked 'upload_date' code for output sequence in YoutubeIE.
Reverted to previous version of 'upload_date' and fixed
a mistake that prevented the code from working properly.
2010-11-30 18:51:00 +02:00
Ricardo Garcia
f8dc441430 Bump version number 2010-11-19 19:41:09 +01:00
Ricardo Garcia
010ebaf783 Update User-Agent string 2010-11-19 19:40:18 +01:00
Ricardo Garcia
138b11f36e Rework upload date mechanism after detecting problems in several tests 2010-11-19 19:31:26 +01:00
Ricardo Garcia
05df0c1d4a Restore file permissions 2010-11-19 18:37:07 +01:00
Ricardo Garcia
b04bb07c94 Merge branch 'master' of https://github.com/psi-neamf/youtube-dl into psi-neamf 2010-11-19 18:30:58 +01:00
jamiejones
b620a5f811 Correctly parse the player URL in RTMP downloads (closes #11)
Fixed several problems courtesy of jamiejones:

The parsing for the SWF url was wrong (the "//" are now escaped and the
initial .*match needs to be 'ungreedy'), so the -W setting to rtmpdump
was not set, causing the decryption of the video to be wrong.

Finally, add "&amp;has_verified=1" to the fetch of the HMTL page to
allow fetching of age-restricted videos.
2010-11-17 20:43:27 +01:00
Nevar Angelo
b3a27b5217 Added 'uploaddate' output sequence for YoutubeIE. 2010-11-17 20:55:30 +02:00
Ricardo Garcia
5e596cac0a Minor help text correction 2010-11-06 22:13:59 +01:00
Nevar Angelo
1e47d226e1 Added command line switch -A --auto-number
Numbering downloaded URLs was implemented with %(ord)s in the
output template. It has been replaced with the %(autonumber)s
sequence and is now also available as a command line switch.
2010-11-06 22:34:22 +02:00
Nevar Angelo
817e8f523f Allow comments in batch file. 2010-11-06 22:21:45 +02:00
Ricardo Garcia
8cc4434116 Add playlist-end option (courtesy of Nevar Angelo) 2010-11-04 23:19:09 +01:00
Ricardo Garcia
893a13df55 Modify autoupdate URLs to match the ones from github.com 2010-10-31 15:46:58 +01:00
36 changed files with 6902 additions and 2323 deletions

15
.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
*.pyc
*.pyo
*~
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

13
.travis.yml Normal file
View File

@@ -0,0 +1,13 @@
language: python
python:
- "2.6"
- "2.7"
# - "3.3"
script: nosetests test --verbose
notifications:
email:
- filippo.valsorda@gmail.com
irc:
channels:
- "irc.freenode.org#youtube-dl"
skip_join: true

View File

@@ -1 +1 @@
2010.10.24
9999.99.99

3
MANIFEST.in Normal file
View File

@@ -0,0 +1,3 @@
include README.md
include test/*.py
include test/*.json

48
Makefile Normal file
View File

@@ -0,0 +1,48 @@
all: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-completion
clean:
rm -rf youtube-dl youtube-dl.exe youtube-dl.1 youtube-dl.bash-completion README.txt MANIFEST build/ dist/
PREFIX=/usr/local
BINDIR=$(PREFIX)/bin
MANDIR=$(PREFIX)/man
SYSCONFDIR=/etc
install: youtube-dl youtube-dl.1 youtube-dl.bash-completion
install -d $(DESTDIR)$(BINDIR)
install -m 755 youtube-dl $(DESTDIR)$(BINDIR)
install -d $(DESTDIR)$(MANDIR)/man1
install -m 644 youtube-dl.1 $(DESTDIR)$(MANDIR)/man1
install -d $(DESTDIR)$(SYSCONFDIR)/bash_completion.d
install -m 644 youtube-dl.bash-completion $(DESTDIR)$(SYSCONFDIR)/bash_completion.d/youtube-dl
test:
nosetests2 --nocapture test
.PHONY: all clean install test
youtube-dl: youtube_dl/*.py
zip --quiet youtube-dl youtube_dl/*.py
zip --quiet --junk-paths youtube-dl youtube_dl/__main__.py
echo '#!/usr/bin/env python' > youtube-dl
cat youtube-dl.zip >> youtube-dl
rm youtube-dl.zip
chmod a+x youtube-dl
README.md: youtube_dl/*.py
COLUMNS=80 python -m youtube_dl --help | python devscripts/make_readme.py
README.txt: README.md
pandoc -f markdown -t plain README.md -o README.txt
youtube-dl.1: README.md
pandoc -s -f markdown -t man README.md -o youtube-dl.1
youtube-dl.bash-completion: youtube_dl/*.py devscripts/bash-completion.template
python devscripts/bash-completion.py
youtube-dl.tar.gz: all
tar -czf youtube-dl.tar.gz -s "|^./|./youtube-dl/|" \
--exclude="*.pyc" --exclude="*.pyo" --exclude="*~" --exclude="youtube-dl.exe" \
--exclude="wine-py2exe/" --exclude="py2exe.log" --exclude="*.kate-swp" \
--exclude="build/" --exclude="dist/" --exclude="MANIFEST" --exclude=".git/" .

201
README.md Normal file
View File

@@ -0,0 +1,201 @@
% YOUTUBE-DL(1)
# NAME
youtube-dl
# SYNOPSIS
**youtube-dl** [OPTIONS] URL [URL...]
# DESCRIPTION
**youtube-dl** is a small command-line program to download videos from
YouTube.com and a few more sites. It requires the Python interpreter, version
2.x (x being at least 6), 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.
# OPTIONS
-h, --help print this help text and exit
--version print program version and exit
-U, --update update this program to latest version
-i, --ignore-errors continue on download errors
-r, --rate-limit LIMIT download rate limit (e.g. 50k or 44.6m)
-R, --retries RETRIES number of retries (default is 10)
--buffer-size SIZE size of download buffer (e.g. 1024 or 16k) (default
is 1024)
--no-resize-buffer do not automatically adjust the buffer size. By
default, the buffer size is automatically resized
from an initial value of SIZE.
--dump-user-agent display the current browser identification
--user-agent UA specify a custom user agent
--list-extractors List all supported extractors and the URLs they
would handle
## Video Selection:
--playlist-start NUMBER playlist video to start at (default is 1)
--playlist-end NUMBER playlist video to end at (default is last)
--match-title REGEX download only matching titles (regex or caseless
sub-string)
--reject-title REGEX skip download for matching titles (regex or
caseless sub-string)
--max-downloads NUMBER Abort after downloading NUMBER files
## Filesystem Options:
-t, --title use title in file name
--id use video ID in file name
-l, --literal [deprecated] alias of --title
-A, --auto-number number downloaded files starting from 00000
-o, --output TEMPLATE output filename template. Use %(title)s to get the
title, %(uploader)s for the uploader name,
%(autonumber)s to get an automatically incremented
number, %(ext)s for the filename extension,
%(upload_date)s for the upload date (YYYYMMDD),
%(extractor)s for the provider (youtube, metacafe,
etc), %(id)s for the video id and %% for a literal
percent. Use - to output to stdout. Can also be
used to download to a different directory, for
example with -o '/my/downloads/%(uploader)s/%(title
)s-%(id)s.%(ext)s' .
--restrict-filenames Restrict filenames to only ASCII characters, and
avoid "&" and spaces in filenames
-a, --batch-file FILE file containing URLs to download ('-' for stdin)
-w, --no-overwrites do not overwrite files
-c, --continue resume partially downloaded files
--no-continue do not resume partially downloaded files (restart
from beginning)
--cookies FILE file to read cookies from and dump cookie jar in
--no-part do not use .part files
--no-mtime do not use the Last-modified header to set the file
modification time
--write-description write video description to a .description file
--write-info-json write video metadata to a .info.json file
## Verbosity / Simulation Options:
-q, --quiet activates quiet mode
-s, --simulate do not download the video and do not write anything
to disk
--skip-download do not download the video
-g, --get-url simulate, quiet but print URL
-e, --get-title simulate, quiet but print title
--get-thumbnail simulate, quiet but print thumbnail URL
--get-description simulate, quiet but print video description
--get-filename simulate, quiet but print output filename
--get-format simulate, quiet but print output format
--no-progress do not print progress bar
--console-title display progress in console titlebar
-v, --verbose print various debugging information
## Video Format Options:
-f, --format FORMAT video format code
--all-formats download all available video formats
--prefer-free-formats prefer free video formats unless a specific one is
requested
--max-quality FORMAT highest quality format to download
-F, --list-formats list all available formats (currently youtube only)
--write-srt write video closed captions to a .srt file
(currently youtube only)
--srt-lang LANG language of the closed captions to download
(optional) use IETF language tags like 'en'
## Authentication Options:
-u, --username USERNAME account username
-p, --password PASSWORD account password
-n, --netrc use .netrc authentication data
## Post-processing Options:
-x, --extract-audio convert video files to audio-only files (requires
ffmpeg or avconv and ffprobe or avprobe)
--audio-format FORMAT "best", "aac", "vorbis", "mp3", "m4a", or "wav";
best by default
--audio-quality QUALITY ffmpeg/avconv audio quality specification, insert a
value between 0 (better) and 9 (worse) for VBR or a
specific bitrate like 128K (default 5)
-k, --keep-video keeps the video file on disk after the post-
processing; the video is erased by default
# CONFIGURATION
You can configure youtube-dl by placing default arguments (such as `--extract-audio --no-mtime` to always extract the audio and not copy the mtime) into `/etc/youtube-dl.conf` and/or `~/.local/config/youtube-dl.conf`.
# OUTPUT TEMPLATE
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 `youtube-dl -o funny_video.flv "http://some/video"`. However, it may contain special sequences that will be replaced when downloading each video. The special sequences have the format `%(NAME)s`. To clarify, that is a percent symbol followed by a name in parenthesis, followed by a lowercase S. Allowed names are:
- `id`: The sequence will be replaced by the video identifier.
- `url`: The sequence will be replaced by the video URL.
- `uploader`: The sequence will be replaced by the nickname of the person who uploaded the video.
- `upload_date`: The sequence will be replaced by the upload date in YYYYMMDD format.
- `title`: The sequence will be replaced by the video title.
- `ext`: The sequence will be replaced by the appropriate extension (like flv or mp4).
- `epoch`: The sequence will be replaced by the Unix epoch when creating the file.
- `autonumber`: The sequence will be replaced by a five-digit number that will be increased with each download, starting at zero.
The current default template is `%(id)s.%(ext)s`, but that will be switchted to `%(title)s-%(id)s.%(ext)s` (which can be requested with `-t` at the moment).
In some cases, you don't want special characters such as 中, spaces, or &, such as when transferring the downloaded filename to a Windows system or the filename through an 8bit-unsafe channel. In these cases, add the `--restrict-filenames` flag to get a shorter title:
$ youtube-dl --get-filename -o "%(title)s.%(ext)s" BaW_jenozKc
youtube-dl test video ''_ä↭𝕐.mp4 # All kinds of weird characters
$ youtube-dl --get-filename -o "%(title)s.%(ext)s" BaW_jenozKc --restrict-filenames
youtube-dl_test_video_.mp4 # A simple file name
# FAQ
### Can you please put the -b option back?
Most people asking this question are not aware that youtube-dl now defaults to downloading the highest available quality as reported by YouTube, which will be 1080p or 720p in some cases, so you no longer need the -b option. For some specific videos, maybe YouTube does not report them to be available in a specific high quality format you''re interested in. In that case, simply request it with the -f option and youtube-dl will try to download it.
### I get HTTP error 402 when trying to download a video. What's this?
Apparently YouTube requires you to pass a CAPTCHA test if you download too much. We''re [considering to provide a way to let you solve the CAPTCHA](https://github.com/rg3/youtube-dl/issues/154), but at the moment, your best course of action is pointing a webbrowser to the youtube URL, solving the CAPTCHA, and restart youtube-dl.
### I have downloaded a video but how can I play it?
Once the video is fully downloaded, use any video player, such as [vlc](http://www.videolan.org) or [mplayer](http://www.mplayerhq.hu/).
### The links provided by youtube-dl -g are not working anymore
The URLs youtube-dl outputs require the downloader to have the correct cookies. Use the `--cookies` option to write the required cookies into a file, and advise your downloader to read cookies from that file. Some sites also require a common user agent to be used, use `--dump-user-agent` to see the one in use by youtube-dl.
### ERROR: no fmt_url_map or conn information found in video info
youtube has switched to a new video info format in July 2011 which is not supported by old versions of youtube-dl. You can update youtube-dl with `sudo youtube-dl --update`.
### ERROR: unable to download video ###
youtube requires an additional signature since September 2012 which is not supported by old versions of youtube-dl. You can update youtube-dl with `sudo youtube-dl --update`.
### SyntaxError: Non-ASCII character ###
The error
File "youtube-dl", line 2
SyntaxError: Non-ASCII character '\x93' ...
means you're using an outdated version of Python. Please update to Python 2.6 or 2.7.
### What is this binary file? Where has the code gone?
Since June 2012 (#342) youtube-dl is packed as an executable zipfile, simply unzip it (might need renaming to `youtube-dl.zip` first on some systems) or clone the git repository, as laid out above. If you modify the code, you can run it by executing the `__main__.py` file. To recompile the executable, run `make youtube-dl`.
### The exe throws a *Runtime error from Visual C++*
To run the exe you need to install first the [Microsoft Visual C++ 2008 Redistributable Package](http://www.microsoft.com/en-us/download/details.aspx?id=29).
# COPYRIGHT
youtube-dl is released into the public domain by the copyright holders.
This README file was originally written by Daniel Bolton (<https://github.com/dbbolton>) and is likewise released into the public domain.
# BUGS
Bugs and suggestions should be reported at: <https://github.com/rg3/youtube-dl/issues>
Please include:
* Your exact command line, like `youtube-dl -t "http://www.youtube.com/watch?v=uHlDtZ6Oc3s&feature=channel_video_title"`. A common mistake is not to escape the `&`. Putting URLs in quotes should solve this problem.
* The output of `youtube-dl --version`
* The output of `python --version`
* The name and version of your Operating System ("Ubuntu 11.04 x64" or "Windows 7 x64" is usually enough).
For discussions, join us in the irc channel #youtube-dl on freenode.

6
bin/youtube-dl Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
import youtube_dl
if __name__ == '__main__':
youtube_dl.main()

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env python
import os
from os.path import dirname as dirn
import sys
sys.path.append(dirn(dirn((os.path.abspath(__file__)))))
import youtube_dl
BASH_COMPLETION_FILE = "youtube-dl.bash-completion"
BASH_COMPLETION_TEMPLATE = "devscripts/bash-completion.template"
def build_completion(opt_parser):
opts_flag = []
for group in opt_parser.option_groups:
for option in group.option_list:
#for every long flag
opts_flag.append(option.get_opt_string())
with open(BASH_COMPLETION_TEMPLATE) as f:
template = f.read()
with open(BASH_COMPLETION_FILE, "w") as f:
#just using the special char
filled_template = template.replace("{{flags}}", " ".join(opts_flag))
f.write(filled_template)
parser = youtube_dl.parseOpts()[0]
build_completion(parser)

View File

@@ -0,0 +1,14 @@
__youtube-dl()
{
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
opts="{{flags}}"
if [[ ${cur} == * ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
}
complete -F __youtube-dl youtube-dl

20
devscripts/make_readme.py Normal file
View File

@@ -0,0 +1,20 @@
import sys
import re
README_FILE = 'README.md'
helptext = sys.stdin.read()
with open(README_FILE) as f:
oldreadme = f.read()
header = oldreadme[:oldreadme.index('# OPTIONS')]
footer = oldreadme[oldreadme.index('# CONFIGURATION'):]
options = helptext[helptext.index(' General Options:')+19:]
options = re.sub(r'^ (\w.+)$', r'## \1', options, flags=re.M)
options = '# OPTIONS\n' + options + '\n'
with open(README_FILE, 'w') as f:
f.write(header)
f.write(options)
f.write(footer)

6
devscripts/posix-locale.sh Executable file
View File

@@ -0,0 +1,6 @@
# source this file in your shell to get a POSIX locale (which will break many programs, but that's kind of the point)
export LC_ALL=POSIX
export LANG=POSIX
export LANGUAGE=POSIX

11
devscripts/release.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
if [ -z "$1" ]; then echo "ERROR: specify version number like this: $0 1994.09.06"; exit 1; fi
version="$1"
if [ ! -z "`git tag | grep "$version"`" ]; then echo 'ERROR: version already present'; exit 1; fi
if [ ! -z "`git status --porcelain`" ]; then echo 'ERROR: the working directory is not clean; commit or stash changes'; exit 1; fi
sed -i "s/__version__ = '.*'/__version__ = '$version'/" youtube_dl/__init__.py
make all
git add -A
git commit -m "release $version"
git tag -m "Release $version" "$version"

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python
import sys, os
try:
import urllib.request as compat_urllib_request
except ImportError: # Python 2
import urllib2 as compat_urllib_request
sys.stderr.write(u'Hi! We changed distribution method and now youtube-dl needs to update itself one more time.\n')
sys.stderr.write(u'This will only happen once. Simply press enter to go on. Sorry for the trouble!\n')
sys.stderr.write(u'The new location of the binaries is https://github.com/rg3/youtube-dl/downloads, not the git repository.\n\n')
try:
raw_input()
except NameError: # Python 3
input()
filename = sys.argv[0]
API_URL = "https://api.github.com/repos/rg3/youtube-dl/downloads"
BIN_URL = "https://github.com/downloads/rg3/youtube-dl/youtube-dl"
if not os.access(filename, os.W_OK):
sys.exit('ERROR: no write permissions on %s' % filename)
try:
urlh = compat_urllib_request.urlopen(BIN_URL)
newcontent = urlh.read()
urlh.close()
except (IOError, OSError) as err:
sys.exit('ERROR: unable to download latest version')
try:
with open(filename, 'wb') as outf:
outf.write(newcontent)
except (IOError, OSError) as err:
sys.exit('ERROR: unable to overwrite current version')
sys.stderr.write(u'Done! Now you can run youtube-dl.\n')

View File

@@ -0,0 +1,12 @@
from distutils.core import setup
import py2exe
py2exe_options = {
"bundle_files": 1,
"compressed": 1,
"optimize": 2,
"dist_dir": '.',
"dll_excludes": ['w9xpopen.exe']
}
setup(console=['youtube-dl.py'], options={ "py2exe": py2exe_options }, zipfile=None)

View File

@@ -0,0 +1,49 @@
#!/usr/bin/env python
import sys, os
import urllib2
sys.stderr.write(u'Hi! We changed distribution method and now youtube-dl needs to update itself one more time.\n')
sys.stderr.write(u'This will only happen once. Simply press enter to go on. Sorry for the trouble!\n')
sys.stderr.write(u'The new location of the binaries is https://github.com/rg3/youtube-dl/downloads, not the git repository.\n\n')
raw_input()
filename = sys.argv[0]
API_URL = "https://api.github.com/repos/rg3/youtube-dl/downloads"
EXE_URL = "https://github.com/downloads/rg3/youtube-dl/youtube-dl.exe"
if not os.access(filename, os.W_OK):
sys.exit('ERROR: no write permissions on %s' % filename)
exe = os.path.abspath(filename)
directory = os.path.dirname(exe)
if not os.access(directory, os.W_OK):
sys.exit('ERROR: no write permissions on %s' % directory)
try:
urlh = urllib2.urlopen(EXE_URL)
newcontent = urlh.read()
urlh.close()
with open(exe + '.new', 'wb') as outf:
outf.write(newcontent)
except (IOError, OSError) as err:
sys.exit('ERROR: unable to download latest version')
try:
bat = os.path.join(directory, 'youtube-dl-updater.bat')
b = open(bat, 'w')
b.write("""
echo Updating youtube-dl...
ping 127.0.0.1 -n 5 -w 1000 > NUL
move /Y "%s.new" "%s"
del "%s"
\n""" %(exe, exe, bat))
b.close()
os.startfile(bat)
except (IOError, OSError) as err:
sys.exit('ERROR: unable to overwrite current version')
sys.stderr.write(u'Done! Now you can run youtube-dl.\n')

56
devscripts/wine-py2exe.sh Executable file
View File

@@ -0,0 +1,56 @@
#!/bin/bash
# Run with as parameter a setup.py that works in the current directory
# e.g. no os.chdir()
# It will run twice, the first time will crash
set -e
SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
if [ ! -d wine-py2exe ]; then
sudo apt-get install wine1.3 axel bsdiff
mkdir wine-py2exe
cd wine-py2exe
export WINEPREFIX=`pwd`
axel -a "http://www.python.org/ftp/python/2.7/python-2.7.msi"
axel -a "http://downloads.sourceforge.net/project/py2exe/py2exe/0.6.9/py2exe-0.6.9.win32-py2.7.exe"
#axel -a "http://winetricks.org/winetricks"
# http://appdb.winehq.org/objectManager.php?sClass=version&iId=21957
echo "Follow python setup on screen"
wine msiexec /i python-2.7.msi
echo "Follow py2exe setup on screen"
wine py2exe-0.6.9.win32-py2.7.exe
#echo "Follow Microsoft Visual C++ 2008 Redistributable Package setup on screen"
#bash winetricks vcrun2008
rm py2exe-0.6.9.win32-py2.7.exe
rm python-2.7.msi
#rm winetricks
# http://bugs.winehq.org/show_bug.cgi?id=3591
mv drive_c/Python27/Lib/site-packages/py2exe/run.exe drive_c/Python27/Lib/site-packages/py2exe/run.exe.backup
bspatch drive_c/Python27/Lib/site-packages/py2exe/run.exe.backup drive_c/Python27/Lib/site-packages/py2exe/run.exe "$SCRIPT_DIR/SizeOfImage.patch"
mv drive_c/Python27/Lib/site-packages/py2exe/run_w.exe drive_c/Python27/Lib/site-packages/py2exe/run_w.exe.backup
bspatch drive_c/Python27/Lib/site-packages/py2exe/run_w.exe.backup drive_c/Python27/Lib/site-packages/py2exe/run_w.exe "$SCRIPT_DIR/SizeOfImage_w.patch"
cd -
else
export WINEPREFIX="$( cd wine-py2exe && pwd )"
fi
wine "C:\\Python27\\python.exe" "$1" py2exe > "py2exe.log" 2>&1 || true
echo '# Copying python27.dll' >> "py2exe.log"
cp "$WINEPREFIX/drive_c/windows/system32/python27.dll" build/bdist.win32/winexe/bundle-2.7/
wine "C:\\Python27\\python.exe" "$1" py2exe >> "py2exe.log" 2>&1

74
setup.py Normal file
View File

@@ -0,0 +1,74 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
from distutils.core import setup
import pkg_resources
import sys
try:
import py2exe
"""This will create an exe that needs Microsoft Visual C++ 2008 Redistributable Package"""
except ImportError:
if len(sys.argv) >= 2 and sys.argv[1] == 'py2exe':
print("Cannot import py2exe", file=sys.stderr)
exit(1)
py2exe_options = {
"bundle_files": 1,
"compressed": 1,
"optimize": 2,
"dist_dir": '.',
"dll_excludes": ['w9xpopen.exe']
}
py2exe_console = [{
"script": "./youtube_dl/__main__.py",
"dest_base": "youtube-dl",
}]
py2exe_params = {
'console': py2exe_console,
'options': { "py2exe": py2exe_options },
'zipfile': None
}
if len(sys.argv) >= 2 and sys.argv[1] == 'py2exe':
params = py2exe_params
else:
params = {
'scripts': ['bin/youtube-dl'],
'data_files': [('etc/bash_completion.d', ['youtube-dl.bash-completion']), # Installing system-wide would require sudo...
('share/doc/youtube_dl', ['README.txt']),
('share/man/man1/', ['youtube-dl.1'])]
}
# Get the version from youtube_dl/version.py without importing the package
exec(compile(open('youtube_dl/version.py').read(), 'youtube_dl/version.py', 'exec'))
setup(
name = 'youtube_dl',
version = __version__,
description = 'YouTube video downloader',
long_description = 'Small command-line program to download videos from YouTube.com and other video sites.',
url = 'https://github.com/rg3/youtube-dl',
author = 'Ricardo Garcia',
maintainer = 'Philipp Hagemeister',
maintainer_email = 'phihag@phihag.de',
packages = ['youtube_dl'],
# Provokes warning on most systems (why?!)
#test_suite = 'nose.collector',
#test_requires = ['nosetest'],
classifiers = [
"Topic :: Multimedia :: Video",
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"License :: Public Domain",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.3"
],
**params
)

132
test/gentests.py Executable file
View File

@@ -0,0 +1,132 @@
#!/usr/bin/env python3
import io # for python 2
import json
import os
import sys
import unittest
# Allow direct execution
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import youtube_dl.InfoExtractors
HEADER = u'''#!/usr/bin/env python
# DO NOT EDIT THIS FILE BY HAND!
# It is auto-generated from tests.json and gentests.py.
import hashlib
import io
import os
import json
import unittest
import sys
import socket
# Allow direct execution
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import youtube_dl.FileDownloader
import youtube_dl.InfoExtractors
from youtube_dl.utils import *
# General configuration (from __init__, not very elegant...)
jar = compat_cookiejar.CookieJar()
cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
proxy_handler = compat_urllib_request.ProxyHandler()
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
compat_urllib_request.install_opener(opener)
socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
class FileDownloader(youtube_dl.FileDownloader):
def __init__(self, *args, **kwargs):
youtube_dl.FileDownloader.__init__(self, *args, **kwargs)
self.to_stderr = self.to_screen
def _file_md5(fn):
with open(fn, 'rb') as f:
return hashlib.md5(f.read()).hexdigest()
try:
_skip_unless = unittest.skipUnless
except AttributeError: # Python 2.6
def _skip_unless(cond, reason='No reason given'):
def resfunc(f):
# Start the function name with test to appease nosetests-2.6
def test_wfunc(*args, **kwargs):
if cond:
return f(*args, **kwargs)
else:
print('Skipped test')
return
return test_wfunc
return resfunc
_skip = lambda *args, **kwargs: _skip_unless(False, *args, **kwargs)
class DownloadTest(unittest.TestCase):
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
def setUp(self):
# Clear old files
self.tearDown()
with io.open(self.PARAMETERS_FILE, encoding='utf-8') as pf:
self.parameters = json.load(pf)
'''
FOOTER = u'''
if __name__ == '__main__':
unittest.main()
'''
DEF_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tests.json')
TEST_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_download.py')
def gentests():
with io.open(DEF_FILE, encoding='utf-8') as deff:
defs = json.load(deff)
with io.open(TEST_FILE, 'w', encoding='utf-8') as testf:
testf.write(HEADER)
spaces = ' ' * 4
write = lambda l: testf.write(spaces + l + u'\n')
for d in defs:
name = d['name']
ie = getattr(youtube_dl.InfoExtractors, name + 'IE')
testf.write(u'\n')
write('@_skip_unless(youtube_dl.InfoExtractors.' + name + 'IE._WORKING, "IE marked as not _WORKING")')
if not d['file']:
write('@_skip("No output file specified")')
elif 'skip' in d:
write('@_skip(' + repr(d['skip']) + ')')
write('def test_' + name + '(self):')
write(' filename = ' + repr(d['file']))
write(' params = self.parameters')
for p in d.get('params', {}):
write(' params["' + p + '"] = ' + repr(d['params'][p]))
write(' fd = FileDownloader(params)')
write(' fd.add_info_extractor(youtube_dl.InfoExtractors.' + name + 'IE())')
for ien in d.get('addIEs', []):
write(' fd.add_info_extractor(youtube_dl.InfoExtractors.' + ien + 'IE())')
write(' fd.download([' + repr(d['url']) + '])')
write(' self.assertTrue(os.path.exists(filename))')
if 'md5' in d:
write(' md5_for_file = _file_md5(filename)')
write(' self.assertEqual(md5_for_file, ' + repr(d['md5']) + ')')
testf.write(u'\n\n')
write('def tearDown(self):')
for d in defs:
if d['file']:
write(' if os.path.exists(' + repr(d['file']) + '):')
write(' os.remove(' + repr(d['file']) + ')')
else:
write(' # No file specified for ' + d['name'])
testf.write(u'\n')
testf.write(FOOTER)
if __name__ == '__main__':
gentests()

40
test/parameters.json Normal file
View File

@@ -0,0 +1,40 @@
{
"consoletitle": false,
"continuedl": true,
"forcedescription": false,
"forcefilename": false,
"forceformat": false,
"forcethumbnail": false,
"forcetitle": false,
"forceurl": false,
"format": null,
"format_limit": null,
"ignoreerrors": false,
"listformats": null,
"logtostderr": false,
"matchtitle": null,
"max_downloads": null,
"nooverwrites": false,
"nopart": false,
"noprogress": false,
"outtmpl": "%(id)s.%(ext)s",
"password": null,
"playlistend": -1,
"playliststart": 1,
"prefer_free_formats": false,
"quiet": false,
"ratelimit": null,
"rejecttitle": null,
"retries": 10,
"simulate": false,
"skip_download": false,
"subtitleslang": null,
"test": true,
"updatetime": true,
"usenetrc": false,
"username": null,
"verbose": true,
"writedescription": false,
"writeinfojson": false,
"writesubtitles": false
}

198
test/test_download.py Normal file
View File

@@ -0,0 +1,198 @@
#!/usr/bin/env python
# DO NOT EDIT THIS FILE BY HAND!
# It is auto-generated from tests.json and gentests.py.
import hashlib
import io
import os
import json
import unittest
import sys
import socket
# Allow direct execution
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import youtube_dl.FileDownloader
import youtube_dl.InfoExtractors
from youtube_dl.utils import *
# General configuration (from __init__, not very elegant...)
jar = compat_cookiejar.CookieJar()
cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
proxy_handler = compat_urllib_request.ProxyHandler()
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
compat_urllib_request.install_opener(opener)
socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
class FileDownloader(youtube_dl.FileDownloader):
def __init__(self, *args, **kwargs):
youtube_dl.FileDownloader.__init__(self, *args, **kwargs)
self.to_stderr = self.to_screen
def _file_md5(fn):
with open(fn, 'rb') as f:
return hashlib.md5(f.read()).hexdigest()
try:
_skip_unless = unittest.skipUnless
except AttributeError: # Python 2.6
def _skip_unless(cond, reason='No reason given'):
def resfunc(f):
# Start the function name with test to appease nosetests-2.6
def test_wfunc(*args, **kwargs):
if cond:
return f(*args, **kwargs)
else:
print('Skipped test')
return
return test_wfunc
return resfunc
_skip = lambda *args, **kwargs: _skip_unless(False, *args, **kwargs)
class DownloadTest(unittest.TestCase):
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
def setUp(self):
# Clear old files
self.tearDown()
with io.open(self.PARAMETERS_FILE, encoding='utf-8') as pf:
self.parameters = json.load(pf)
@_skip_unless(youtube_dl.InfoExtractors.YoutubeIE._WORKING, "IE marked as not _WORKING")
def test_Youtube(self):
filename = u'BaW_jenozKc.mp4'
params = self.parameters
fd = FileDownloader(params)
fd.add_info_extractor(youtube_dl.InfoExtractors.YoutubeIE())
fd.download([u'http://www.youtube.com/watch?v=BaW_jenozKc'])
self.assertTrue(os.path.exists(filename))
@_skip_unless(youtube_dl.InfoExtractors.DailymotionIE._WORKING, "IE marked as not _WORKING")
def test_Dailymotion(self):
filename = u'x33vw9.mp4'
params = self.parameters
fd = FileDownloader(params)
fd.add_info_extractor(youtube_dl.InfoExtractors.DailymotionIE())
fd.download([u'http://www.dailymotion.com/video/x33vw9_tutoriel-de-youtubeur-dl-des-video_tech'])
self.assertTrue(os.path.exists(filename))
md5_for_file = _file_md5(filename)
self.assertEqual(md5_for_file, u'392c4b85a60a90dc4792da41ce3144eb')
@_skip_unless(youtube_dl.InfoExtractors.MetacafeIE._WORKING, "IE marked as not _WORKING")
def test_Metacafe(self):
filename = u'_aUehQsCQtM.flv'
params = self.parameters
fd = FileDownloader(params)
fd.add_info_extractor(youtube_dl.InfoExtractors.MetacafeIE())
fd.add_info_extractor(youtube_dl.InfoExtractors.YoutubeIE())
fd.download([u'http://www.metacafe.com/watch/yt-_aUehQsCQtM/the_electric_company_short_i_pbs_kids_go/'])
self.assertTrue(os.path.exists(filename))
@_skip_unless(youtube_dl.InfoExtractors.BlipTVIE._WORKING, "IE marked as not _WORKING")
def test_BlipTV(self):
filename = u'5779306.m4v'
params = self.parameters
fd = FileDownloader(params)
fd.add_info_extractor(youtube_dl.InfoExtractors.BlipTVIE())
fd.download([u'http://blip.tv/cbr/cbr-exclusive-gotham-city-imposters-bats-vs-jokerz-short-3-5796352'])
self.assertTrue(os.path.exists(filename))
md5_for_file = _file_md5(filename)
self.assertEqual(md5_for_file, u'b2d849efcf7ee18917e4b4d9ff37cafe')
@_skip_unless(youtube_dl.InfoExtractors.XVideosIE._WORKING, "IE marked as not _WORKING")
def test_XVideos(self):
filename = u'939581.flv'
params = self.parameters
fd = FileDownloader(params)
fd.add_info_extractor(youtube_dl.InfoExtractors.XVideosIE())
fd.download([u'http://www.xvideos.com/video939581/funny_porns_by_s_-1'])
self.assertTrue(os.path.exists(filename))
md5_for_file = _file_md5(filename)
self.assertEqual(md5_for_file, u'1d0c835822f0a71a7bf011855db929d0')
@_skip_unless(youtube_dl.InfoExtractors.VimeoIE._WORKING, "IE marked as not _WORKING")
def test_Vimeo(self):
filename = u'14160053.mp4'
params = self.parameters
fd = FileDownloader(params)
fd.add_info_extractor(youtube_dl.InfoExtractors.VimeoIE())
fd.download([u'http://vimeo.com/14160053'])
self.assertTrue(os.path.exists(filename))
md5_for_file = _file_md5(filename)
self.assertEqual(md5_for_file, u'60540a4ec7cc378ec84b919c0aed5023')
@_skip_unless(youtube_dl.InfoExtractors.SoundcloudIE._WORKING, "IE marked as not _WORKING")
def test_Soundcloud(self):
filename = u'62986583.mp3'
params = self.parameters
fd = FileDownloader(params)
fd.add_info_extractor(youtube_dl.InfoExtractors.SoundcloudIE())
fd.download([u'http://soundcloud.com/ethmusic/lostin-powers-she-so-heavy'])
self.assertTrue(os.path.exists(filename))
md5_for_file = _file_md5(filename)
self.assertEqual(md5_for_file, u'ebef0a451b909710ed1d7787dddbf0d7')
@_skip_unless(youtube_dl.InfoExtractors.StanfordOpenClassroomIE._WORKING, "IE marked as not _WORKING")
def test_StanfordOpenClassroom(self):
filename = u'PracticalUnix_intro-environment.mp4'
params = self.parameters
fd = FileDownloader(params)
fd.add_info_extractor(youtube_dl.InfoExtractors.StanfordOpenClassroomIE())
fd.download([u'http://openclassroom.stanford.edu/MainFolder/VideoPage.php?course=PracticalUnix&video=intro-environment&speed=100'])
self.assertTrue(os.path.exists(filename))
md5_for_file = _file_md5(filename)
self.assertEqual(md5_for_file, u'544a9468546059d4e80d76265b0443b8')
@_skip_unless(youtube_dl.InfoExtractors.XNXXIE._WORKING, "IE marked as not _WORKING")
def test_XNXX(self):
filename = u'1135332.flv'
params = self.parameters
fd = FileDownloader(params)
fd.add_info_extractor(youtube_dl.InfoExtractors.XNXXIE())
fd.download([u'http://video.xnxx.com/video1135332/lida_naked_funny_actress_5_'])
self.assertTrue(os.path.exists(filename))
md5_for_file = _file_md5(filename)
self.assertEqual(md5_for_file, u'0831677e2b4761795f68d417e0b7b445')
@_skip_unless(youtube_dl.InfoExtractors.YoukuIE._WORKING, "IE marked as not _WORKING")
def test_Youku(self):
filename = u'XNDgyMDQ2NTQw_part00.flv'
params = self.parameters
params["test"] = False
fd = FileDownloader(params)
fd.add_info_extractor(youtube_dl.InfoExtractors.YoukuIE())
fd.download([u'http://v.youku.com/v_show/id_XNDgyMDQ2NTQw.html'])
self.assertTrue(os.path.exists(filename))
md5_for_file = _file_md5(filename)
self.assertEqual(md5_for_file, u'ffe3f2e435663dc2d1eea34faeff5b5b')
def tearDown(self):
if os.path.exists(u'BaW_jenozKc.mp4'):
os.remove(u'BaW_jenozKc.mp4')
if os.path.exists(u'x33vw9.mp4'):
os.remove(u'x33vw9.mp4')
if os.path.exists(u'_aUehQsCQtM.flv'):
os.remove(u'_aUehQsCQtM.flv')
if os.path.exists(u'5779306.m4v'):
os.remove(u'5779306.m4v')
if os.path.exists(u'939581.flv'):
os.remove(u'939581.flv')
if os.path.exists(u'14160053.mp4'):
os.remove(u'14160053.mp4')
if os.path.exists(u'62986583.mp3'):
os.remove(u'62986583.mp3')
if os.path.exists(u'PracticalUnix_intro-environment.mp4'):
os.remove(u'PracticalUnix_intro-environment.mp4')
if os.path.exists(u'1135332.flv'):
os.remove(u'1135332.flv')
if os.path.exists(u'XNDgyMDQ2NTQw_part00.flv'):
os.remove(u'XNDgyMDQ2NTQw_part00.flv')
if __name__ == '__main__':
unittest.main()

26
test/test_execution.py Normal file
View File

@@ -0,0 +1,26 @@
import unittest
import sys
import os
import subprocess
rootDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
try:
_DEV_NULL = subprocess.DEVNULL
except AttributeError:
_DEV_NULL = open(os.devnull, 'wb')
class TestExecution(unittest.TestCase):
def test_import(self):
subprocess.check_call([sys.executable, '-c', 'import youtube_dl'], cwd=rootDir)
def test_module_exec(self):
if sys.version_info >= (2,7): # Python 2.6 doesn't support package execution
subprocess.check_call([sys.executable, '-m', 'youtube_dl', '--version'], cwd=rootDir, stdout=_DEV_NULL)
def test_main_exec(self):
subprocess.check_call([sys.executable, 'youtube_dl/__main__.py', '--version'], cwd=rootDir, stdout=_DEV_NULL)
if __name__ == '__main__':
unittest.main()

100
test/test_utils.py Normal file
View File

@@ -0,0 +1,100 @@
#!/usr/bin/env python
# Various small unit tests
import sys
import unittest
# Allow direct execution
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
#from youtube_dl.utils import htmlentity_transform
from youtube_dl.utils import timeconvert
from youtube_dl.utils import sanitize_filename
from youtube_dl.utils import unescapeHTML
from youtube_dl.utils import orderedSet
if sys.version_info < (3, 0):
_compat_str = lambda b: b.decode('unicode-escape')
else:
_compat_str = lambda s: s
class TestUtil(unittest.TestCase):
def test_timeconvert(self):
self.assertTrue(timeconvert('') is None)
self.assertTrue(timeconvert('bougrg') is None)
def test_sanitize_filename(self):
self.assertEqual(sanitize_filename('abc'), 'abc')
self.assertEqual(sanitize_filename('abc_d-e'), 'abc_d-e')
self.assertEqual(sanitize_filename('123'), '123')
self.assertEqual('abc_de', sanitize_filename('abc/de'))
self.assertFalse('/' in sanitize_filename('abc/de///'))
self.assertEqual('abc_de', sanitize_filename('abc/<>\\*|de'))
self.assertEqual('xxx', sanitize_filename('xxx/<>\\*|'))
self.assertEqual('yes no', sanitize_filename('yes? no'))
self.assertEqual('this - that', sanitize_filename('this: that'))
self.assertEqual(sanitize_filename('AT&T'), 'AT&T')
aumlaut = _compat_str('\xe4')
self.assertEqual(sanitize_filename(aumlaut), aumlaut)
tests = _compat_str('\u043a\u0438\u0440\u0438\u043b\u043b\u0438\u0446\u0430')
self.assertEqual(sanitize_filename(tests), tests)
forbidden = '"\0\\/'
for fc in forbidden:
for fbc in forbidden:
self.assertTrue(fbc not in sanitize_filename(fc))
def test_sanitize_filename_restricted(self):
self.assertEqual(sanitize_filename('abc', restricted=True), 'abc')
self.assertEqual(sanitize_filename('abc_d-e', restricted=True), 'abc_d-e')
self.assertEqual(sanitize_filename('123', restricted=True), '123')
self.assertEqual('abc_de', sanitize_filename('abc/de', restricted=True))
self.assertFalse('/' in sanitize_filename('abc/de///', restricted=True))
self.assertEqual('abc_de', sanitize_filename('abc/<>\\*|de', restricted=True))
self.assertEqual('xxx', sanitize_filename('xxx/<>\\*|', restricted=True))
self.assertEqual('yes_no', sanitize_filename('yes? no', restricted=True))
self.assertEqual('this_-_that', sanitize_filename('this: that', restricted=True))
tests = _compat_str('a\xe4b\u4e2d\u56fd\u7684c')
self.assertEqual(sanitize_filename(tests, restricted=True), 'a_b_c')
self.assertTrue(sanitize_filename(_compat_str('\xf6'), restricted=True) != '') # No empty filename
forbidden = '"\0\\/&!: \'\t\n()[]{}$;`^,#'
for fc in forbidden:
for fbc in forbidden:
self.assertTrue(fbc not in sanitize_filename(fc, restricted=True))
# Handle a common case more neatly
self.assertEqual(sanitize_filename(_compat_str('\u5927\u58f0\u5e26 - Song'), restricted=True), 'Song')
self.assertEqual(sanitize_filename(_compat_str('\u603b\u7edf: Speech'), restricted=True), 'Speech')
# .. but make sure the file name is never empty
self.assertTrue(sanitize_filename('-', restricted=True) != '')
self.assertTrue(sanitize_filename(':', restricted=True) != '')
def test_sanitize_ids(self):
self.assertEquals(sanitize_filename('_n_cd26wFpw', is_id=True), '_n_cd26wFpw')
self.assertEquals(sanitize_filename('_BD_eEpuzXw', is_id=True), '_BD_eEpuzXw')
self.assertEquals(sanitize_filename('N0Y__7-UOdI', is_id=True), 'N0Y__7-UOdI')
def test_ordered_set(self):
self.assertEqual(orderedSet([1, 1, 2, 3, 4, 4, 5, 6, 7, 3, 5]), [1, 2, 3, 4, 5, 6, 7])
self.assertEqual(orderedSet([]), [])
self.assertEqual(orderedSet([1]), [1])
#keep the list ordered
self.assertEqual(orderedSet([135, 1, 1, 1]), [135, 1])
def test_unescape_html(self):
self.assertEqual(unescapeHTML(_compat_str('%20;')), _compat_str('%20;'))
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,70 @@
#!/usr/bin/env python
import sys
import unittest
import socket
# Allow direct execution
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from youtube_dl.InfoExtractors import YoutubePlaylistIE
from youtube_dl.utils import *
# General configuration (from __init__, not very elegant...)
jar = compat_cookiejar.CookieJar()
cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
proxy_handler = compat_urllib_request.ProxyHandler()
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
compat_urllib_request.install_opener(opener)
socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
class FakeDownloader(object):
def __init__(self):
self.result = []
self.params = {}
def to_screen(self, s):
print(s)
def trouble(self, s):
raise Exception(s)
def download(self, x):
self.result.append(x)
class TestYoutubeLists(unittest.TestCase):
def test_youtube_playlist(self):
DL = FakeDownloader()
IE = YoutubePlaylistIE(DL)
IE.extract('https://www.youtube.com/playlist?list=PLwiyx1dc3P2JR9N8gQaQN_BCvlSlap7re')
self.assertEqual(DL.result, [
['http://www.youtube.com/watch?v=bV9L5Ht9LgY'],
['http://www.youtube.com/watch?v=FXxLjLQi3Fg'],
['http://www.youtube.com/watch?v=tU3Bgo5qJZE']
])
def test_youtube_playlist_long(self):
DL = FakeDownloader()
IE = YoutubePlaylistIE(DL)
IE.extract('https://www.youtube.com/playlist?list=UUBABnxM4Ar9ten8Mdjj1j0Q')
self.assertTrue(len(DL.result) >= 799)
def test_youtube_course(self):
DL = FakeDownloader()
IE = YoutubePlaylistIE(DL)
# TODO find a > 100 (paginating?) videos course
IE.extract('https://www.youtube.com/course?list=ECUl4u3cNGP61MdtwGTqZA0MreSaDybji8')
self.assertEqual(DL.result[0], ['http://www.youtube.com/watch?v=j9WZyLZCBzs'])
self.assertEqual(len(DL.result), 25)
self.assertEqual(DL.result[-1], ['http://www.youtube.com/watch?v=rYefUsYuEp0'])
def test_youtube_channel(self):
"""I give up, please find a channel that does paginate and test this like test_youtube_playlist_long"""
pass # TODO
def test_youtube_user(self):
DL = FakeDownloader()
IE = YoutubePlaylistIE(DL)
IE.extract('https://www.youtube.com/user/TheLinuxFoundation')
self.assertTrue(len(DL.result) >= 320)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
import sys
import unittest
# Allow direct execution
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from youtube_dl.InfoExtractors import YoutubeIE, YoutubePlaylistIE
class TestYoutubePlaylistMatching(unittest.TestCase):
def test_playlist_matching(self):
self.assertTrue(YoutubePlaylistIE().suitable(u'ECUl4u3cNGP61MdtwGTqZA0MreSaDybji8'))
self.assertTrue(YoutubePlaylistIE().suitable(u'PL63F0C78739B09958'))
self.assertFalse(YoutubePlaylistIE().suitable(u'PLtS2H6bU1M'))
def test_youtube_matching(self):
self.assertTrue(YoutubeIE().suitable(u'PLtS2H6bU1M'))
if __name__ == '__main__':
unittest.main()

62
test/tests.json Normal file
View File

@@ -0,0 +1,62 @@
[
{
"name": "Youtube",
"url": "http://www.youtube.com/watch?v=BaW_jenozKc",
"file": "BaW_jenozKc.mp4"
},
{
"name": "Dailymotion",
"md5": "392c4b85a60a90dc4792da41ce3144eb",
"url": "http://www.dailymotion.com/video/x33vw9_tutoriel-de-youtubeur-dl-des-video_tech",
"file": "x33vw9.mp4"
},
{
"name": "Metacafe",
"addIEs": ["Youtube"],
"url": "http://www.metacafe.com/watch/yt-_aUehQsCQtM/the_electric_company_short_i_pbs_kids_go/",
"file": "_aUehQsCQtM.flv"
},
{
"name": "BlipTV",
"md5": "b2d849efcf7ee18917e4b4d9ff37cafe",
"url": "http://blip.tv/cbr/cbr-exclusive-gotham-city-imposters-bats-vs-jokerz-short-3-5796352",
"file": "5779306.m4v"
},
{
"name": "XVideos",
"md5": "1d0c835822f0a71a7bf011855db929d0",
"url": "http://www.xvideos.com/video939581/funny_porns_by_s_-1",
"file": "939581.flv"
},
{
"name": "Vimeo",
"md5": "60540a4ec7cc378ec84b919c0aed5023",
"url": "http://vimeo.com/14160053",
"file": "14160053.mp4"
},
{
"name": "Soundcloud",
"md5": "ebef0a451b909710ed1d7787dddbf0d7",
"url": "http://soundcloud.com/ethmusic/lostin-powers-she-so-heavy",
"file": "62986583.mp3"
},
{
"name": "StanfordOpenClassroom",
"md5": "544a9468546059d4e80d76265b0443b8",
"url": "http://openclassroom.stanford.edu/MainFolder/VideoPage.php?course=PracticalUnix&video=intro-environment&speed=100",
"file": "PracticalUnix_intro-environment.mp4"
},
{
"name": "XNXX",
"md5": "0831677e2b4761795f68d417e0b7b445",
"url": "http://video.xnxx.com/video1135332/lida_naked_funny_actress_5_",
"file": "1135332.flv"
},
{
"name": "Youku",
"url": "http://v.youku.com/v_show/id_XNDgyMDQ2NTQw.html",
"file": "XNDgyMDQ2NTQw_part00.flv",
"md5": "ffe3f2e435663dc2d1eea34faeff5b5b",
"params": { "test": false }
}
]

2351
youtube-dl

File diff suppressed because it is too large Load Diff

BIN
youtube-dl.exe Normal file

Binary file not shown.

View File

@@ -0,0 +1,728 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import math
import os
import re
import socket
import subprocess
import sys
import time
import traceback
if os.name == 'nt':
import ctypes
from .utils import *
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 extracts
all the information about the video or videos the URL refers to, and
asks the FileDownloader to process the video information, possibly
downloading the video.
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 params
attribute 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.
forcethumbnail: Force printing thumbnail URL.
forcedescription: Force printing description.
forcefilename: Force printing final filename.
simulate: Do not download the video files.
format: Video format code.
format_limit: Highest quality format to try.
outtmpl: Template for output names.
restrictfilenames: Do not allow "&" and spaces in file names
ignoreerrors: Do not stop on download errors.
ratelimit: Download speed limit, in bytes/sec.
nooverwrites: Prevent overwriting files.
retries: Number of times to retry for HTTP error 5xx
buffersize: Size of download buffer in bytes.
noresizebuffer: Do not automatically resize the download buffer.
continuedl: Try to continue downloads if possible.
noprogress: Do not print the progress bar.
playliststart: Playlist item to start at.
playlistend: Playlist item to end at.
matchtitle: Download only matching titles.
rejecttitle: Reject downloads for matching titles.
logtostderr: Log messages to stderr instead of stdout.
consoletitle: Display progress in console window's titlebar.
nopart: Do not use temporary .part files.
updatetime: Use the Last-modified header to set output file timestamps.
writedescription: Write the video description to a .description file
writeinfojson: Write the video description to a .info.json file
writesubtitles: Write the video subtitles to a .srt file
subtitleslang: Language of the subtitles to download
test: Download only first bytes to test the downloader.
"""
params = None
_ies = []
_pps = []
_download_retcode = None
_num_downloads = None
_screen_file = None
def __init__(self, params):
"""Create a FileDownloader object with the given options."""
self._ies = []
self._pps = []
self._download_retcode = 0
self._num_downloads = 0
self._screen_file = [sys.stdout, sys.stderr][params.get('logtostderr', False)]
self.params = params
if '%(stitle)s' in self.params['outtmpl']:
self.to_stderr(u'WARNING: %(stitle)s is deprecated. Use the %(title)s and the --restrict-filenames flag(which also secures %(uploader)s et al) instead.')
@staticmethod
def format_bytes(bytes):
if bytes is None:
return 'N/A'
if type(bytes) is str:
bytes = float(bytes)
if bytes == 0.0:
exponent = 0
else:
exponent = int(math.log(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 = int((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 an 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 int(round(number * multiplier))
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_screen(self, message, skip_eol=False):
"""Print message to stdout if not in quiet mode."""
assert type(message) == type(u'')
if not self.params.get('quiet', False):
terminator = [u'\n', u''][skip_eol]
output = message + terminator
if 'b' in getattr(self._screen_file, 'mode', '') or sys.version_info[0] < 3: # Python 2 lies about the mode of sys.stdout/sys.stderr
output = output.encode(preferredencoding(), 'ignore')
self._screen_file.write(output)
self._screen_file.flush()
def to_stderr(self, message):
"""Print message to stderr."""
assert type(message) == type(u'')
output = message + u'\n'
if 'b' in getattr(self._screen_file, 'mode', '') or sys.version_info[0] < 3: # Python 2 lies about the mode of sys.stdout/sys.stderr
output = output.encode(preferredencoding())
sys.stderr.write(output)
def to_cons_title(self, message):
"""Set console/terminal window title to message."""
if not self.params.get('consoletitle', False):
return
if os.name == 'nt' and ctypes.windll.kernel32.GetConsoleWindow():
# c_wchar_p() might not be necessary if `message` is
# already of type unicode()
ctypes.windll.kernel32.SetConsoleTitleW(ctypes.c_wchar_p(message))
elif 'TERM' in os.environ:
sys.stderr.write('\033]0;%s\007' % message.encode(preferredencoding()))
def fixed_template(self):
"""Checks if the output template is fixed."""
return (re.search(u'(?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 message is not None:
self.to_stderr(message)
if self.params.get('verbose'):
self.to_stderr(u''.join(traceback.format_list(traceback.extract_stack())))
if not self.params.get('ignoreerrors', False):
raise DownloadError(message)
self._download_retcode = 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 temp_name(self, filename):
"""Returns a temporary filename for the given filename."""
if self.params.get('nopart', False) or filename == u'-' or \
(os.path.exists(encodeFilename(filename)) and not os.path.isfile(encodeFilename(filename))):
return filename
return filename + u'.part'
def undo_temp_name(self, filename):
if filename.endswith(u'.part'):
return filename[:-len(u'.part')]
return filename
def try_rename(self, old_filename, new_filename):
try:
if old_filename == new_filename:
return
os.rename(encodeFilename(old_filename), encodeFilename(new_filename))
except (IOError, OSError) as err:
self.trouble(u'ERROR: unable to rename file')
def try_utime(self, filename, last_modified_hdr):
"""Try to set the last-modified time of the given file."""
if last_modified_hdr is None:
return
if not os.path.isfile(encodeFilename(filename)):
return
timestr = last_modified_hdr
if timestr is None:
return
filetime = timeconvert(timestr)
if filetime is None:
return filetime
try:
os.utime(filename, (time.time(), filetime))
except:
pass
return filetime
def report_writedescription(self, descfn):
""" Report that the description file is being written """
self.to_screen(u'[info] Writing video description to: ' + descfn)
def report_writesubtitles(self, srtfn):
""" Report that the subtitles file is being written """
self.to_screen(u'[info] Writing video subtitles to: ' + srtfn)
def report_writeinfojson(self, infofn):
""" Report that the metadata file has been written """
self.to_screen(u'[info] Video description metadata as JSON to: ' + infofn)
def report_destination(self, filename):
"""Report destination filename."""
self.to_screen(u'[download] Destination: ' + filename)
def report_progress(self, percent_str, data_len_str, speed_str, eta_str):
"""Report download progress."""
if self.params.get('noprogress', False):
return
self.to_screen(u'\r[download] %s of %s at %s ETA %s' %
(percent_str, data_len_str, speed_str, eta_str), skip_eol=True)
self.to_cons_title(u'youtube-dl - %s of %s at %s ETA %s' %
(percent_str.strip(), data_len_str.strip(), speed_str.strip(), eta_str.strip()))
def report_resuming_byte(self, resume_len):
"""Report attempt to resume at given byte."""
self.to_screen(u'[download] Resuming download at byte %s' % resume_len)
def report_retry(self, count, retries):
"""Report retry in case of HTTP error 5xx"""
self.to_screen(u'[download] Got server HTTP error. Retrying (attempt %d of %d)...' % (count, retries))
def report_file_already_downloaded(self, file_name):
"""Report file has already been fully downloaded."""
try:
self.to_screen(u'[download] %s has already been downloaded' % file_name)
except (UnicodeEncodeError) as err:
self.to_screen(u'[download] The file has already been downloaded')
def report_unable_to_resume(self):
"""Report it was impossible to resume download."""
self.to_screen(u'[download] Unable to resume')
def report_finish(self):
"""Report download finished."""
if self.params.get('noprogress', False):
self.to_screen(u'[download] Download completed')
else:
self.to_screen(u'')
def increment_downloads(self):
"""Increment the ordinal that assigns a number to each file."""
self._num_downloads += 1
def prepare_filename(self, info_dict):
"""Generate the output filename."""
try:
template_dict = dict(info_dict)
template_dict['epoch'] = int(time.time())
template_dict['autonumber'] = u'%05d' % self._num_downloads
sanitize = lambda k,v: sanitize_filename(
u'NA' if v is None else compat_str(v),
restricted=self.params.get('restrictfilenames'),
is_id=(k==u'id'))
template_dict = dict((k, sanitize(k, v)) for k,v in template_dict.items())
filename = self.params['outtmpl'] % template_dict
return filename
except (ValueError, KeyError) as err:
self.trouble(u'ERROR: invalid system charset or erroneous output template')
return None
def _match_entry(self, info_dict):
""" Returns None iff the file should be downloaded """
title = info_dict['title']
matchtitle = self.params.get('matchtitle', False)
if matchtitle:
matchtitle = matchtitle.decode('utf8')
if not re.search(matchtitle, title, re.IGNORECASE):
return u'[download] "' + title + '" title did not match pattern "' + matchtitle + '"'
rejecttitle = self.params.get('rejecttitle', False)
if rejecttitle:
rejecttitle = rejecttitle.decode('utf8')
if re.search(rejecttitle, title, re.IGNORECASE):
return u'"' + title + '" title matched reject pattern "' + rejecttitle + '"'
return None
def process_info(self, info_dict):
"""Process a single dictionary returned by an InfoExtractor."""
# Keep for backwards compatibility
info_dict['stitle'] = info_dict['title']
if not 'format' in info_dict:
info_dict['format'] = info_dict['ext']
reason = self._match_entry(info_dict)
if reason is not None:
self.to_screen(u'[download] ' + reason)
return
max_downloads = self.params.get('max_downloads')
if max_downloads is not None:
if self._num_downloads > int(max_downloads):
raise MaxDownloadsReached()
filename = self.prepare_filename(info_dict)
# Forced printings
if self.params.get('forcetitle', False):
compat_print(info_dict['title'])
if self.params.get('forceurl', False):
compat_print(info_dict['url'])
if self.params.get('forcethumbnail', False) and 'thumbnail' in info_dict:
compat_print(info_dict['thumbnail'])
if self.params.get('forcedescription', False) and 'description' in info_dict:
compat_print(info_dict['description'])
if self.params.get('forcefilename', False) and filename is not None:
compat_print(filename)
if self.params.get('forceformat', False):
compat_print(info_dict['format'])
# Do nothing else if in simulate mode
if self.params.get('simulate', False):
return
if filename is None:
return
try:
dn = os.path.dirname(encodeFilename(filename))
if dn != '' and not os.path.exists(dn): # dn is already encoded
os.makedirs(dn)
except (OSError, IOError) as err:
self.trouble(u'ERROR: unable to create directory ' + compat_str(err))
return
if self.params.get('writedescription', False):
try:
descfn = filename + u'.description'
self.report_writedescription(descfn)
descfile = open(encodeFilename(descfn), 'wb')
try:
descfile.write(info_dict['description'].encode('utf-8'))
finally:
descfile.close()
except (OSError, IOError):
self.trouble(u'ERROR: Cannot write description file ' + descfn)
return
if self.params.get('writesubtitles', False) and 'subtitles' in info_dict and info_dict['subtitles']:
# subtitles download errors are already managed as troubles in relevant IE
# that way it will silently go on when used with unsupporting IE
try:
srtfn = filename.rsplit('.', 1)[0] + u'.srt'
self.report_writesubtitles(srtfn)
srtfile = open(encodeFilename(srtfn), 'wb')
try:
srtfile.write(info_dict['subtitles'].encode('utf-8'))
finally:
srtfile.close()
except (OSError, IOError):
self.trouble(u'ERROR: Cannot write subtitles file ' + descfn)
return
if self.params.get('writeinfojson', False):
infofn = filename + u'.info.json'
self.report_writeinfojson(infofn)
try:
json.dump
except (NameError,AttributeError):
self.trouble(u'ERROR: No JSON encoder found. Update to Python 2.6+, setup a json module, or leave out --write-info-json.')
return
try:
infof = open(encodeFilename(infofn), 'wb')
try:
json_info_dict = dict((k,v) for k,v in info_dict.iteritems() if not k in ('urlhandle',))
json.dump(json_info_dict, infof)
finally:
infof.close()
except (OSError, IOError):
self.trouble(u'ERROR: Cannot write metadata to JSON file ' + infofn)
return
if not self.params.get('skip_download', False):
if self.params.get('nooverwrites', False) and os.path.exists(encodeFilename(filename)):
success = True
else:
try:
success = self._do_download(filename, info_dict)
except (OSError, IOError) as err:
raise UnavailableVideoError()
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
self.trouble(u'ERROR: unable to download video data: %s' % str(err))
return
except (ContentTooShortError, ) as err:
self.trouble(u'ERROR: content too short (expected %s bytes and served %s)' % (err.expected, err.downloaded))
return
if success:
try:
self.post_process(filename, info_dict)
except (PostProcessingError) as err:
self.trouble(u'ERROR: postprocessing: %s' % str(err))
return
def download(self, url_list):
"""Download a given list of URLs."""
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:
# Go to next InfoExtractor if not suitable
if not ie.suitable(url):
continue
# Warn if the _WORKING attribute is False
if not ie.working():
self.trouble(u'WARNING: the program functionality for this site has been marked as broken, '
u'and will probably not work. If you want to go on, use the -i option.')
# Suitable InfoExtractor found
suitable_found = True
# Extract information from URL and process it
videos = ie.extract(url)
for video in videos or []:
video['extractor'] = ie.IE_NAME
try:
self.increment_downloads()
self.process_info(video)
except UnavailableVideoError:
self.trouble(u'\nERROR: unable to download video')
# Suitable InfoExtractor had been found; go to next URL
break
if not suitable_found:
self.trouble(u'ERROR: no suitable InfoExtractor: %s' % url)
return self._download_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 _download_with_rtmpdump(self, filename, url, player_url):
self.report_destination(filename)
tmpfilename = self.temp_name(filename)
# Check for rtmpdump first
try:
subprocess.call(['rtmpdump', '-h'], stdout=(file(os.path.devnull, 'w')), stderr=subprocess.STDOUT)
except (OSError, IOError):
self.trouble(u'ERROR: RTMP download detected but "rtmpdump" could not be run')
return False
# Download using rtmpdump. rtmpdump returns exit code 2 when
# the connection was interrumpted and resuming appears to be
# possible. This is part of rtmpdump's normal usage, AFAIK.
basic_args = ['rtmpdump', '-q'] + [[], ['-W', player_url]][player_url is not None] + ['-r', url, '-o', tmpfilename]
args = basic_args + [[], ['-e', '-k', '1']][self.params.get('continuedl', False)]
if self.params.get('verbose', False):
try:
import pipes
shell_quote = lambda args: ' '.join(map(pipes.quote, args))
except ImportError:
shell_quote = repr
self.to_screen(u'[debug] rtmpdump command line: ' + shell_quote(args))
retval = subprocess.call(args)
while retval == 2 or retval == 1:
prevsize = os.path.getsize(encodeFilename(tmpfilename))
self.to_screen(u'\r[rtmpdump] %s bytes' % prevsize, skip_eol=True)
time.sleep(5.0) # This seems to be needed
retval = subprocess.call(basic_args + ['-e'] + [[], ['-k', '1']][retval == 1])
cursize = os.path.getsize(encodeFilename(tmpfilename))
if prevsize == cursize and retval == 1:
break
# Some rtmp streams seem abort after ~ 99.8%. Don't complain for those
if prevsize == cursize and retval == 2 and cursize > 1024:
self.to_screen(u'\r[rtmpdump] Could not download the whole video. This can happen for some advertisements.')
retval = 0
break
if retval == 0:
self.to_screen(u'\r[rtmpdump] %s bytes' % os.path.getsize(encodeFilename(tmpfilename)))
self.try_rename(tmpfilename, filename)
return True
else:
self.trouble(u'\nERROR: rtmpdump exited with code %d' % retval)
return False
def _do_download(self, filename, info_dict):
url = info_dict['url']
player_url = info_dict.get('player_url', None)
# Check file already present
if self.params.get('continuedl', False) and os.path.isfile(encodeFilename(filename)) and not self.params.get('nopart', False):
self.report_file_already_downloaded(filename)
return True
# Attempt to download using rtmpdump
if url.startswith('rtmp'):
return self._download_with_rtmpdump(filename, url, player_url)
tmpfilename = self.temp_name(filename)
stream = None
# Do not include the Accept-Encoding header
headers = {'Youtubedl-no-compression': 'True'}
basic_request = compat_urllib_request.Request(url, None, headers)
request = compat_urllib_request.Request(url, None, headers)
if self.params.get('test', False):
request.add_header('Range','bytes=0-10240')
# Establish possible resume length
if os.path.isfile(encodeFilename(tmpfilename)):
resume_len = os.path.getsize(encodeFilename(tmpfilename))
else:
resume_len = 0
open_mode = 'wb'
if resume_len != 0:
if self.params.get('continuedl', False):
self.report_resuming_byte(resume_len)
request.add_header('Range','bytes=%d-' % resume_len)
open_mode = 'ab'
else:
resume_len = 0
count = 0
retries = self.params.get('retries', 0)
while count <= retries:
# Establish connection
try:
if count == 0 and 'urlhandle' in info_dict:
data = info_dict['urlhandle']
data = compat_urllib_request.urlopen(request)
break
except (compat_urllib_error.HTTPError, ) as err:
if (err.code < 500 or err.code >= 600) and err.code != 416:
# Unexpected HTTP error
raise
elif err.code == 416:
# Unable to resume (requested range not satisfiable)
try:
# Open the connection again without the range header
data = compat_urllib_request.urlopen(basic_request)
content_length = data.info()['Content-Length']
except (compat_urllib_error.HTTPError, ) as err:
if err.code < 500 or err.code >= 600:
raise
else:
# Examine the reported length
if (content_length is not None and
(resume_len - 100 < int(content_length) < resume_len + 100)):
# The file had already been fully downloaded.
# Explanation to the above condition: in issue #175 it was revealed that
# YouTube sometimes adds or removes a few bytes from the end of the file,
# changing the file size slightly and causing problems for some users. So
# I decided to implement a suggested change and consider the file
# completely downloaded if the file size differs less than 100 bytes from
# the one in the hard drive.
self.report_file_already_downloaded(filename)
self.try_rename(tmpfilename, filename)
return True
else:
# The length does not match, we start the download over
self.report_unable_to_resume()
open_mode = 'wb'
break
# Retry
count += 1
if count <= retries:
self.report_retry(count, retries)
if count > retries:
self.trouble(u'ERROR: giving up after %s retries' % retries)
return False
data_len = data.info().get('Content-length', None)
if data_len is not None:
data_len = int(data_len) + resume_len
data_len_str = self.format_bytes(data_len)
byte_counter = 0 + resume_len
block_size = self.params.get('buffersize', 1024)
start = time.time()
while True:
# Download and write
before = time.time()
data_block = data.read(block_size)
after = time.time()
if len(data_block) == 0:
break
byte_counter += len(data_block)
# Open file just in time
if stream is None:
try:
(stream, tmpfilename) = sanitize_open(tmpfilename, open_mode)
assert stream is not None
filename = self.undo_temp_name(tmpfilename)
self.report_destination(filename)
except (OSError, IOError) as err:
self.trouble(u'ERROR: unable to open for writing: %s' % str(err))
return False
try:
stream.write(data_block)
except (IOError, OSError) as err:
self.trouble(u'\nERROR: unable to write data: %s' % str(err))
return False
if not self.params.get('noresizebuffer', False):
block_size = self.best_block_size(after - before, len(data_block))
# Progress message
speed_str = self.calc_speed(start, time.time(), byte_counter - resume_len)
if data_len is None:
self.report_progress('Unknown %', data_len_str, speed_str, 'Unknown ETA')
else:
percent_str = self.calc_percent(byte_counter, data_len)
eta_str = self.calc_eta(start, time.time(), data_len - resume_len, byte_counter - resume_len)
self.report_progress(percent_str, data_len_str, speed_str, eta_str)
# Apply rate limit
self.slow_down(start, byte_counter - resume_len)
if stream is None:
self.trouble(u'\nERROR: Did not get any data blocks')
return False
stream.close()
self.report_finish()
if data_len is not None and byte_counter != data_len:
raise ContentTooShortError(byte_counter, int(data_len))
self.try_rename(tmpfilename, filename)
# Update file modification time
if self.params.get('updatetime', True):
info_dict['filetime'] = self.try_utime(filename, data.info().get('last-modified', None))
return True

3568
youtube_dl/InfoExtractors.py Normal file

File diff suppressed because it is too large Load Diff

200
youtube_dl/PostProcessor.py Normal file
View File

@@ -0,0 +1,200 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import os
import subprocess
import sys
import time
from .utils import *
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 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
composed 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
class AudioConversionError(BaseException):
def __init__(self, message):
self.message = message
class FFmpegExtractAudioPP(PostProcessor):
def __init__(self, downloader=None, preferredcodec=None, preferredquality=None, keepvideo=False):
PostProcessor.__init__(self, downloader)
if preferredcodec is None:
preferredcodec = 'best'
self._preferredcodec = preferredcodec
self._preferredquality = preferredquality
self._keepvideo = keepvideo
self._exes = self.detect_executables()
@staticmethod
def detect_executables():
def executable(exe):
try:
subprocess.Popen([exe, '-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
except OSError:
return False
return exe
programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe']
return dict((program, executable(program)) for program in programs)
def get_audio_codec(self, path):
if not self._exes['ffprobe'] and not self._exes['avprobe']: return None
try:
cmd = [self._exes['avprobe'] or self._exes['ffprobe'], '-show_streams', '--', encodeFilename(path)]
handle = subprocess.Popen(cmd, stderr=file(os.path.devnull, 'w'), stdout=subprocess.PIPE)
output = handle.communicate()[0]
if handle.wait() != 0:
return None
except (IOError, OSError):
return None
audio_codec = None
for line in output.split('\n'):
if line.startswith('codec_name='):
audio_codec = line.split('=')[1].strip()
elif line.strip() == 'codec_type=audio' and audio_codec is not None:
return audio_codec
return None
def run_ffmpeg(self, path, out_path, codec, more_opts):
if not self._exes['ffmpeg'] and not self._exes['avconv']:
raise AudioConversionError('ffmpeg or avconv not found. Please install one.')
if codec is None:
acodec_opts = []
else:
acodec_opts = ['-acodec', codec]
cmd = ([self._exes['avconv'] or self._exes['ffmpeg'], '-y', '-i', encodeFilename(path), '-vn']
+ acodec_opts + more_opts +
['--', encodeFilename(out_path)])
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout,stderr = p.communicate()
if p.returncode != 0:
msg = stderr.strip().split('\n')[-1]
raise AudioConversionError(msg)
def run(self, information):
path = information['filepath']
filecodec = self.get_audio_codec(path)
if filecodec is None:
self._downloader.to_stderr(u'WARNING: unable to obtain file audio codec with ffprobe')
return None
more_opts = []
if self._preferredcodec == 'best' or self._preferredcodec == filecodec or (self._preferredcodec == 'm4a' and filecodec == 'aac'):
if self._preferredcodec == 'm4a' and filecodec == 'aac':
# Lossless, but in another container
acodec = 'copy'
extension = self._preferredcodec
more_opts = [self._exes['avconv'] and '-bsf:a' or '-absf', 'aac_adtstoasc']
elif filecodec in ['aac', 'mp3', 'vorbis']:
# Lossless if possible
acodec = 'copy'
extension = filecodec
if filecodec == 'aac':
more_opts = ['-f', 'adts']
if filecodec == 'vorbis':
extension = 'ogg'
else:
# MP3 otherwise.
acodec = 'libmp3lame'
extension = 'mp3'
more_opts = []
if self._preferredquality is not None:
if int(self._preferredquality) < 10:
more_opts += [self._exes['avconv'] and '-q:a' or '-aq', self._preferredquality]
else:
more_opts += [self._exes['avconv'] and '-b:a' or '-ab', self._preferredquality + 'k']
else:
# We convert the audio (lossy)
acodec = {'mp3': 'libmp3lame', 'aac': 'aac', 'm4a': 'aac', 'vorbis': 'libvorbis', 'wav': None}[self._preferredcodec]
extension = self._preferredcodec
more_opts = []
if self._preferredquality is not None:
if int(self._preferredquality) < 10:
more_opts += [self._exes['avconv'] and '-q:a' or '-aq', self._preferredquality]
else:
more_opts += [self._exes['avconv'] and '-b:a' or '-ab', self._preferredquality + 'k']
if self._preferredcodec == 'aac':
more_opts += ['-f', 'adts']
if self._preferredcodec == 'm4a':
more_opts += [self._exes['avconv'] and '-bsf:a' or '-absf', 'aac_adtstoasc']
if self._preferredcodec == 'vorbis':
extension = 'ogg'
if self._preferredcodec == 'wav':
extension = 'wav'
more_opts += ['-f', 'wav']
prefix, sep, ext = path.rpartition(u'.') # not os.path.splitext, since the latter does not work on unicode in all setups
new_path = prefix + sep + extension
self._downloader.to_screen(u'[' + (self._exes['avconv'] and 'avconv' or 'ffmpeg') + '] Destination: ' + new_path)
try:
self.run_ffmpeg(path, new_path, acodec, more_opts)
except:
etype,e,tb = sys.exc_info()
if isinstance(e, AudioConversionError):
self._downloader.to_stderr(u'ERROR: audio conversion failed: ' + e.message)
else:
self._downloader.to_stderr(u'ERROR: error running ' + (self._exes['avconv'] and 'avconv' or 'ffmpeg'))
return None
# Try to update the date time for extracted audio file.
if information.get('filetime') is not None:
try:
os.utime(encodeFilename(new_path), (time.time(), information['filetime']))
except:
self._downloader.to_stderr(u'WARNING: Cannot update utime of audio file')
if not self._keepvideo:
try:
os.remove(encodeFilename(path))
except (IOError, OSError):
self._downloader.to_stderr(u'WARNING: Unable to remove downloaded video file')
return None
information['filepath'] = new_path
return information

602
youtube_dl/__init__.py Normal file
View File

@@ -0,0 +1,602 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import with_statement
from __future__ import absolute_import
__authors__ = (
'Ricardo Garcia Gonzalez',
'Danny Colligan',
'Benjamin Johnson',
'Vasyl\' Vavrychuk',
'Witold Baryluk',
'Paweł Paprota',
'Gergely Imreh',
'Rogério Brito',
'Philipp Hagemeister',
'Sören Schulze',
'Kevin Ngo',
'Ori Avtalion',
'shizeeg',
'Filippo Valsorda',
'Christian Albrecht',
)
__license__ = 'Public Domain'
import getpass
import optparse
import os
import re
import shlex
import socket
import subprocess
import sys
import warnings
from .utils import *
from .version import __version__
from .FileDownloader import *
from .InfoExtractors import *
from .PostProcessor import *
def updateSelf(downloader, filename):
"""Update the program file with the latest version from the repository"""
# TODO: at least, check https certificates
from zipimport import zipimporter
API_URL = "https://api.github.com/repos/rg3/youtube-dl/downloads"
BIN_URL = "https://github.com/downloads/rg3/youtube-dl/youtube-dl"
EXE_URL = "https://github.com/downloads/rg3/youtube-dl/youtube-dl.exe"
if hasattr(sys, "frozen"): # PY2EXE
if not os.access(filename, os.W_OK):
sys.exit('ERROR: no write permissions on %s' % filename)
downloader.to_screen(u'Updating to latest version...')
urla = compat_urllib_request.urlopen(API_URL)
download = filter(lambda x: x["name"] == "youtube-dl.exe", json.loads(urla.read()))
if not download:
downloader.to_screen(u'ERROR: can\'t find the current version. Please try again later.')
return
newversion = download[0]["description"].strip()
if newversion == __version__:
downloader.to_screen(u'youtube-dl is up-to-date (' + __version__ + ')')
return
urla.close()
exe = os.path.abspath(filename)
directory = os.path.dirname(exe)
if not os.access(directory, os.W_OK):
sys.exit('ERROR: no write permissions on %s' % directory)
try:
urlh = compat_urllib_request.urlopen(EXE_URL)
newcontent = urlh.read()
urlh.close()
with open(exe + '.new', 'wb') as outf:
outf.write(newcontent)
except (IOError, OSError) as err:
sys.exit('ERROR: unable to download latest version')
try:
bat = os.path.join(directory, 'youtube-dl-updater.bat')
b = open(bat, 'w')
b.write("""
echo Updating youtube-dl...
ping 127.0.0.1 -n 5 -w 1000 > NUL
move /Y "%s.new" "%s"
del "%s"
\n""" %(exe, exe, bat))
b.close()
os.startfile(bat)
except (IOError, OSError) as err:
sys.exit('ERROR: unable to overwrite current version')
elif isinstance(globals().get('__loader__'), zipimporter): # UNIX ZIP
if not os.access(filename, os.W_OK):
sys.exit('ERROR: no write permissions on %s' % filename)
downloader.to_screen(u'Updating to latest version...')
urla = compat_urllib_request.urlopen(API_URL)
download = [x for x in json.loads(urla.read().decode('utf8')) if x["name"] == "youtube-dl"]
if not download:
downloader.to_screen(u'ERROR: can\'t find the current version. Please try again later.')
return
newversion = download[0]["description"].strip()
if newversion == __version__:
downloader.to_screen(u'youtube-dl is up-to-date (' + __version__ + ')')
return
urla.close()
try:
urlh = compat_urllib_request.urlopen(BIN_URL)
newcontent = urlh.read()
urlh.close()
except (IOError, OSError) as err:
sys.exit('ERROR: unable to download latest version')
try:
with open(filename, 'wb') as outf:
outf.write(newcontent)
except (IOError, OSError) as err:
sys.exit('ERROR: unable to overwrite current version')
else:
downloader.to_screen(u'It looks like you installed youtube-dl with pip or setup.py. Please use that to update.')
return
downloader.to_screen(u'Updated youtube-dl. Restart youtube-dl to use the new version.')
def parseOpts():
def _readOptions(filename_bytes):
try:
optionf = open(filename_bytes)
except IOError:
return [] # silently skip if file is not present
try:
res = []
for l in optionf:
res += shlex.split(l, comments=True)
finally:
optionf.close()
return res
def _format_option_string(option):
''' ('-o', '--option') -> -o, --format METAVAR'''
opts = []
if option._short_opts:
opts.append(option._short_opts[0])
if option._long_opts:
opts.append(option._long_opts[0])
if len(opts) > 1:
opts.insert(1, ', ')
if option.takes_value(): opts.append(' %s' % option.metavar)
return "".join(opts)
def _find_term_columns():
columns = os.environ.get('COLUMNS', None)
if columns:
return int(columns)
try:
sp = subprocess.Popen(['stty', 'size'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out,err = sp.communicate()
return int(out.split()[1])
except:
pass
return None
max_width = 80
max_help_position = 80
# No need to wrap help messages if we're on a wide console
columns = _find_term_columns()
if columns: max_width = columns
fmt = optparse.IndentedHelpFormatter(width=max_width, max_help_position=max_help_position)
fmt.format_option_strings = _format_option_string
kw = {
'version' : __version__,
'formatter' : fmt,
'usage' : '%prog [options] url [url...]',
'conflict_handler' : 'resolve',
}
parser = optparse.OptionParser(**kw)
# option groups
general = optparse.OptionGroup(parser, 'General Options')
selection = optparse.OptionGroup(parser, 'Video Selection')
authentication = optparse.OptionGroup(parser, 'Authentication Options')
video_format = optparse.OptionGroup(parser, 'Video Format Options')
postproc = optparse.OptionGroup(parser, 'Post-processing Options')
filesystem = optparse.OptionGroup(parser, 'Filesystem Options')
verbosity = optparse.OptionGroup(parser, 'Verbosity / Simulation Options')
general.add_option('-h', '--help',
action='help', help='print this help text and exit')
general.add_option('-v', '--version',
action='version', help='print program version and exit')
general.add_option('-U', '--update',
action='store_true', dest='update_self', help='update this program to latest version')
general.add_option('-i', '--ignore-errors',
action='store_true', dest='ignoreerrors', help='continue on download errors', default=False)
general.add_option('-r', '--rate-limit',
dest='ratelimit', metavar='LIMIT', help='download rate limit (e.g. 50k or 44.6m)')
general.add_option('-R', '--retries',
dest='retries', metavar='RETRIES', help='number of retries (default is %default)', default=10)
general.add_option('--buffer-size',
dest='buffersize', metavar='SIZE', help='size of download buffer (e.g. 1024 or 16k) (default is %default)', default="1024")
general.add_option('--no-resize-buffer',
action='store_true', dest='noresizebuffer',
help='do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE.', default=False)
general.add_option('--dump-user-agent',
action='store_true', dest='dump_user_agent',
help='display the current browser identification', default=False)
general.add_option('--user-agent',
dest='user_agent', help='specify a custom user agent', metavar='UA')
general.add_option('--list-extractors',
action='store_true', dest='list_extractors',
help='List all supported extractors and the URLs they would handle', default=False)
general.add_option('--test', action='store_true', dest='test', default=False, help=optparse.SUPPRESS_HELP)
selection.add_option('--playlist-start',
dest='playliststart', metavar='NUMBER', help='playlist video to start at (default is %default)', default=1)
selection.add_option('--playlist-end',
dest='playlistend', metavar='NUMBER', help='playlist video to end at (default is last)', default=-1)
selection.add_option('--match-title', dest='matchtitle', metavar='REGEX',help='download only matching titles (regex or caseless sub-string)')
selection.add_option('--reject-title', dest='rejecttitle', metavar='REGEX',help='skip download for matching titles (regex or caseless sub-string)')
selection.add_option('--max-downloads', metavar='NUMBER', dest='max_downloads', help='Abort after downloading NUMBER files', default=None)
authentication.add_option('-u', '--username',
dest='username', metavar='USERNAME', help='account username')
authentication.add_option('-p', '--password',
dest='password', metavar='PASSWORD', help='account password')
authentication.add_option('-n', '--netrc',
action='store_true', dest='usenetrc', help='use .netrc authentication data', default=False)
video_format.add_option('-f', '--format',
action='store', dest='format', metavar='FORMAT', help='video format code')
video_format.add_option('--all-formats',
action='store_const', dest='format', help='download all available video formats', const='all')
video_format.add_option('--prefer-free-formats',
action='store_true', dest='prefer_free_formats', default=False, help='prefer free video formats unless a specific one is requested')
video_format.add_option('--max-quality',
action='store', dest='format_limit', metavar='FORMAT', help='highest quality format to download')
video_format.add_option('-F', '--list-formats',
action='store_true', dest='listformats', help='list all available formats (currently youtube only)')
video_format.add_option('--write-srt',
action='store_true', dest='writesubtitles',
help='write video closed captions to a .srt file (currently youtube only)', default=False)
video_format.add_option('--srt-lang',
action='store', dest='subtitleslang', metavar='LANG',
help='language of the closed captions to download (optional) use IETF language tags like \'en\'')
verbosity.add_option('-q', '--quiet',
action='store_true', dest='quiet', help='activates quiet mode', default=False)
verbosity.add_option('-s', '--simulate',
action='store_true', dest='simulate', help='do not download the video and do not write anything to disk', default=False)
verbosity.add_option('--skip-download',
action='store_true', dest='skip_download', help='do not download the video', default=False)
verbosity.add_option('-g', '--get-url',
action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
verbosity.add_option('-e', '--get-title',
action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
verbosity.add_option('--get-thumbnail',
action='store_true', dest='getthumbnail',
help='simulate, quiet but print thumbnail URL', default=False)
verbosity.add_option('--get-description',
action='store_true', dest='getdescription',
help='simulate, quiet but print video description', default=False)
verbosity.add_option('--get-filename',
action='store_true', dest='getfilename',
help='simulate, quiet but print output filename', default=False)
verbosity.add_option('--get-format',
action='store_true', dest='getformat',
help='simulate, quiet but print output format', default=False)
verbosity.add_option('--no-progress',
action='store_true', dest='noprogress', help='do not print progress bar', default=False)
verbosity.add_option('--console-title',
action='store_true', dest='consoletitle',
help='display progress in console titlebar', default=False)
verbosity.add_option('-v', '--verbose',
action='store_true', dest='verbose', help='print various debugging information', default=False)
filesystem.add_option('-t', '--title',
action='store_true', dest='usetitle', help='use title in file name', default=False)
filesystem.add_option('--id',
action='store_true', dest='useid', help='use video ID in file name', default=False)
filesystem.add_option('-l', '--literal',
action='store_true', dest='usetitle', help='[deprecated] alias of --title', default=False)
filesystem.add_option('-A', '--auto-number',
action='store_true', dest='autonumber',
help='number downloaded files starting from 00000', default=False)
filesystem.add_option('-o', '--output',
dest='outtmpl', metavar='TEMPLATE', help='output filename template. Use %(title)s to get the title, %(uploader)s for the uploader name, %(autonumber)s to get an automatically incremented number, %(ext)s for the filename extension, %(upload_date)s for the upload date (YYYYMMDD), %(extractor)s for the provider (youtube, metacafe, etc), %(id)s for the video id and %% for a literal percent. Use - to output to stdout. Can also be used to download to a different directory, for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .')
filesystem.add_option('--restrict-filenames',
action='store_true', dest='restrictfilenames',
help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False)
filesystem.add_option('-a', '--batch-file',
dest='batchfile', metavar='FILE', help='file containing URLs to download (\'-\' for stdin)')
filesystem.add_option('-w', '--no-overwrites',
action='store_true', dest='nooverwrites', help='do not overwrite files', default=False)
filesystem.add_option('-c', '--continue',
action='store_true', dest='continue_dl', help='resume partially downloaded files', default=True)
filesystem.add_option('--no-continue',
action='store_false', dest='continue_dl',
help='do not resume partially downloaded files (restart from beginning)')
filesystem.add_option('--cookies',
dest='cookiefile', metavar='FILE', help='file to read cookies from and dump cookie jar in')
filesystem.add_option('--no-part',
action='store_true', dest='nopart', help='do not use .part files', default=False)
filesystem.add_option('--no-mtime',
action='store_false', dest='updatetime',
help='do not use the Last-modified header to set the file modification time', default=True)
filesystem.add_option('--write-description',
action='store_true', dest='writedescription',
help='write video description to a .description file', default=False)
filesystem.add_option('--write-info-json',
action='store_true', dest='writeinfojson',
help='write video metadata to a .info.json file', default=False)
postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False,
help='convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe)')
postproc.add_option('--audio-format', metavar='FORMAT', dest='audioformat', default='best',
help='"best", "aac", "vorbis", "mp3", "m4a", or "wav"; best by default')
postproc.add_option('--audio-quality', metavar='QUALITY', dest='audioquality', default='5',
help='ffmpeg/avconv audio quality specification, insert a value between 0 (better) and 9 (worse) for VBR or a specific bitrate like 128K (default 5)')
postproc.add_option('-k', '--keep-video', action='store_true', dest='keepvideo', default=False,
help='keeps the video file on disk after the post-processing; the video is erased by default')
parser.add_option_group(general)
parser.add_option_group(selection)
parser.add_option_group(filesystem)
parser.add_option_group(verbosity)
parser.add_option_group(video_format)
parser.add_option_group(authentication)
parser.add_option_group(postproc)
xdg_config_home = os.environ.get('XDG_CONFIG_HOME')
if xdg_config_home:
userConf = os.path.join(xdg_config_home, 'youtube-dl.conf')
else:
userConf = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl.conf')
argv = _readOptions('/etc/youtube-dl.conf') + _readOptions(userConf) + sys.argv[1:]
opts, args = parser.parse_args(argv)
return parser, opts, args
def gen_extractors():
""" Return a list of an instance of every supported extractor.
The order does matter; the first extractor matched is the one handling the URL.
"""
return [
YoutubePlaylistIE(),
YoutubeChannelIE(),
YoutubeUserIE(),
YoutubeSearchIE(),
YoutubeIE(),
MetacafeIE(),
DailymotionIE(),
GoogleIE(),
GoogleSearchIE(),
PhotobucketIE(),
YahooIE(),
YahooSearchIE(),
DepositFilesIE(),
FacebookIE(),
BlipTVUserIE(),
BlipTVIE(),
VimeoIE(),
MyVideoIE(),
ComedyCentralIE(),
EscapistIE(),
CollegeHumorIE(),
XVideosIE(),
SoundcloudIE(),
InfoQIE(),
MixcloudIE(),
StanfordOpenClassroomIE(),
MTVIE(),
YoukuIE(),
XNXXIE(),
GooglePlusIE(),
ArteTvIE(),
GenericIE()
]
def _real_main():
parser, opts, args = parseOpts()
# Open appropriate CookieJar
if opts.cookiefile is None:
jar = compat_cookiejar.CookieJar()
else:
try:
jar = compat_cookiejar.MozillaCookieJar(opts.cookiefile)
if os.path.isfile(opts.cookiefile) and os.access(opts.cookiefile, os.R_OK):
jar.load()
except (IOError, OSError) as err:
sys.exit(u'ERROR: unable to open cookie file')
# Set user agent
if opts.user_agent is not None:
std_headers['User-Agent'] = opts.user_agent
# Dump user agent
if opts.dump_user_agent:
print(std_headers['User-Agent'])
sys.exit(0)
# Batch file verification
batchurls = []
if opts.batchfile is not None:
try:
if opts.batchfile == '-':
batchfd = sys.stdin
else:
batchfd = open(opts.batchfile, 'r')
batchurls = batchfd.readlines()
batchurls = [x.strip() for x in batchurls]
batchurls = [x for x in batchurls if len(x) > 0 and not re.search(r'^[#/;]', x)]
except IOError:
sys.exit(u'ERROR: batch file could not be read')
all_urls = batchurls + args
all_urls = [url.strip() for url in all_urls]
# General configuration
cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
proxy_handler = compat_urllib_request.ProxyHandler()
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
compat_urllib_request.install_opener(opener)
socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
extractors = gen_extractors()
if opts.list_extractors:
for ie in extractors:
print(ie.IE_NAME + (' (CURRENTLY BROKEN)' if not ie._WORKING else ''))
matchedUrls = filter(lambda url: ie.suitable(url), all_urls)
all_urls = filter(lambda url: url not in matchedUrls, all_urls)
for mu in matchedUrls:
print(u' ' + mu)
sys.exit(0)
# Conflicting, missing and erroneous options
if opts.usenetrc and (opts.username is not None or opts.password is not None):
parser.error(u'using .netrc conflicts with giving username/password')
if opts.password is not None and opts.username is None:
parser.error(u'account username missing')
if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):
parser.error(u'using output template conflicts with using title, video ID or auto number')
if opts.usetitle and opts.useid:
parser.error(u'using title conflicts with using video ID')
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:
parser.error(u'invalid rate limit specified')
opts.ratelimit = numeric_limit
if opts.retries is not None:
try:
opts.retries = int(opts.retries)
except (TypeError, ValueError) as err:
parser.error(u'invalid retry count specified')
if opts.buffersize is not None:
numeric_buffersize = FileDownloader.parse_bytes(opts.buffersize)
if numeric_buffersize is None:
parser.error(u'invalid buffer size specified')
opts.buffersize = numeric_buffersize
try:
opts.playliststart = int(opts.playliststart)
if opts.playliststart <= 0:
raise ValueError(u'Playlist start must be positive')
except (TypeError, ValueError) as err:
parser.error(u'invalid playlist start number specified')
try:
opts.playlistend = int(opts.playlistend)
if opts.playlistend != -1 and (opts.playlistend <= 0 or opts.playlistend < opts.playliststart):
raise ValueError(u'Playlist end must be greater than playlist start')
except (TypeError, ValueError) as err:
parser.error(u'invalid playlist end number specified')
if opts.extractaudio:
if opts.audioformat not in ['best', 'aac', 'mp3', 'vorbis', 'm4a', 'wav']:
parser.error(u'invalid audio format specified')
if opts.audioquality:
opts.audioquality = opts.audioquality.strip('k').strip('K')
if not opts.audioquality.isdigit():
parser.error(u'invalid audio quality specified')
# File downloader
fd = FileDownloader({
'usenetrc': opts.usenetrc,
'username': opts.username,
'password': opts.password,
'quiet': (opts.quiet or opts.geturl or opts.gettitle or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
'forceurl': opts.geturl,
'forcetitle': opts.gettitle,
'forcethumbnail': opts.getthumbnail,
'forcedescription': opts.getdescription,
'forcefilename': opts.getfilename,
'forceformat': opts.getformat,
'simulate': opts.simulate,
'skip_download': (opts.skip_download or opts.simulate or opts.geturl or opts.gettitle or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
'format': opts.format,
'format_limit': opts.format_limit,
'listformats': opts.listformats,
'outtmpl': ((opts.outtmpl is not None and opts.outtmpl.decode(preferredencoding()))
or (opts.format == '-1' and opts.usetitle and u'%(title)s-%(id)s-%(format)s.%(ext)s')
or (opts.format == '-1' and u'%(id)s-%(format)s.%(ext)s')
or (opts.usetitle and opts.autonumber and u'%(autonumber)s-%(title)s-%(id)s.%(ext)s')
or (opts.usetitle and u'%(title)s-%(id)s.%(ext)s')
or (opts.useid and u'%(id)s.%(ext)s')
or (opts.autonumber and u'%(autonumber)s-%(id)s.%(ext)s')
or u'%(id)s.%(ext)s'),
'restrictfilenames': opts.restrictfilenames,
'ignoreerrors': opts.ignoreerrors,
'ratelimit': opts.ratelimit,
'nooverwrites': opts.nooverwrites,
'retries': opts.retries,
'buffersize': opts.buffersize,
'noresizebuffer': opts.noresizebuffer,
'continuedl': opts.continue_dl,
'noprogress': opts.noprogress,
'playliststart': opts.playliststart,
'playlistend': opts.playlistend,
'logtostderr': opts.outtmpl == '-',
'consoletitle': opts.consoletitle,
'nopart': opts.nopart,
'updatetime': opts.updatetime,
'writedescription': opts.writedescription,
'writeinfojson': opts.writeinfojson,
'writesubtitles': opts.writesubtitles,
'subtitleslang': opts.subtitleslang,
'matchtitle': opts.matchtitle,
'rejecttitle': opts.rejecttitle,
'max_downloads': opts.max_downloads,
'prefer_free_formats': opts.prefer_free_formats,
'verbose': opts.verbose,
'test': opts.test,
})
if opts.verbose:
fd.to_screen(u'[debug] Proxy map: ' + str(proxy_handler.proxies))
for extractor in extractors:
fd.add_info_extractor(extractor)
# PostProcessors
if opts.extractaudio:
fd.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, keepvideo=opts.keepvideo))
# Update version
if opts.update_self:
updateSelf(fd, sys.argv[0])
# Maybe do nothing
if len(all_urls) < 1:
if not opts.update_self:
parser.error(u'you must provide at least one URL')
else:
sys.exit()
try:
retcode = fd.download(all_urls)
except MaxDownloadsReached:
fd.to_screen(u'--max-download limit reached, aborting.')
retcode = 101
# Dump cookie jar if requested
if opts.cookiefile is not None:
try:
jar.save()
except (IOError, OSError) as err:
sys.exit(u'ERROR: unable to save cookie jar')
sys.exit(retcode)
def main():
try:
_real_main()
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')

17
youtube_dl/__main__.py Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env python
# Execute with
# $ python youtube_dl/__main__.py (2.6+)
# $ python -m youtube_dl (2.7+)
import sys
if __package__ is None and not hasattr(sys, "frozen"):
# direct call of __main__.py
import os.path
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import youtube_dl
if __name__ == '__main__':
youtube_dl.main()

511
youtube_dl/utils.py Normal file
View File

@@ -0,0 +1,511 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import gzip
import io
import locale
import os
import re
import sys
import zlib
import email.utils
import json
try:
import urllib.request as compat_urllib_request
except ImportError: # Python 2
import urllib2 as compat_urllib_request
try:
import urllib.error as compat_urllib_error
except ImportError: # Python 2
import urllib2 as compat_urllib_error
try:
import urllib.parse as compat_urllib_parse
except ImportError: # Python 2
import urllib as compat_urllib_parse
try:
from urllib.parse import urlparse as compat_urllib_parse_urlparse
except ImportError: # Python 2
from urlparse import urlparse as compat_urllib_parse_urlparse
try:
import http.cookiejar as compat_cookiejar
except ImportError: # Python 2
import cookielib as compat_cookiejar
try:
import html.entities as compat_html_entities
except ImportError: # Python 2
import htmlentitydefs as compat_html_entities
try:
import html.parser as compat_html_parser
except ImportError: # Python 2
import HTMLParser as compat_html_parser
try:
import http.client as compat_http_client
except ImportError: # Python 2
import httplib as compat_http_client
try:
from urllib.parse import parse_qs as compat_parse_qs
except ImportError: # Python 2
# HACK: The following is the correct parse_qs implementation from cpython 3's stdlib.
# Python 2's version is apparently totally broken
def _unquote(string, encoding='utf-8', errors='replace'):
if string == '':
return string
res = string.split('%')
if len(res) == 1:
return string
if encoding is None:
encoding = 'utf-8'
if errors is None:
errors = 'replace'
# pct_sequence: contiguous sequence of percent-encoded bytes, decoded
pct_sequence = b''
string = res[0]
for item in res[1:]:
try:
if not item:
raise ValueError
pct_sequence += item[:2].decode('hex')
rest = item[2:]
if not rest:
# This segment was just a single percent-encoded character.
# May be part of a sequence of code units, so delay decoding.
# (Stored in pct_sequence).
continue
except ValueError:
rest = '%' + item
# Encountered non-percent-encoded characters. Flush the current
# pct_sequence.
string += pct_sequence.decode(encoding, errors) + rest
pct_sequence = b''
if pct_sequence:
# Flush the final pct_sequence
string += pct_sequence.decode(encoding, errors)
return string
def _parse_qsl(qs, keep_blank_values=False, strict_parsing=False,
encoding='utf-8', errors='replace'):
qs, _coerce_result = qs, unicode
pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
r = []
for name_value in pairs:
if not name_value and not strict_parsing:
continue
nv = name_value.split('=', 1)
if len(nv) != 2:
if strict_parsing:
raise ValueError("bad query field: %r" % (name_value,))
# Handle case of a control-name with no equal sign
if keep_blank_values:
nv.append('')
else:
continue
if len(nv[1]) or keep_blank_values:
name = nv[0].replace('+', ' ')
name = _unquote(name, encoding=encoding, errors=errors)
name = _coerce_result(name)
value = nv[1].replace('+', ' ')
value = _unquote(value, encoding=encoding, errors=errors)
value = _coerce_result(value)
r.append((name, value))
return r
def compat_parse_qs(qs, keep_blank_values=False, strict_parsing=False,
encoding='utf-8', errors='replace'):
parsed_result = {}
pairs = _parse_qsl(qs, keep_blank_values, strict_parsing,
encoding=encoding, errors=errors)
for name, value in pairs:
if name in parsed_result:
parsed_result[name].append(value)
else:
parsed_result[name] = [value]
return parsed_result
try:
compat_str = unicode # Python 2
except NameError:
compat_str = str
try:
compat_chr = unichr # Python 2
except NameError:
compat_chr = chr
std_headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0',
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'en-us,en;q=0.5',
}
def preferredencoding():
"""Get preferred encoding.
Returns the best encoding scheme for the system, based on
locale.getpreferredencoding() and some further tweaks.
"""
try:
pref = locale.getpreferredencoding()
u'TEST'.encode(pref)
except:
pref = 'UTF-8'
return pref
if sys.version_info < (3,0):
def compat_print(s):
print(s.encode(preferredencoding(), 'xmlcharrefreplace'))
else:
def compat_print(s):
assert type(s) == type(u'')
print(s)
def htmlentity_transform(matchobj):
"""Transforms an HTML entity to a character.
This function receives a match object and is intended to be used with
the re.sub() function.
"""
entity = matchobj.group(1)
# Known non-numeric HTML entity
if entity in compat_html_entities.name2codepoint:
return compat_chr(compat_html_entities.name2codepoint[entity])
mobj = re.match(u'(?u)#(x?\\d+)', entity)
if mobj is not None:
numstr = mobj.group(1)
if numstr.startswith(u'x'):
base = 16
numstr = u'0%s' % numstr
else:
base = 10
return compat_chr(int(numstr, base))
# Unknown entity in name, return its literal representation
return (u'&%s;' % entity)
compat_html_parser.locatestarttagend = re.compile(r"""<[a-zA-Z][-.a-zA-Z0-9:_]*(?:\s+(?:(?<=['"\s])[^\s/>][^\s/=>]*(?:\s*=+\s*(?:'[^']*'|"[^"]*"|(?!['"])[^>\s]*))?\s*)*)?\s*""", re.VERBOSE) # backport bugfix
class IDParser(compat_html_parser.HTMLParser):
"""Modified HTMLParser that isolates a tag with the specified id"""
def __init__(self, id):
self.id = id
self.result = None
self.started = False
self.depth = {}
self.html = None
self.watch_startpos = False
self.error_count = 0
compat_html_parser.HTMLParser.__init__(self)
def error(self, message):
if self.error_count > 10 or self.started:
raise compat_html_parser.HTMLParseError(message, self.getpos())
self.rawdata = '\n'.join(self.html.split('\n')[self.getpos()[0]:]) # skip one line
self.error_count += 1
self.goahead(1)
def loads(self, html):
self.html = html
self.feed(html)
self.close()
def handle_starttag(self, tag, attrs):
attrs = dict(attrs)
if self.started:
self.find_startpos(None)
if 'id' in attrs and attrs['id'] == self.id:
self.result = [tag]
self.started = True
self.watch_startpos = True
if self.started:
if not tag in self.depth: self.depth[tag] = 0
self.depth[tag] += 1
def handle_endtag(self, tag):
if self.started:
if tag in self.depth: self.depth[tag] -= 1
if self.depth[self.result[0]] == 0:
self.started = False
self.result.append(self.getpos())
def find_startpos(self, x):
"""Needed to put the start position of the result (self.result[1])
after the opening tag with the requested id"""
if self.watch_startpos:
self.watch_startpos = False
self.result.append(self.getpos())
handle_entityref = handle_charref = handle_data = handle_comment = \
handle_decl = handle_pi = unknown_decl = find_startpos
def get_result(self):
if self.result is None:
return None
if len(self.result) != 3:
return None
lines = self.html.split('\n')
lines = lines[self.result[1][0]-1:self.result[2][0]]
lines[0] = lines[0][self.result[1][1]:]
if len(lines) == 1:
lines[-1] = lines[-1][:self.result[2][1]-self.result[1][1]]
lines[-1] = lines[-1][:self.result[2][1]]
return '\n'.join(lines).strip()
def get_element_by_id(id, html):
"""Return the content of the tag with the specified id in the passed HTML document"""
parser = IDParser(id)
try:
parser.loads(html)
except compat_html_parser.HTMLParseError:
pass
return parser.get_result()
def clean_html(html):
"""Clean an HTML snippet into a readable string"""
# Newline vs <br />
html = html.replace('\n', ' ')
html = re.sub('\s*<\s*br\s*/?\s*>\s*', '\n', html)
# Strip html tags
html = re.sub('<.*?>', '', html)
# Replace html entities
html = unescapeHTML(html)
return html
def sanitize_open(filename, open_mode):
"""Try to open the given filename, and slightly tweak it if this fails.
Attempts to open the given filename. If this fails, it tries to change
the filename slightly, step by step, until it's either able to open it
or it fails and raises a final exception, like the standard open()
function.
It returns the tuple (stream, definitive_file_name).
"""
try:
if filename == u'-':
if sys.platform == 'win32':
import msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
return (sys.stdout, filename)
stream = open(encodeFilename(filename), open_mode)
return (stream, filename)
except (IOError, OSError) as err:
# In case of error, try to remove win32 forbidden chars
filename = re.sub(u'[/<>:"\\|\\\\?\\*]', u'#', filename)
# An exception here should be caught in the caller
stream = open(encodeFilename(filename), open_mode)
return (stream, filename)
def timeconvert(timestr):
"""Convert RFC 2822 defined time string into system timestamp"""
timestamp = None
timetuple = email.utils.parsedate_tz(timestr)
if timetuple is not None:
timestamp = email.utils.mktime_tz(timetuple)
return timestamp
def sanitize_filename(s, restricted=False, is_id=False):
"""Sanitizes a string so it could be used as part of a filename.
If restricted is set, use a stricter subset of allowed characters.
Set is_id if this is not an arbitrary string, but an ID that should be kept if possible
"""
def replace_insane(char):
if char == '?' or ord(char) < 32 or ord(char) == 127:
return ''
elif char == '"':
return '' if restricted else '\''
elif char == ':':
return '_-' if restricted else ' -'
elif char in '\\/|*<>':
return '_'
if restricted and (char in '!&\'()[]{}$;`^,#' or char.isspace()):
return '_'
if restricted and ord(char) > 127:
return '_'
return char
result = u''.join(map(replace_insane, s))
if not is_id:
while '__' in result:
result = result.replace('__', '_')
result = result.strip('_')
# Common case of "Foreign band name - English song title"
if restricted and result.startswith('-_'):
result = result[2:]
if not result:
result = '_'
return result
def orderedSet(iterable):
""" Remove all duplicates from the input iterable """
res = []
for el in iterable:
if el not in res:
res.append(el)
return res
def unescapeHTML(s):
"""
@param s a string
"""
assert type(s) == type(u'')
result = re.sub(u'(?u)&(.+?);', htmlentity_transform, s)
return result
def encodeFilename(s):
"""
@param s The name of the file
"""
assert type(s) == type(u'')
# Python 3 has a Unicode API
if sys.version_info >= (3, 0):
return s
if sys.platform == 'win32' and sys.getwindowsversion()[0] >= 5:
# Pass u'' directly to use Unicode APIs on Windows 2000 and up
# (Detecting Windows NT 4 is tricky because 'major >= 4' would
# match Windows 9x series as well. Besides, NT 4 is obsolete.)
return s
else:
return s.encode(sys.getfilesystemencoding(), 'ignore')
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 MaxDownloadsReached(Exception):
""" --max-downloads limit has been reached. """
pass
class UnavailableVideoError(Exception):
"""Unavailable Format exception.
This exception will be thrown when a video is requested
in a format that is not available for that video.
"""
pass
class ContentTooShortError(Exception):
"""Content Too Short exception.
This exception may be raised by FileDownloader objects when a file they
download is too small for what the server announced first, indicating
the connection was probably interrupted.
"""
# Both in bytes
downloaded = None
expected = None
def __init__(self, downloaded, expected):
self.downloaded = downloaded
self.expected = expected
class Trouble(Exception):
"""Trouble helper exception
This is an exception to be handled with
FileDownloader.trouble
"""
class YoutubeDLHandler(compat_urllib_request.HTTPHandler):
"""Handler for HTTP requests and responses.
This class, when installed with an OpenerDirector, automatically adds
the standard headers to every HTTP request and handles gzipped and
deflated responses from web servers. If compression is to be avoided in
a particular request, the original request in the program code only has
to include the HTTP header "Youtubedl-No-Compression", which will be
removed before making the real request.
Part of this code was copied from:
http://techknack.net/python-urllib2-handlers/
Andrew Rowls, the author of that code, agreed to release it to the
public domain.
"""
@staticmethod
def deflate(data):
try:
return zlib.decompress(data, -zlib.MAX_WBITS)
except zlib.error:
return zlib.decompress(data)
@staticmethod
def addinfourl_wrapper(stream, headers, url, code):
if hasattr(compat_urllib_request.addinfourl, 'getcode'):
return compat_urllib_request.addinfourl(stream, headers, url, code)
ret = compat_urllib_request.addinfourl(stream, headers, url)
ret.code = code
return ret
def http_request(self, req):
for h in std_headers:
if h in req.headers:
del req.headers[h]
req.add_header(h, std_headers[h])
if 'Youtubedl-no-compression' in req.headers:
if 'Accept-encoding' in req.headers:
del req.headers['Accept-encoding']
del req.headers['Youtubedl-no-compression']
return req
def http_response(self, req, resp):
old_resp = resp
# gzip
if resp.headers.get('Content-encoding', '') == 'gzip':
gz = gzip.GzipFile(fileobj=io.BytesIO(resp.read()), mode='r')
resp = self.addinfourl_wrapper(gz, old_resp.headers, old_resp.url, old_resp.code)
resp.msg = old_resp.msg
# deflate
if resp.headers.get('Content-encoding', '') == 'deflate':
gz = io.BytesIO(self.deflate(resp.read()))
resp = self.addinfourl_wrapper(gz, old_resp.headers, old_resp.url, old_resp.code)
resp.msg = old_resp.msg
return resp
https_request = http_request
https_response = http_response

2
youtube_dl/version.py Normal file
View File

@@ -0,0 +1,2 @@
__version__ = '2012.12.11'