Compare commits

..

975 Commits

Author SHA1 Message Date
085c8b75a6 release 2013.02.02 2013-02-02 14:45:38 +01:00
dbf2ba3d61 Better help for new options 2013-02-02 14:44:22 +01:00
b47bbac393 Disable Stanford OC test for now, and enable escapist 2013-02-02 14:40:41 +01:00
229cac754a Improve cookie error handling 2013-02-02 13:51:54 +01:00
0e33684194 Switch to m4a by default (Closes #240) 2013-02-01 18:23:20 +01:00
9e982f9e4e Added "min-filesize" and "max-filesize" options 2013-02-01 18:09:34 +01:00
c7a725cfad Merge remote-tracking branch 'dcoppa/master' 2013-02-01 18:05:42 +01:00
450a30cae8 Add PyPi upload to release script 2013-02-01 18:01:53 +01:00
9cd5e4fce8 release 2013.02.01 2013-02-01 17:57:32 +01:00
edba5137b8 Fix Facebook IE 2013-02-01 17:56:22 +01:00
233a22960a Switch ComedyCentral test to a permanent URL (They delete full episodes older than a month) 2013-02-01 17:46:03 +01:00
3b024e17af Work around buggy HTML Parser in Python < 2.7.3 (Closes #662) 2013-02-01 17:29:50 +01:00
a32b573ccb Try setuptools first, then fallback to distutils.core 2013-01-30 15:31:38 +01:00
ec71c13ab8 release 2013.01.28 2013-01-27 18:33:58 +01:00
f0bad2b026 Fix Stanford (Closes #653) 2013-01-27 15:23:26 +01:00
25580f3251 8tracks: Ignore hashes 2013-01-27 04:15:12 +01:00
da4de959df 8tracks: Better default titles 2013-01-27 04:05:53 +01:00
d0d51a8afa 8tracks: Include performer as uploader 2013-01-27 03:27:46 +01:00
c67598c3e1 Remove space before shebang 2013-01-27 03:07:07 +01:00
811d253bc2 Merge remote-tracking branch 'jaimeMF/makefilePythonversion' 2013-01-27 03:06:32 +01:00
c3a1642ead release 2013.01.27 2013-01-27 03:03:02 +01:00
ccf65f9dee 8tracks IE (Closes #652) 2013-01-27 03:01:23 +01:00
b954070d70 Fix Facebook (Closes #375) 2013-01-25 16:54:48 +01:00
30e9f4496b Drop md5: spec for now (unused and breaks int values) 2013-01-25 16:54:25 +01:00
271d3fbdaa Option in makefile to select python interpreter 2013-01-25 15:11:03 +01:00
6df40dcbe0 Guard against sys.getfilesystemencoding() == None (#503) 2013-01-20 01:48:05 +01:00
97f194c1fb twitch.tv: Use id as title if no title is present (Closes #638) 2013-01-16 09:55:45 +01:00
4da769ccca Do not backup version.py (under version control and frankly, not that complex) 2013-01-12 23:04:46 +01:00
253d96f2e2 Force build removal 2013-01-12 22:25:54 +01:00
bbc3e2753a release 2013.01.13 2013-01-12 22:18:13 +01:00
67353612ba Revert "Move update to front"
This reverts commit db30f02b50.
2013-01-12 22:10:36 +01:00
bffbd5f038 Download progress hooks 2013-01-12 20:34:50 +01:00
d8bbf2018e Aggressive test timeout to catch hanging servers 2013-01-12 20:33:03 +01:00
187f491ad2 [RBMA] Do not fail if thumbnail is empty 2013-01-12 18:45:50 +01:00
335959e778 Correct Blip.tv on 2.6, where HTTP headers are case-sensitive (wtf?) 2013-01-12 18:38:23 +01:00
3b83bf8f6a correct pushes in release script 2013-01-12 18:37:21 +01:00
51719893bf Default to py3 in sign-versions 2013-01-12 18:14:07 +01:00
1841f65e64 Python 2-proof versions.py 2013-01-12 18:12:24 +01:00
bb28998920 fix location of updates_key in devscripts/release 2013-01-12 18:07:31 +01:00
fbc5f99db9 release 2013.01.12 2013-01-12 17:59:58 +01:00
ca0a0bbeec RBMA IE (Closes #630) 2013-01-12 17:58:39 +01:00
6119f78cb9 Add location field 2013-01-12 17:34:31 +01:00
539679c7f9 Make uploader and upload_date fields optional 2013-01-12 17:34:09 +01:00
b642cd44c1 restore youtube-dl (update) binary 2013-01-12 17:07:12 +01:00
fffec3b9d9 Credit jefftimesten for YouPornIE, PornoTubeIE, YouJizzIE 2013-01-12 16:51:20 +01:00
3446dfb7cb Proper support for changing User-Agents from IEs 2013-01-12 16:49:13 +01:00
db16276b7c Improve YouJizz 2013-01-12 16:41:04 +01:00
629fcdd135 Add agecheck and various improvements to YouPorn IE 2013-01-12 16:10:35 +01:00
64ce2aada8 _request_webpage helper methods for queries that need the final URL 2013-01-12 16:10:16 +01:00
565f751967 Clean up porno IEs 2013-01-12 15:17:04 +01:00
6017964580 Merge remote-tracking branch 'jefftimesten/master' 2013-01-12 15:12:50 +01:00
1d16b0c3fe Keep file without any PPs (oops, missed the obvious case) 2013-01-12 15:12:28 +01:00
7851b37993 --recode-video option (Closes #18) 2013-01-12 15:09:09 +01:00
d81edc573e Merge 'jaimeMF/videoconversion' (sans actual option for now) 2013-01-12 14:04:30 +01:00
ef0c8d5f9f Make ustream IE more robust 2013-01-12 13:49:14 +01:00
db30f02b50 Move update to front 2013-01-12 13:45:39 +01:00
4ba7262467 Less confusing player version 2013-01-12 13:35:16 +01:00
67d0c25eab Add a PostProcessor for converting video format 2013-01-11 20:50:49 +01:00
09f9552b40 Less git acrobatics in devscripts/release.sh 2013-01-11 08:28:37 +01:00
142d38f776 release 2013.01.11 2013-01-11 08:05:30 +01:00
6dd3471900 Add Makefile in tarball (Closes #626) 2013-01-11 08:00:27 +01:00
280d67896a Correct documentation (Closes #625) 2013-01-10 23:20:26 +01:00
510e6f6dc1 Support --audio-format=opus 2013-01-10 19:15:04 +01:00
712e86b999 Fix broken ffmpeg (Closes #623) 2013-01-09 14:46:19 +01:00
74fdba620d release 2013.01.08 2013-01-08 10:29:53 +01:00
dc1c479a6f Merge pull request #621 from atomizer/master
justin.tv tweaks
2013-01-08 00:57:46 -08:00
119d536e07 Merge branch 'my-origin/master' 2013-01-07 17:03:58 +04:00
fa1bf9c653 justin.tv tweaks
- download all parts of a broadcast, fixes #614
- set "uploader" variable to channel_name if available
- catch api errors even if http status is 200
2013-01-07 16:59:39 +04:00
814eed0ea1 Fix tar target (--exclude-vcs is not supported everywhere, and reading . while writing to it can fail randomly) 2013-01-07 12:48:07 +01:00
0aa3068e9e Do not check in test_coverage 2013-01-06 23:38:36 +01:00
db2d6124b1 correct quoting 2013-01-06 23:14:56 +01:00
039dc61bd2 Simplify Makefile 2013-01-06 23:02:31 +01:00
4b879984ea release 2013.01.06 2013-01-06 22:52:04 +01:00
55e286ba55 read -n is bash-specific 2013-01-06 22:50:20 +01:00
9450bfa26e fixed tests (used the --test option) so that they pass. go figure 2013-01-06 16:33:37 -05:00
18be482a6f oops - didn't remove some reminders 2013-01-06 15:52:33 -05:00
ca6710ee41 made changes recommended in pull request 2013-01-06 15:40:50 -05:00
9314810243 fix ComedyCentral IE in Python3 2013-01-06 21:36:01 +01:00
7717ae19fa Add tests for ComedyCentral IE 2013-01-06 21:35:20 +01:00
32635ec685 Switch comedycentral IE to http downloads 2013-01-06 21:26:31 +01:00
caec7618a1 re-fixed XNXX regex problem 2013-01-05 16:05:23 -05:00
7e7ab2815c Merge branch 'master' of https://github.com/jefftimesten/youtube-dl 2013-01-05 16:01:03 -05:00
d7744f2219 Merge branch 'master' of https://github.com/jefftimesten/youtube-dl 2013-01-05 16:00:50 -05:00
7161829de5 Merge branch 'master' of https://github.com/jefftimesten/youtube-dl 2013-01-05 15:59:28 -05:00
991ba7fae3 Added extractors for 3 porn sites 2013-01-05 15:59:01 -05:00
a7539296ce Added extractors for 3 porn sites 2013-01-05 15:42:35 -05:00
258d5850c9 Merge branch 'master' of https://github.com/rg3/youtube-dl
Conflicts:
	.gitignore
	LATEST_VERSION
	Makefile
	youtube-dl
	youtube-dl.exe
	youtube_dl/InfoExtractors.py
	youtube_dl/__init__.py
2013-01-05 15:03:54 -05:00
20759b340a Disable travis irc notifications
travis is much to verbose for that, with random IEs constantly failing
2013-01-04 00:34:02 +01:00
8e5f761870 Merge pull request #617 from jaimeMF/steamIE
[steamIE]Allow downloading videos with other characters in their titles
2013-01-03 15:16:27 -08:00
26714799c9 steamIE remove the HTMLparser object 2013-01-03 23:56:02 +01:00
5e9d042d8f steamIE follow @phihag suggestions 2013-01-03 23:51:48 +01:00
9cf98a2bcc Allow downloading videos with other characters in their titles
Especially html entities
2013-01-03 21:17:35 +01:00
f5ebb61495 Support page URL in RTMP downloads 2013-01-03 20:26:38 +01:00
431d88dd31 Also generate SHA2-256 2013-01-03 19:49:06 +01:00
876f1a86af Also publish hashsums 2013-01-03 19:18:55 +01:00
01951dda7a Make ExtractorError usable for other causes 2013-01-03 15:39:55 +01:00
6e3dba168b release.sh edits based on 2013.01.02 experience 2013-01-02 23:40:24 +01:00
d851e895d5 release 2013.01.02 2013-01-02 22:21:45 +01:00
b962b76f43 re-worked release workflow, it is one-step and creates GPG signatures now 2013-01-02 21:52:27 +01:00
26cf040827 Support youtube videos of google+ users 2013-01-02 19:12:44 +01:00
8e241d1a1a Simplify DailyMotion IE 2013-01-01 21:22:30 +01:00
3a648b209c Remove .part files before and after tests 2013-01-01 21:16:03 +01:00
c80f0a417a Better name for InfoQ IE 2013-01-01 21:10:45 +01:00
4fcca4bb18 Fix infoQ in Python3 2013-01-01 21:07:37 +01:00
511eda8eda add test for infoq 2013-01-01 21:01:49 +01:00
5f9551719c Simplify some IEs 2013-01-01 20:52:59 +01:00
d830b7c297 _download_webpage helper function 2013-01-01 20:43:43 +01:00
1c256f7047 ExtractorError for errors during extraction 2013-01-01 20:27:53 +01:00
a34dd63beb Remove superfluous IE names 2013-01-01 19:40:48 +01:00
4aeae91f86 Move gen_extractors to InfoExtractors 2013-01-01 19:37:07 +01:00
c073e35b1e Simplify test parameter initialization 2013-01-01 19:34:54 +01:00
5c892b0ba9 Adapt test_download to support playlists, and remove race conditions 2013-01-01 19:30:29 +01:00
6985325e01 Revert "In tests.json file and md5 join in a 'files' list to handle multiple-file IEs"
This made the JSON structure really unreadable and was a quick fix.

This reverts commit 6535e9511f.
2013-01-01 19:07:06 +01:00
911ee27e83 typo 2013-01-01 19:07:01 +01:00
2069acc6a4 credit @jaimeMF 2013-01-01 18:29:43 +01:00
278986ea0f ustreamIE 2013-01-01 18:14:20 +01:00
6535e9511f In tests.json file and md5 join in a 'files' list to handle multiple-file IEs 2013-01-01 16:07:26 +01:00
60c7520a51 Merge pull request #612 from jaimeMF/steamIE
SteamIE
2013-01-01 06:49:30 -08:00
deb594a9a0 Test for steam 2013-01-01 15:41:55 +01:00
e314ba675b SteamIE 2013-01-01 14:12:14 +01:00
0214ce7c75 Ok, the Escapist test was passing only in my Travis repo, do not ask me why; also, a small bugfix to the latest commit 2012-12-31 19:21:28 +01:00
95fedbf86b three small edits
* ask for a --verbose log when reporting bugs in README.md
* re-enable Escapist test, seems stable now
* check that we are not downloading multiple videos when the template is fixed (NOT a complete fix: not detecting playlists)
2012-12-31 19:12:57 +01:00
b7769a05ec addedd a serious Public Domain dedication, see http://unlicense.org/ 2012-12-31 15:32:26 +01:00
067f6a3536 moved docs and updates generation scripts from gh-pages branch to devscripts 2012-12-30 21:02:19 +01:00
8cad53e84c Removed a spurious increment_downloads, this time cleanly 2012-12-30 19:53:07 +01:00
d5ed35b664 moved updating code to update.py 2012-12-30 19:50:33 +01:00
f427df17ab some fixes, pulled the codename from the code 2012-12-30 19:50:33 +01:00
4e38899e97 print some version and environment info on --verbose (+ py3 fixes) 2012-12-30 19:50:33 +01:00
cb6ff87fbb The new updates system, relies on gh-pages, secured by RSA, uses external web servers 2012-12-30 19:50:33 +01:00
0deac3a2d8 Revert "Removed a spurious increment_downloads"
This reverts commit 92e3e18a1d.
2012-12-29 16:56:52 +01:00
92e3e18a1d Removed a spurious increment_downloads 2012-12-29 16:49:49 +01:00
3bb6165927 Allow ampersand right after ? in youtube URLs (Closes #602) 2012-12-27 05:31:36 +01:00
d0d4f277da TweetReel IE 2012-12-27 01:38:41 +01:00
99b0a1292b add --no-post-overwrites to README.md; + minor style fixes 2012-12-26 20:39:33 +01:00
dc23886a77 Merge pull request #601 from paullik/no-post-overwrites
No post-processing overwrites
2012-12-24 03:18:48 -08:00
b7298b6e2a not relying on ffmpeg to do the post-processed file checking, instead doing it directly in youtube-dl 2012-12-24 12:53:28 +02:00
3e6c3f52a9 apparently the -n option is available only in ffmpeg 2012-12-23 20:20:19 +02:00
0c0074328b modified FFmpegExtractAudioPP to accept whether it should overwrite post-processed files or not 2012-12-23 19:51:41 +02:00
f0648fc18c added the --no-post-overwrites argument 2012-12-23 19:36:48 +02:00
a7c0f8602e Merge branch 'master' of github.com:rg3/youtube-dl 2012-12-20 21:28:32 +01:00
21a9c6aaac FunnyOrDie IE (Fixes #599) 2012-12-20 21:28:27 +01:00
162e3c5261 Temporary skip Escapist test as it fails only on Travis; we'll make a more specific workaround later if we can't fix it 2012-12-20 17:21:46 +01:00
6b3aef80ce better Vimeo tests; fixed a couple of VimeoIE fields 2012-12-20 16:30:55 +01:00
77c4beab8a new info_dict field: uploader_id 2012-12-20 16:28:16 +01:00
1a2c3c0f3e some py3 fixes, both needed and recommended; we should pass 2to3 as cleanly as possible now 2012-12-20 14:20:24 +01:00
0eaf520d77 add info_dict testing to test_download 2012-12-20 14:20:24 +01:00
056d857571 refactor YouTube subtitles code, it was ugly (my bad) 2012-12-20 14:20:24 +01:00
69a3883199 Enable 3.3 in Travis (works; see https://travis-ci.org/phihag/youtube-dl/jobs/3757443 ) 2012-12-20 13:48:39 +01:00
0dcfb234ed Update Vimeo Info Extractor to get pull in the description properly 2012-12-20 13:27:44 +01:00
43e8fafd49 Refactor IDParser to search for elements by any attribute not just ID 2012-12-20 13:27:38 +01:00
314d506b96 Do not use deprecated method 2012-12-20 13:26:37 +01:00
af42895612 Extend json info data / description file test 2012-12-20 13:26:21 +01:00
bfa6389b74 Clean up legacy code 2012-12-20 13:25:54 +01:00
9b14f51a3e Remove legacy code 2012-12-20 13:14:27 +01:00
f4bfd65ff2 Correct JSON writing (Closes #596) 2012-12-20 13:13:24 +01:00
3cc687d486 test write_info_json 2012-12-20 13:11:52 +01:00
cdb3076445 Sublime space formatting 2012-12-19 14:19:08 +00:00
8a2f13c304 Ignore DS_Store files in Git 2012-12-19 14:17:21 +00:00
77bd7968ea Switch test to metacafe.com, whose DNS seems to be fine atm 2012-12-17 20:32:05 +01:00
993693aa79 Merge remote-tracking branch 'origin/master' 2012-12-17 20:21:41 +01:00
ce4be3a91d Remove some antipatterns and ensure that we always write the JSON file with UTF-8 2012-12-17 19:48:10 +01:00
937021133f a number of new tests and fixes; all tests green on 3.3 2012-12-17 18:33:11 +01:00
f7b111b7d1 Google Video has been shutdown as of 11/15/2012. All videos on Google Video will be migrated to YouTube by the end of 2012. 2012-12-17 16:33:49 +01:00
80d3177e5c various py3 fixes; all tests green on 3.3 2012-12-17 16:25:03 +01:00
5e5ddcfbcf test subtitles 2012-12-17 16:23:55 +01:00
5910e210f4 Fix --extract-audio on Python 3 2012-12-16 12:29:03 +01:00
b375c8b946 Tests for justin.tv 2012-12-16 11:17:10 +01:00
88f6c78b02 Credit vasi for justin.tv 2012-12-16 11:16:57 +01:00
4096b60948 Misc justin.tv fixes 2012-12-16 04:45:46 -05:00
2ab1c5ed1a Support more than 100 videos for justin.tv 2012-12-16 04:26:22 -05:00
0b40544f29 Preliminary support for twitch.tv and justin.tv 2012-12-16 03:50:41 -05:00
187da2c093 added YouJizz extractor 2012-12-16 00:26:27 -05:00
9a2cf56d51 Fixed a problem with the XNXXIE Regex 2012-12-15 23:22:07 -05:00
0be41ec241 Do not decode None 2012-12-15 23:55:13 +01:00
f1171f7c2d Fix VimeoIE in Python 3 2012-12-15 18:25:00 +01:00
28ca6b5afa Fix Dailymotion in Python 3 2012-12-15 18:23:17 +01:00
bec102a843 Fix XNXX in Python 3 2012-12-15 18:19:25 +01:00
8f6f40d991 More Youku Python 3 fixing 2012-12-15 17:59:09 +01:00
e2a8ff24a9 Fix YoukuIE in Python3 (and in general) 2012-12-15 17:57:13 +01:00
8588a86f9e Fix xvideo IE in Python 3 2012-12-15 17:50:45 +01:00
5cb9c3129b restrict sys.argv craziness to Python 2 (Fixes #591) 2012-12-15 17:44:48 +01:00
4cc3d07426 NBA IE (Closes #590) 2012-12-13 21:27:57 +01:00
5d01a64719 Revert "Don't be too clever"
This reverts commit a276e06080.
2012-12-12 15:14:58 +01:00
a276e06080 Don't be too clever 2012-12-12 15:00:03 +01:00
fd5ff02042 streamlined and simplified dynamic tests generation; readded a couple of test features 2012-12-12 14:15:21 +01:00
2b5b2cb84c Merge remote-tracking branch 'gcmalloc/master' into fork_master 2012-12-12 14:11:40 +01:00
nto
ca6849e65d Add support for comedycentral clips (closes #233)
Support individual clips, not just full episodes.
break up now monstrous _VALID_URL regex over multiple lines to improve readability,
pass re.VERBOSE flag when using regex to ignore the whitespace
2012-12-11 21:38:16 -06:00
1535ac2ae9 test automation 2012-12-12 04:03:35 +01:00
a4680a590f changing the template file extension 2012-12-11 20:49:54 +01:00
fedb6816cd rollback tests multiprocess, Travis and OSX don't support it 2012-12-11 20:07:35 +01:00
f6152b4b64 changing the template file extension 2012-12-11 19:17:02 +01:00
4b618047ce Speed up testing (<10s instead of 25s) 2012-12-11 18:52:50 +01:00
2c6945be30 Fix TestYoutubeLists.test_youtube_user 2012-12-11 18:07:38 +01:00
9a6f4429a0 Fix test selection in Python 2.6 2012-12-11 18:03:22 +01:00
4c21c56bfe Merge branch 'master' of github.com:rg3/youtube-dl 2012-12-11 17:07:13 +01:00
2a298b72eb Release 2012.12.11 2012-12-11 17:00:13 +01:00
55c0539872 Fix blip.tv in python3 2012-12-11 17:00:11 +01:00
9789a05c20 fix playlist pagination and add YT playlist tests (closes #569) 2012-12-11 16:58:36 +01:00
d050de77f9 Merge pull request #580 from FiloSottile/master
The new shiny build system
2012-12-11 07:52:44 -08:00
95eb771dcd Merge branch 'master' into fork_master
Conflicts:
	.travis.yml
2012-12-11 12:15:16 +01:00
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
d3d3199870 gentests: allow test-specific FileDownloader params override from tests.json 2012-12-11 12:09:22 +01:00
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
59ce201915 print traceback on trouble if --verbose (why didn't I think of this before!?) 2012-12-11 11:02:21 +01:00
8d5d3a5d00 exposing the test mode as --test (hidden and undocumented) 2012-12-11 09:57:40 +01:00
37c8fd4842 added a test mode to FileDownloader that fetches only first 10K 2012-12-11 09:49:27 +01:00
3c6ffbaedb Merge 'rg3/master' into fork_master 2012-12-08 01:57:43 +01:00
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
5a304a7637 new updating scheme, based on GH downloads; also, check if not updateable (pip installed) 2012-12-08 00:48:07 +01:00
4c1d273e88 it's curious but bash-completion is with - and not _ 2012-12-08 00:37:26 +01:00
a9d2f7e894 making the script compatible with python3 2012-12-07 22:01:02 +01:00
682407f2d5 little correction on the readme 2012-12-07 21:40:06 +01:00
bdff345529 adding a proper bash-completion generation 2012-12-07 21:38:45 +01:00
23109d6a9c youtube-dl.tar.gz make target 2012-12-07 14:46:14 +01:00
4bb028f48e devscripts/make_readme.py in place of all that sedding, that has porting problems 2012-12-07 14:45:16 +01:00
fec89790b1 and now, also py2exe compiles fine :) (on Windows) 2012-12-07 12:04:52 +01:00
a5741a3f5e pip installs fine! 2012-12-07 11:39:08 +01:00
863baa16ec SoundCloud IDs have changed, fix tests 2012-12-07 01:34:40 +01:00
c7214f9a6f Use Soundcloud API (Closes #579) 2012-12-07 01:30:03 +01:00
8fd3afd56c More work on soundcloud IE 2012-12-07 01:24:51 +01:00
f9b2f2b955 Correct accidental rename 2012-12-07 00:57:06 +01:00
633b4a5ff6 Mark SoundCloud IE as nonfunctional for now (#579) 2012-12-07 00:50:56 +01:00
b4cd069d5e Better error reporting for SoundCloud IE 2012-12-07 00:40:13 +01:00
0f8d03f81c Let YoutubeDLHandler (transparent gzip) handle HTTPS URLs as well (Needed for #579) 2012-12-07 00:39:44 +01:00
077174f4ed Add an example to the -o documentation (#573) 2012-12-04 11:05:38 +01:00
e387eb5aba Let youtube IE handle IDs starting with PL (Closes #572) 2012-12-04 10:59:38 +01:00
4083bf81a0 Correct metacafe test filename (happens to start with an underscore) 2012-12-03 20:17:47 +01:00
796173d08b Keep video IDs verbatim if possible (Closes #571) 2012-12-03 15:36:41 +01:00
e575b6821e Improve execution tests 2012-12-01 15:52:34 +01:00
d78be7e331 Add test for Youku (Mentioned in #314) 2012-11-30 08:42:11 +01:00
15c8d83358 Fix Soundcloud IE (+ Python3 support) 2012-11-29 20:40:12 +01:00
e91d2338d8 Fix MD5 calculation 2012-11-29 20:38:16 +01:00
4b235346d6 Add irc channel notice 2012-11-29 19:45:07 +01:00
ad348291bb Enable travis notifications 2012-11-29 19:41:09 +01:00
2f1765c4ea setup.py Python3 fix, PyPi classifiers 2012-11-29 19:21:19 +01:00
3c5b63d2d6 Merge branch 'master' of github.com:rg3/youtube-dl 2012-11-29 18:14:43 +01:00
cc51a7d4e0 New repo skeleton, getting ready for PyPi 2012-11-29 16:51:55 +01:00
8af4ed7b4f Fix 2.6 nosetests 2012-11-29 16:35:57 +01:00
8192ebe1f8 Merge remote-tracking branch 'origin/master' into fork_master
New tests - merged with md5 correction
2012-11-29 15:38:07 +01:00
20ba04267c removed __main__.py from the root of the repo 2012-11-29 15:20:20 +01:00
743b28ce11 Allow youtube_dl/__main__.py to be called directly 2012-11-29 15:11:24 +01:00
caaa47d372 adding the script hook 2012-11-29 14:12:06 +01:00
10f100ac8a cleaning binaries 2012-11-28 19:38:37 +01:00
8176041605 Check during test runtime instead of test generation for _WORKING, and add 2.6 compat 2012-11-28 19:03:11 +01:00
87bec4c715 getting version from git or failing 2012-11-28 18:49:56 +01:00
190e8e27d8 removing the zip option, this can be done with python setup.py bdist --format=zip 2012-11-28 18:33:58 +01:00
4efe62a016 moving to setup.py 2012-11-28 18:24:16 +01:00
c64de2c980 correction on the test 2012-11-28 18:21:53 +01:00
6ad98fb3fd Correct exception raising 2012-11-28 18:21:06 +01:00
b08e09c370 Mark broken IEs in --list-extractors 2012-11-28 17:58:55 +01:00
cdab8aa389 Update download tests 2012-11-28 15:09:56 +01:00
3cd69a54b2 Merge branch 'master' of github.com:rg3/youtube-dl 2012-11-28 12:59:55 +01:00
627dcfff39 Restrict more characters (Closes #566) 2012-11-28 12:59:27 +01:00
df5cff3751 make tests skip on not _WORKING 2012-11-28 11:54:20 +01:00
79ae0a06d5 comment out 3.3 testing until Travis implements it 2012-11-28 11:46:56 +01:00
2d2fa229ec making the metacafe test pass 2012-11-28 11:46:03 +01:00
5a59fd6392 new .travis.yml with notifications and 3.3 2012-11-28 11:46:03 +01:00
0eb0faa26f Mark CollegeHumorIE not working until phihag finishes 2012-11-28 11:43:35 +01:00
32761d863c fix YouTubeIE on 2.6, sorry 2012-11-28 11:28:59 +01:00
799c076384 collegehumor: able to download a single f4f file (not yet playable) 2012-11-28 04:51:27 +01:00
f1cb5bcad2 Make __main__ work in all scenarios with relative imports 2012-11-28 03:55:35 +01:00
9e8056d5a7 Use relative imports 2012-11-28 03:34:40 +01:00
c6f3620859 Drop 2.5 support 2012-11-28 03:30:35 +01:00
59ae15a507 Convert all tabs to 4 spaces (PEP8) 2012-11-28 02:04:46 +01:00
40b35b4aa6 hack for apparently broken parse_qs in python2 2012-11-28 02:01:09 +01:00
be0f77d075 test import 2012-11-28 02:00:45 +01:00
0f00efed4c Woooohooo! python3 youtube_dl BaW_jenozKc -t works! 2012-11-28 00:56:20 +01:00
e6137fd61d Remove superfluous encodings 2012-11-28 00:53:09 +01:00
8cd10ac4ef Fix printing title etc. 2012-11-28 00:46:21 +01:00
64a57846d3 correct to_stderr 2012-11-28 00:33:38 +01:00
72f976701a youtube IE: Correct bytes vs str 2012-11-28 00:31:59 +01:00
5bd9cc7a6a typo 2012-11-28 00:22:55 +01:00
f660c89d51 Use list comprehension instead of map 2012-11-28 00:19:24 +01:00
73dce4b2e4 Import from the correct module 2012-11-28 00:17:59 +01:00
9f37a95941 Py2/3 parse_qs compatibility 2012-11-28 00:17:12 +01:00
a130bc6d02 One more except..as 2012-11-28 00:13:40 +01:00
348d0a7a18 Py2/3 compatibility for http.client 2012-11-28 00:13:00 +01:00
03f9daab34 Use io.BytesIO instead of StringIO 2012-11-28 00:09:17 +01:00
a8156c1d2e Python 3 version of HTMLParser 2012-11-28 00:06:28 +01:00
3e669f369f Py3 compat for unichr and htmlentitydefs 2012-11-28 00:02:55 +01:00
da779b4924 Fall back to urllib instead of urllib2 for Python 3 urllib.parse 2012-11-27 23:58:47 +01:00
89fb51dd2d Remove ur references for Python 3.3 support 2012-11-27 23:56:10 +01:00
01ba00ca42 Prepare urllib references for 2/3 compatibility 2012-11-27 23:54:09 +01:00
e08bee320e Use except .. as everywhere (#180) 2012-11-27 23:31:55 +01:00
96731798db Rename util.u to util.compat_str 2012-11-27 23:29:18 +01:00
c116339ddb Merge branch 'master' of github.com:rg3/youtube-dl 2012-11-27 23:23:37 +01:00
e643e2c6b7 Merge pull request #563 from FiloSottile/IE_cleanup
General IE docs and return dicts cleanup
2012-11-27 14:22:40 -08:00
c63cc10ffa Merge remote-tracking branch 'origin/master' into IE_cleanup
Conflicts:
	youtube_dl/FileDownloader.py
2012-11-27 23:20:32 +01:00
dae7c920f6 Make test_utils.py run on Python 3 2012-11-27 23:20:29 +01:00
f462df021a Use None on missing required info_dict fields 2012-11-27 23:15:33 +01:00
1a84d8675b Use u instead of str in Python 2 2012-11-27 23:11:44 +01:00
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
c806f804d8 Only encode when output stream is binary 2012-11-27 21:07:25 +01:00
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
95649b3936 Replace long with int (see PEP 237) 2012-11-27 19:05:03 +01:00
3aeb78ea4e Better formatting (PEP 8) 2012-11-27 19:03:37 +01:00
dd109dee8e Remove mentions of unicode 2012-11-27 19:02:37 +01:00
b514df2034 Clean up with the help of pep8 2012-11-27 18:55:35 +01:00
0969bdd305 unify spacing 2012-11-27 18:49:18 +01:00
1a9c655e3b Merge remote-tracking branch 'Asido/master' 2012-11-27 18:48:43 +01:00
88db5ef279 2012.11.29 2012-11-27 18:36:43 +01:00
f8d8b39bba Prepare 2012.11.29 release 2012-11-27 18:30:34 +01:00
dcd60025f8 Fix filename sanitation (Closes #555) 2012-11-27 18:27:46 +01:00
7e4674830e document info_dict['subtitles'] and info_dict['urlhandle'] 2012-11-27 18:08:07 +01:00
9ce5d9ee75 make all IEs return 'upload_date' and 'uploader', even if only u'NA' 2012-11-27 17:57:12 +01:00
b49e75ff9a info_dict['upload_date'] is documented in --output, IEs MUST specify it 2012-11-27 17:38:22 +01:00
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
717b1f72ed default info_dict['format'] to info_dict['ext'] and make the YT one more verbose 2012-11-27 17:20:25 +01:00
26396311b5 Add Christian Albrecht (Arte.tv IE) to authors 2012-11-27 17:16:49 +01:00
dffe658bac Remove exclamation mark in --restrict-filenames mode 2012-11-27 17:15:33 +01:00
33d94a6c99 Merge remote-tracking branch 'alab1001101/master' 2012-11-27 17:14:29 +01:00
4d47921c9e ignore kate swap files 2012-11-27 17:01:12 +01:00
d94adc2638 Actually fix manpage (#473) 2012-11-27 16:58:50 +01:00
5c5d06d31d Merge pull request #473 from grimreaper/master
fix mdoc nits
2012-11-27 07:52:58 -08:00
cc872b68a8 Actually merge #379 2012-11-27 16:42:50 +01:00
17cb14a336 Merge remote-tracking branch 'joelverhagen/master' 2012-11-27 16:41:16 +01:00
877f4c45d3 Fix output format doc 2012-11-27 16:28:29 +01:00
02531431f2 Extended documentation for output format in README (Closes #268) 2012-11-27 16:27:35 +01:00
e02066e7ff Windows build for 2012.11.28 2012-11-27 16:15:15 +01:00
c9128b353d Bump version number to a numeric-only one to appease py2exe 2012-11-27 16:12:08 +01:00
e7c6f1a2dc Bump version number 2012-11-27 16:08:39 +01:00
1a911e60a4 Add test for asian characters (#551) 2012-11-27 16:07:52 +01:00
46cbda0be4 Minor filename encoding improvement in a common case 2012-11-27 15:07:10 +01:00
fa59f4b6a9 Merge remote-tracking branch 'chrisjrn/master' 2012-11-27 14:55:18 +01:00
4a702f3819 Fixes the InfoExtractor for the Colbert Report. 2012-11-27 23:54:43 +11:00
6bac102a4d Fix spacing in comedycentral IE 2012-11-27 13:24:10 +01:00
958a22b7cf Merge remote-tracking branch 'chrisjrn/master' 2012-11-27 13:19:18 +01:00
97cd3afc75 warn if %(stitle)s is being used 2012-11-27 13:11:06 +01:00
aa2a94ed81 Encode the entire filename 2012-11-27 13:01:32 +01:00
c7032546f1 Clean up test 2012-11-27 12:46:27 +01:00
56781d3d2e Switch back to underline for invalid characters, and make restricted ASCII-only 2012-11-27 12:46:09 +01:00
feb22fe5fe Fixed indentation error 2012-11-27 22:32:24 +11:00
d8dddb7c02 Removes extranous debugging info :) 2012-11-27 22:30:07 +11:00
4408d996fb Adds format listing/selection support to the Comedy Central extractor. 2012-11-27 22:28:16 +11:00
ed7516c69d Merge remote-tracking branch 'chrisjrn/master' 2012-11-27 12:25:51 +01:00
89af8e9d32 Removes extraneous debug message. 2012-11-27 21:51:30 +11:00
36a9c0b5ff Points the ComedyCentral extractor at a CDN which works with more RTMPDump versions. 2012-11-27 21:49:27 +11:00
9fb3bfb45a Merge remote-tracking branch 'gcmalloc/master' 2012-11-27 00:42:47 +01:00
d479e34043 release 2012.11.27 2012-11-27 00:22:39 +01:00
240089e5df remove accidental remnants 2012-11-27 00:14:12 +01:00
1c469a9480 New optoin --restrict-filenames 2012-11-26 23:58:46 +01:00
71f36332dd Remove redundancy in instructions 2012-11-26 23:40:51 +01:00
8179d2ba74 Merge branch 'master' of github.com:rg3/youtube-dl 2012-11-26 23:25:04 +01:00
df4bad3245 Document configuration 2012-11-26 23:24:55 +01:00
a7b5c8d6a8 fix FAQ on how to compile (also, starnge fix in the Makefile) 2012-11-26 22:35:12 +01:00
92b91c1878 Use character instead of byte strings 2012-11-26 04:23:20 +01:00
7ec1a206ea Remove longs (int does the right thing since Python 2.2, see PEP 237) 2012-11-26 04:13:43 +01:00
51937c0869 Add some parentheses around print for #180 2012-11-26 04:05:54 +01:00
6b50761222 Merge pull request #538 from zejn/patch-1
Also enable album URLs on Vimeo.
2012-11-25 18:04:11 -08:00
6571408dc6 Merge pull request #545 from FiloSottile/alias
Kill (alias) --literal and %(title)
2012-11-25 15:57:57 -08:00
b6fab35b9f alias %(title)s to %(stitle)s 2012-11-25 20:39:42 +01:00
baec15387c aliased --literal to --title 2012-11-25 20:28:49 +01:00
297d7fd9c0 Also enable album URLs on Vimeo. 2012-11-21 13:24:14 +01:00
5002aea371 release 2012.11.17 2012-11-17 14:02:31 +01:00
5f7ad21633 Strip HTML out of uploader name 2012-11-13 17:48:30 -05:00
089d47f8d5 Removed the README.md build target in the makefile. It is broken... 2012-11-13 17:48:10 -05:00
74033a662d Reworked Vimeo file selection logic (quality, codec) - closes #530 2012-11-13 21:53:18 +01:00
fdef722fa1 Added YouPorn infoExtractor 2012-11-13 13:10:56 -05:00
110d4f4c91 Added Pornotube support (for Laborers of Love) 2012-11-12 16:17:55 -05:00
0526e4f55a Merge pull request #522 from art-zhitnik/master
--(match|reject)-title utf8 fix
2012-11-11 06:22:10 -08:00
39973a0236 Solve the bug of parsing titles with unicode (cyrillic) 2012-11-11 14:09:12 +10:00
5d40a470a2 quiet the HTMLParser debug info - closes #517 2012-11-09 12:32:07 +01:00
4cc391461a fix DailyMotion official users videos - closes #281 - by @yvestan 2012-11-07 14:44:10 +01:00
bf95333e5e fixed MetacafeIE (uploader nickname regex) - closes #515 2012-11-06 23:08:10 +01:00
b7a34316d2 -x for --extract-audio, one of the most popular options 2012-10-30 17:41:38 +01:00
74e453bdea New --id option for the old default filename pattern 2012-10-30 17:37:53 +01:00
156a59e7a9 Additional tests in file name sanitation 2012-10-29 08:19:54 +01:00
aeca861f22 Merge pull request #502 from FiloSottile/new_sanitize_filename
My sanitize_filename proposal
2012-10-28 15:33:59 -07:00
42cb53fcfa modified filename escaping to a "smarter" one 2012-10-28 22:47:02 +01:00
fe4d68e196 slight change to Dailymotion uploader regex (fix) 2012-10-28 21:43:43 +01:00
25b7fd9c01 Merge pull request #491 from tyll/master
Update install target
2012-10-26 01:10:25 -07:00
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
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
a8ac2f8664 adding second vimeo url 2012-10-24 15:57:19 +02:00
fb0e99b884 skipping vimeo for the moment 2012-10-24 00:32:23 +02:00
9c6e9a4532 adding xnxx test 2012-10-24 00:13:16 +02:00
67af74992e adding collegehumor test 2012-10-24 00:05:45 +02:00
103c508ffa adding stanford open class courses 2012-10-23 23:59:12 +02:00
2876773381 adding test for vimeo, xvideo and soundcloud 2012-10-23 23:53:33 +02:00
f06eaa873e Fix audio bitrate quality for ffmpeg/avconv 2012-10-23 16:37:12 +02:00
ece34e8951 Merge pull request #486 from Tailszefox/local
Added duration for YouTube videos
2012-10-23 05:53:28 -07:00
2262a32dd7 Added duration for YouTube videos 2012-10-22 18:32:42 +02:00
c6c0e23a32 Support raw playlist parameters (Closes #482) 2012-10-22 13:01:36 +02:00
02b324a23d Restore 2.5 compat by activating with_statement future 2012-10-22 12:51:20 +02:00
b8005afc20 handle YT urls with #/ redirects (closes #484) 2012-10-22 09:15:27 +02:00
073522bc6c Don't use 2.7+ check_output 2012-10-19 23:28:37 +02:00
9248cb0549 Merge pull request #472 from gcmalloc/master
Test proposal
2012-10-19 05:48:12 -07:00
6b41b61119 correcting travis 2012-10-19 12:53:20 +02:00
591bbe9c90 changing test from md5 to filesize, the file changed between download 2012-10-19 12:53:20 +02:00
fc7376016c cleaning the test that doesn't work with the api for the moment 2012-10-19 12:53:20 +02:00
97a37c2319 some assertion on the file downloaded 2012-10-19 12:53:20 +02:00
3afed78a6a removing testing video 2012-10-19 12:53:20 +02:00
4279a0ca98 correcting test to be compatible with python2.6 2012-10-19 12:53:20 +02:00
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
7f60b5aa40 correction on the test 2012-10-19 12:53:19 +02:00
65adb79fb6 Fix mandoc nits 2012-10-15 21:45:56 -04:00
aeeb29a356 adding travis support 2012-10-15 10:58:35 +02:00
902b2a0a45 New IE: YouTube channels (closes #396) 2012-10-14 13:48:18 +02:00
6d9c22cd26 correcting the makefile according to the new one 2012-10-12 20:30:01 +02:00
729baf58b2 removing extended globbing for the find utility 2012-10-12 20:25:22 +02:00
4c9afeca34 adding xvideo 2012-10-12 20:25:22 +02:00
6da7877bf5 adding facebook test 2012-10-12 20:25:22 +02:00
b4e5de51ec adding photobucket test 2012-10-12 20:25:22 +02:00
a4b5f22554 adding metacafe test 2012-10-12 20:25:22 +02:00
ff08984246 adding dailymotion test 2012-10-12 20:25:22 +02:00
137c5803c3 some changes to keep the same standard 2012-10-12 20:25:22 +02:00
3eec021a1f removing unused global modifier 2012-10-12 20:25:22 +02:00
5a33b73309 correcting the makefile 2012-10-12 20:25:22 +02:00
0b4e98490b changing test video 2012-10-12 20:24:58 +02:00
80a846e119 correction on the test for the utils.py 2012-10-12 20:24:58 +02:00
434d60cd95 adding clean rule in the makefile 2012-10-12 20:24:58 +02:00
efe8902f0b adding download test with md5 check 2012-10-12 20:24:58 +02:00
44fb345437 adding TestCase class and corresponding test 2012-10-12 20:24:58 +02:00
9993976ae4 correction on the sanitize title method, change in title resulting 2012-10-12 20:24:58 +02:00
b387fb0385 adding test rule in the Makefile 2012-10-12 20:24:58 +02:00
10daa766a1 support EDU YouTube playlists (closes #407) 2012-10-11 08:27:19 +02:00
7b107eea51 release 2012.10.09 2012-10-09 15:53:20 +02:00
646b885cbf Added missing dependencies to Makefile 2012-10-09 15:49:24 +02:00
0bfd0b598a Re-engineered Dailymotion qualities selection (thanks @knagano, sort of merges #176) 2012-10-09 12:28:44 +02:00
fd873c69a4 Merge PR #422 from 'kevinamadeus/master'
Add InfoExtractor for Google Plus video
(with fixes)
2012-10-09 10:48:49 +02:00
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
27fec0e3bd Merge branch 'master' of github.com:rg3/youtube-dl 2012-10-08 22:14:28 +02:00
65f934dc93 Correct detect_executables on Windows (Closes #447, #457) 2012-10-08 22:14:19 +02:00
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
aa85963987 Merge pull request #452 from Tailszefox/local
Added uploaded date for Dailymotion
2012-10-03 11:29:51 -07:00
413575f7a5 Added uploaded date for Dailymotion 2012-10-03 10:57:46 +02:00
b7b4796bf2 Fix docs 2012-10-01 18:39:24 +02:00
fcbc8c830e Merge branch 'master' of github.com:rg3/youtube-dl 2012-10-01 18:38:19 +02:00
f48ce130c7 Fix doc of extractor field 2012-10-01 18:38:10 +02:00
13e69f546c Merged, modified and compiled Dailymotion pull request #446 by @Steap 2012-09-30 21:45:43 +02:00
63ec7b7479 DailymotionIE: There is not necessarily an underscore in a Dailymotion URL. 2012-09-30 15:47:37 +02:00
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
39ce6e79e7 Updated youtube-dl.exe 2012-09-29 19:12:56 +02:00
5c961d89df Merge pull request #403 from FiloSottile/re_VERBOSE 2012-09-29 17:05:40 +02:00
3c4d6c9eba Not all Dailymotion videos have an hqURL, now downloads highest quality available 2012-09-29 16:53:06 +02:00
349e2e3e21 Fixed DailymotionIE, now downloads high-def mp4s, which might be too much (?) 2012-09-29 16:38:38 +02:00
551fa9dfbf adding new --output replacements. Thanks @danut007ro (closes #442) 2012-09-29 15:49:10 +02:00
ce3674430b added new FAQ on exe dependency 2012-09-29 15:35:07 +02:00
5cdfaeb37b New FAQ: What is this binary file? (+ small fix to other one) 2012-09-28 19:55:18 +02:00
38612b4edc update default UA string (Closes #390) 2012-09-27 23:38:11 +02:00
6c5b442a9b Add recent breakage to FAQ (Closes #433) 2012-09-27 23:30:17 +02:00
5a5523698d Add new field "extractor" to the info dictionary 2012-09-27 20:48:16 +02:00
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
8ca21983d8 Merge pull request #432 from cryzed/master
Fixed YouTube playlist parsing
2012-09-27 11:42:58 -07:00
20326b8b1b Let Makefile use youtube-dl source code instead of compiled binary 2012-09-27 20:21:20 +02:00
5d534e2fe6 Improve option definitions 2012-09-27 20:19:27 +02:00
234e230c87 Merge remote-tracking branch 'FiloSottille/vbr'
Conflicts:
	youtube-dl
	youtube-dl.exe
2012-09-27 20:18:29 +02:00
34ae0f9d20 Merge branch 'master' of github.com:rg3/youtube-dl 2012-09-27 19:56:29 +02:00
df09e5f9e1 Merge pull request #405 from hdclark/master
Support for custom user agent
2012-09-27 10:56:25 -07:00
3af2f7656c Fixed YouTube playlist parsing 2012-09-27 19:48:29 +02:00
74e716bb64 original test video 2012-09-27 19:44:44 +02:00
85f76ac90b Merge remote-tracking branch 'FiloSottille/automation' 2012-09-27 19:41:51 +02:00
7f36e39676 Merge remote-tracking branch 'FiloSottille/supports'
Conflicts:
	youtube-dl
2012-09-27 19:24:41 +02:00
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
b5de8af234 Release 2012.09.27 2012-09-27 11:25:46 +02:00
eb817499b0 Compile updated youtube-dl 2012-09-27 11:23:44 +02:00
e2af9232b2 Merge pull request #428 from virtulis/master
A quick fix to #427
2012-09-27 02:22:05 -07:00
9ca667065e Add 'signature' to YouTube URLs, fixes #427 2012-09-27 09:44:49 +03:00
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
3cd98c7894 Removed provider (mistake) and add provider parameter to process_info 2012-09-27 00:07:20 +03:00
2866e68838 Merge branch 'master' of https://github.com/rg3/youtube-dl 2012-09-26 21:09:44 +03:00
be8786a6a4 Every extractor also return it's name. 2012-09-26 21:00:28 +03:00
0e841bdc54 add PREFIX option to make install 2012-09-26 00:10:39 +02:00
225dceb046 moved make release to devscripts/release.sh 2012-09-25 23:56:01 +02:00
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
d443aca863 Add InfoExtractor for Google Plus video 2012-09-25 16:21:02 +08:00
2ebc6e6a92 Make youtube-dl 2012-08-26 09:57:49 +02:00
f2ad10a97d Add arte.tv Info Extractor 2012-08-26 09:47:19 +02:00
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
202e76cfb0 Made the YouTubeIE regex verbose/commented 2012-08-20 00:58:10 +02:00
3a68d7b467 tweaked the --audio-quality input validation/specification 2012-08-19 23:25:16 +02:00
795cc5059a Re-engineered XNXXIE to actually exit on ERRORs even with -i 2012-08-19 18:46:23 +02:00
5dc846fad0 Merge pull request #398 from tempname/master 2012-08-19 18:39:43 +02:00
d5c4c4c10e bugfix and standarize the youku.com support 2012-08-19 17:44:34 +02:00
1ac3e3315e Merge pull request #395 from thesues/master 2012-08-19 17:08:39 +02:00
0e4dc2fc74 Merge 'rbrito/support-tube.majestyc.net' (PR #391) with small fix 2012-08-19 17:00:20 +02:00
9bb8dc8e42 Python 2.6 compatibility fix. Thanks @Jamesc359 - closes #400 2012-08-19 16:06:33 +02:00
154b55dae3 added InfoExtractor for XNXX 2012-08-15 20:57:27 -03:00
6de7ef9b8d added InfoExtractor for XNXX 2012-08-15 20:54:03 -03:00
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
51661d8600 add www.youku.com support 2012-08-09 13:54:19 +08:00
b5809a68bf merge 2012-08-09 12:26:26 +08:00
7733d455c8 fix 0a->0A bug 2012-08-09 03:14:02 +08:00
0a98b09bc2 youku default to download hd2 video 2012-08-09 02:53:21 +08:00
302efc19ea add youku support 2012-08-09 02:04:02 +08:00
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
dce1088450 A more "make-esque" Makefile with file targets and dependencies 2012-08-03 20:10:54 +02:00
a171dbfc27 Merge pull request #386 from FiloSottile/blip
Blip.tv
2012-08-01 12:26:00 -07:00
11a141dec9 BlipTVUserIE fix 2012-08-01 21:11:04 +02:00
818282710b moved the User-Agent workaround to the BlipTV IE 2012-08-01 20:51:56 +02:00
7a7c093ab0 added one-step realese script 'make release version=nn' - closes #158 2012-08-01 18:40:27 +02:00
ce7b2a40d0 added automatically generated bash-completion; closes #191 2012-08-01 17:26:50 +02:00
cfcec69331 auto-generating manpage from README.md (closes #151); redesigned Makefile 2012-08-01 11:54:27 +02:00
91645066e2 Merge branch 'joehillen/master' - pull request #381 2012-08-01 11:35:04 +02:00
dee5d76923 changed YouTube closed captions URL; closes #382 2012-07-31 15:56:35 +02:00
363a4e1114 xvideos patch by @pocoimporta - closes #370 2012-07-31 01:40:29 +02:00
ef0c08cdfe Added install target to Makefile. 2012-07-22 13:36:22 -07:00
3210735c49 Fix EscapistMagazine IE 2012-07-18 21:17:51 +02:00
aab4fca422 Updated --no-resize-buffer docs, removed -b option 2012-07-16 10:59:21 -04:00
891d7f2329 Added options to set download buffer size and disable automatic buffer resizing. 2012-07-14 16:47:19 -04:00
b24676ce88 changed --audio-quality behaviour to support both CBR and VBR 2012-07-14 19:43:24 +02:00
cca4828ac9 fixed a logic bug in post-processing 2012-07-14 14:35:57 +02:00
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
d4e16d3e97 YouTube playlist fix; closes #365 and #331 2012-06-30 15:04:30 +02:00
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
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
7df97fb59f display a meaningful error message on rental videos (#359) 2012-06-22 13:57:17 +02:00
3187e42a23 Merge pull requests #356 #357 #358 by jcarlosgarciasegovia 2012-06-06 20:51:29 +02:00
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
eeeb4daabc Information Extractor for blip.tv users 2012-06-06 16:16:16 +00:00
3c4fc580bb Use an User-Agent that will allow downloading from blip.tv fixes #325 2012-06-06 13:24:12 +00:00
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
505ed3088f normalize ffmpeg/avconv names printing 2012-06-03 12:11:39 +02:00
0b976545c7 check for avconv and ffmpeg, use as available; closes #344 2012-06-03 12:10:15 +02:00
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
6ab92c8b62 don't corrupt stdout (-o -) in verbose mode 2012-05-30 11:50:13 +01:00
f36cd07685 fixed a couple of Windows exe update bugs 2012-05-27 23:03:45 +02:00
668d975039 quiet zip in make compile 2012-05-23 19:19:53 +02:00
9ab3406ddb Fix Escapist IE 2012-05-23 19:19:31 +02:00
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
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
0b8c922da9 Introduced Trouble(Exception) for more elegant non-fatal errors handling 2012-05-09 09:43:11 +00:00
3fe294e4ef merge upstream 2012-05-01 18:22:08 +02:00
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
0c24eed73a merge #336 2012-04-19 09:46:01 +02:00
29ce2c1201 Merge git://git.jankratochvil.net/youtube-dl 2012-04-19 09:44:25 +02:00
532c74ae86 Add format #46 - WebM 1920x1080. 2012-04-16 17:13:01 +02:00
9beb5af82e some HTMLParser bugfixes 2012-04-13 22:09:24 +02:00
9e6dd23876 merged unescapeHTML branch; removed lxml dependency 2012-04-11 00:22:51 +02:00
7a8501e307 ignore parsing errors in get_element_by_id() 2012-04-10 23:08:53 +02:00
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
c6f45d4314 removed dependency from lxml: added IDParser 2012-04-10 18:21:00 +02:00
d11d05d07a better naming for the sub-modules 2012-04-10 16:46:36 +02:00
e179aadfdf moved trivialjson to a separate file 2012-04-10 16:37:40 +02:00
d6a9615347 standardized the use of unescapeHTML; added clean_html() 2012-04-10 16:31:46 +02:00
c6306eb798 wine-py2exe.sh to create the exe under linux (!!) 2012-04-07 20:07:42 +02:00
bcfde70d73 py2exe -U fix for Windows XP 2012-03-31 01:27:47 +02:00
53e893615d corrected -U to support new zipfile and exe (#153) formats 2012-03-31 01:19:30 +02:00
303692b5ed 's/ /\t/' 2012-03-30 23:54:16 +02:00
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
770234afa2 Added py2exe script 2012-03-25 23:48:53 +02:00
d77c3dfd02 Split code as a package, compiled into an executable zip 2012-03-25 03:07:37 +02:00
c23d8a74dc Merge branch 'next-url' 2012-03-25 01:07:47 +01:00
74a5ff5f43 transplant ceba827e9a, d891ff9fd9, 69d3b2d824, 071940680f 2012-03-24 01:23:19 +01:00
071940680f Always extract original URL from next_url (#318) 2012-03-24 01:17:36 +01:00
69d3b2d824 Extract original URL from next_url parameter of verify_age page, before actual extract 2012-03-23 06:17:29 +01:00
d891ff9fd9 Ignore leading spaces (and trailing also) in all URL from url list or command line 2012-03-23 06:15:57 +01:00
6af22cf0ef added support for HTTP redirects. Closes #315 2012-03-18 22:15:58 +01:00
fff24d5e35 Clean up superfluous whitespace 2012-03-15 20:52:35 +01:00
ceba827e9a Credit Filippo Valsorda 2012-03-15 20:47:27 +01:00
a0432a1e80 added --srt-lang; updated README; extended the -g FAQ 2012-03-15 14:56:08 +01:00
cfcf32d038 Merge branch 'master' of git://github.com/rg3/youtube-dl into closed-captions 2012-03-15 14:05:34 +01:00
a67bdc34fa transplant gist of 7151f63a5f 2012-03-15 08:36:31 +01:00
b3a653c245 Merge commit '7151f63a5f3820a322ba8bf61eebe8d9f75d6ee5' 2012-03-15 08:26:44 +01:00
4a34b7252e transplant 2934c2ce43 and afbaa80b8b 2012-03-15 08:05:21 +01:00
7e45ec57a8 transplant 0f6e296a8e 2012-03-15 07:56:32 +01:00
afbaa80b8b switched ytsearch to more robust Youtube Data API (fixes #307) 2012-03-14 22:44:45 +01:00
115d243428 added youtube closed captions .srt support (see #90) 2012-03-13 23:49:33 +01:00
7151f63a5f Fixed downloading of unrelated videos when downloading a YouTube playlist 2012-03-09 22:05:35 +01:00
597e7b1805 Vimeo: Added support for flv only videos 2012-03-07 21:02:12 +01:00
2934c2ce43 Switch Vimeo to scraping: fixes #285 2012-03-05 17:51:16 +01:00
0f6e296a8e Fixed gvsearch 2012-03-02 00:35:56 +01:00
9c228928b6 release 2012.02.27 2012-02-27 20:19:28 +01:00
ff3a2b8eab Always determine youtube description 2012-02-27 20:19:03 +01:00
c4105fa035 release 2012.02.26 2012-02-27 00:42:26 +01:00
871dbd3c92 Output RTMP command line if verbose is set 2012-02-27 00:41:53 +01:00
c9ed14e6d6 Move imports to top (Closes #283) 2012-02-26 23:53:56 +01:00
1ad85e5061 Set default continue behavior to true, no breakage observed in the wild 2012-02-26 23:44:32 +01:00
09fbc6c952 verbose flag, and output proxies if it is set 2012-02-26 23:33:19 +01:00
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
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
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
4afdff39d7 Support Unicode in file names on Windows NT 2012-01-16 12:08:01 +02:00
661a807c65 Release 2012.01.08b 2012-01-08 17:23:10 +01:00
6d58c4546e correct to_screen prints 2012-01-08 17:22:48 +01:00
38ffbc0222 Release 2012.01.08 2012-01-08 17:20:55 +01:00
fefb166c52 Leave out characters the filesystem cannot encode (Closes: #264) 2012-01-08 17:20:18 +01:00
dcb3c22e0b MTV IE 2012-01-07 01:30:30 +01:00
47a53c9e46 release 2012.01.05 2012-01-05 11:08:50 +01:00
1413cd87eb Correct distinction between unicode and bytes (Closes: #257) 2012-01-05 10:46:21 +01:00
c92e184f75 Correct comedycentral flash URL regex 2012-01-05 00:39:47 +01:00
3906e6ce60 correct epydoc 2012-01-05 00:36:47 +01:00
c7d3c3db0d Fix tds RTMP url extraction 2012-01-04 14:08:17 +01:00
d6639d05c2 release 2011.12.18 2011-12-17 01:35:05 +01:00
633cf7cbad Add wav audio output 2011-12-17 01:32:28 +01:00
a5647b79ce Only skip download if files exists; convert audio 2011-12-16 23:33:46 +01:00
ba5059dd66 Release 2011.12.15 2011-12-15 20:32:37 +01:00
bb8abbbbae Dailymotion: Use og:title instead of <title> to find title (Closes: #253) 2011-12-15 20:32:05 +01:00
561504fffa Release 2011.12.08 2011-12-08 21:39:13 +01:00
23e6b8adc8 --prefer-free-formats (Closes #231) 2011-12-08 21:38:28 +01:00
3e0ea7d07a m4a: aac in mp4 container (Closes #240) 2011-12-08 21:21:25 +01:00
94fd3201b2 Abort when --max-downloads is reached. 2011-12-08 20:59:02 +01:00
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
a05d2a0c05 Merge branch 'master' of github.com:rg3/youtube-dl 2011-12-08 20:39:22 +01:00
0b14e0b367 OpenClassRoom IE (Closes: #234) 2011-12-08 20:36:00 +01:00
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
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
f1f300e629 Add list of files to ignore for youtube-dl. 2011-11-30 14:17:20 -02:00
dd17922afc OpenClassRoom videos (#234) 2011-11-30 10:52:04 +01:00
40fd4cb86a Move merged code to dev version 2011-11-30 10:00:36 +01:00
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
8abf76ddb9 Fix the DailymotionIE to parse the new title of a webpage 2011-11-29 22:30:42 -05:00
c95da745bc Mention -o - in doc (Closes #204) 2011-11-29 20:22:27 +01:00
0cd235eef6 Use freedesktop.org mandated user config file location (Suggested by Tyll in #231) 2011-11-29 20:13:13 +01:00
77315556f1 Do not count unmatched or skipped videos towards max-downloads (Closes #232) 2011-11-29 20:08:01 +01:00
c379c181e0 Preliminary implementation of configuration files 2011-11-28 01:29:46 +01:00
31a2ec2d88 Document -o %(upload_date)s (Closes #228) 2011-11-28 01:00:01 +01:00
b88a52504e --max-downloads option (Closes #230) 2011-11-28 00:55:44 +01:00
a95567af99 Credit shizeeg for Mixcloud IE 2011-11-24 18:58:19 +01:00
849edab8ec Move MixcloudIE to __init__.py 2011-11-24 18:02:12 +01:00
b158a1d946 Mixcloud IE 2011-11-24 20:45:14 +04:00
fa2672f9fc Release 2011.11.23 2011-11-23 10:36:52 +01:00
28e3614bc0 Fix vimeo error (Closes #224) 2011-11-23 10:35:55 +01:00
208e095f72 Correct simplify_title call in ComedyCentral IE 2011-11-22 21:26:38 +01:00
0ae7abe57c Release 2011.11.22 2011-11-22 15:32:53 +01:00
dc0a294a73 Make exception handling 2.5-compatible (Closes #223) 2011-11-22 15:31:30 +01:00
468c99257c Release 2011.11.21 2011-11-21 21:51:24 +01:00
af8e8d63f9 Allow non-ASCII characters in simplified titles(Closes #220) 2011-11-21 21:50:39 +01:00
e092418d8b Simplify simplify_title 2011-11-21 20:17:07 +01:00
e33e3045c6 First tests 2011-11-21 20:07:24 +01:00
cb6568bf21 Use the dev version in Makefile 2011-11-21 20:00:54 +01:00
235b3ba479 Move code into a separate Python module 2011-11-21 19:59:59 +01:00
5b3330e0cf Remove empty real_initialize defs 2011-11-21 19:31:20 +01:00
aab771fbdf Credit authors of Soundclound and InfoQ extractors 2011-11-16 09:33:03 +01:00
00f95a93f5 InfoQ IE (Closes #216) 2011-11-15 23:00:31 +01:00
1724e7c461 Merge remote-tracking branch 'ngokevin/soundcloud' 2011-11-15 22:37:49 +01:00
3b98a5ddac InfoQ IE 2011-11-15 23:20:29 +02:00
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
c3e4e7c182 Fix youtube playlist IE match (Closes: #210) 2011-11-15 10:35:59 +01:00
38348005b3 removed weird indent 2011-11-12 17:28:26 -08:00
208c4b9128 added whitespace below soundcloudIE class 2011-11-12 17:10:21 -08:00
ec574c2c41 extracts full title from source 2011-11-12 17:08:40 -08:00
871be928a8 now downloads soundcloud songs, need to polish title grabbing and file naming 2011-11-12 16:48:43 -08:00
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
073d7a5985 extracted all of the soundcloud information including description (not tested), need to hook into filedownloader 2011-11-09 01:52:36 -08:00
40306424b1 work on soundcloud information extractor...need to talk to youtube-dl guys 2011-11-08 00:03:35 -08:00
ecb3bfe543 going home and need to upload what little i did 2011-11-07 18:02:10 -08:00
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
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
857e5f329a FacebookIE: Fix regexp to recognize videos that weren't considered. 2011-11-01 12:07:05 -02:00
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
99e207bab0 FacebookIE: Fix extraction of title as Facebook has changed stuff. 2011-10-20 20:27:48 -02:00
0067bbe7a7 FacebookIE: Not all videos are available in all formats. 2011-10-20 20:26:42 -02:00
45aa690868 Release 2011.10.19 2011-10-19 00:40:13 +02:00
beb245e92f Merge branch 'vimeo' of https://github.com/rbrito/youtube-dl 2011-10-19 00:38:41 +02:00
c424df0d2f vimeo: Add the ability to detect if a video is available in HD. (Closes: #194) 2011-10-19 00:37:45 +02:00
87929e4b35 vimeo: Add the ability to detect if a video is available in HD. 2011-10-18 19:47:19 -02:00
d76736fc5e Merge pull request #195 from rbrito/xvideos
Fixes for the xvideos IE
2011-10-18 13:49:21 -07:00
0f9b77223e xvideos: Capture only the video title, not the name of the site. 2011-10-18 18:42:01 -02:00
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
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
6501a06d46 Quick and dirty IE for xvideos.com. 2011-10-13 16:44:20 -03:00
8d89fbae5a CollegeHumor IE 2011-10-12 21:13:43 +02:00
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
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
54d47874f7 release 2011.09.30 2011-09-30 09:07:59 +02:00
2761012f69 Cosmetic changes to --list-formats 2011-09-30 09:07:36 +02:00
3de2a1e635 Added option -L to list available formats 2011-09-28 01:28:37 +02:00
1eff9ac0c5 Release 2011.09.27 2011-09-27 21:42:52 +02:00
54f329fe93 blip.tv: Handle direct URLs (Thanks to Bahman) 2011-09-27 21:42:15 +02:00
9baa2ef53b Update REAMDE.md 2011-09-26 20:29:15 +02:00
6bde5972c3 Mention ext in output template help 2011-09-26 20:26:40 +02:00
36f6cb369b Add help to output template 2011-09-26 20:24:43 +02:00
b845d58b04 Adding HTTPS support for YouTube playlists and users; Escaping dots. 2011-09-25 21:28:20 +03:00
efb113c736 Simplify test 2011-09-21 18:49:08 +02:00
3ce59dae88 Update README with new options 2011-09-21 18:48:51 +02:00
f0b0caa3fa Merge pull request #172 from richardc/master
add support for vorbis to  --extract-audio
2011-09-21 09:47:11 -07:00
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
abb870d1ad Clarify --cookies option 2011-09-18 18:50:23 +02:00
daa982bc01 release 2011.09.18c: Prefer mp4 over webm 2011-09-17 00:58:44 +02:00
767414a292 Prefer format 18 over 43 2011-09-17 00:58:14 +02:00
7b417b388a Add youtube format 44 2011-09-17 00:51:25 +02:00
44424ceee9 Prefer mp4 over webm - seems to work better for most users 2011-09-17 00:39:51 +02:00
08a5b7f800 Release 2011.09.18b 2011-09-16 22:33:08 +02:00
1cde6f1d52 Prevent youtube IE from taking youtube playlists 2011-09-16 22:31:31 +02:00
2d8acd8039 Fix escapist URL match 2011-09-15 20:25:22 +02:00
67035ede49 Fix progress message when Content-Length is not set 2011-09-15 20:24:21 +02:00
eb6c37da43 Clarified "restart" 2011-09-15 20:10:27 +02:00
2736595628 Do not update if already up-to-date (Closes #166) 2011-09-15 20:09:30 +02:00
7b1a2bbe17 release 2011.09.18 2011-09-15 19:29:16 +02:00
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
cc025e1226 release 2011.09.17 2011-09-15 19:23:52 +02:00
eca1b76f01 Update README 2011-09-15 19:23:17 +02:00
366cbfb04a Fix _do_download signature 2011-09-15 19:22:18 +02:00
18bb3d1e35 Make --continue the default and provide --no-continue (Closes #119) 2011-09-15 19:12:04 +02:00
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
ef357c4bf2 Bump version number 2011-09-15 18:48:29 +02:00
5260e68f64 Add format fallback 2011-09-15 18:47:36 +02:00
6a1ca41e17 Update README 2011-09-15 12:45:56 +02:00
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
da0db53a75 added option to get the available formats for a video (Closes #106) 2011-09-15 12:20:03 +02:00
c52b01f326 Added ability to download worst quality video file only. (Closes #113) 2011-09-15 12:14:16 +02:00
36597dc40f Updated to stamp extracted audio file with HTTP last modified date. 2011-09-15 12:04:54 +02:00
9b4556c469 New option --skip-download (Closes #162) 2011-09-15 11:36:49 +02:00
f3098c4d8a --list-extractors (Closes #161) 2011-09-15 11:03:29 +02:00
bdb3f7a769 Simplify suitable 2011-09-15 10:06:14 +02:00
afb5b55de6 Proper warning if xml.etree.ElementTree is not available 2011-09-15 09:59:03 +02:00
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
e5b9fac281 Bump version number 2011-09-14 22:55:26 +02:00
08c1d0d3bc Update README 2011-09-14 22:55:09 +02:00
20e91e8375 Add --match-title and --reject-title (Closes #132) 2011-09-14 22:54:51 +02:00
f9c6878714 Support for The Escapist 2011-09-14 22:26:53 +02:00
8c5dc3ad40 Simplify IE index 2011-09-14 21:39:41 +02:00
1d2e86aed9 Decapitalize options in README for consistency with youtube-dl --help 2011-09-14 21:20:23 +02:00
a2f7e3a5bb Clarify usage 2011-09-14 21:19:33 +02:00
f2a3a3522c typo in README 2011-09-14 21:18:22 +02:00
b487ef0833 Fully implement comedycentral downloader 2011-09-14 21:17:05 +02:00
d0922f29a3 Update LATEST_VERSION (oops) 2011-09-14 00:04:46 +02:00
b90bcbe79e Bump version number 2011-09-13 23:58:46 +02:00
8236e85178 s#phihag#rg3 2011-09-13 23:58:31 +02:00
803abae206 Do not claim copyright in README (Closes #157) 2011-09-13 23:54:50 +02:00
50bdd8a9e7 Merge remote-tracking branch 'knagano/master' 2011-09-13 23:50:44 +02:00
34554a7ad4 Merge remote-tracking branch 'CaptainPatate/master' 2011-09-13 23:35:48 +02:00
93e1659586 Bump version number (remove -phihag) 2011-09-13 22:39:20 +02:00
b576abb457 Automatically generate LATEST_VERSION (Closes #16) 2011-09-13 22:29:50 +02:00
f166bccc8f Allow downloading current thedailyshow episode with youtube-dl :tds 2011-09-13 21:51:44 +02:00
5a2ba45e09 Clarify README 2011-09-13 21:51:06 +02:00
e133e1213f README: More bug filing instructions 2011-09-09 08:47:00 +02:00
454d6691d8 Include ERROR: no fmt_url_map or conn information found in video info in FAQ 2011-09-09 08:41:52 +02:00
d793aebaed comedycentral: 1 seems to be the constant correct offset 2011-09-09 08:14:01 +02:00
5991ddfd7a comedycentral: Use media number instead of act number as ID 2011-09-08 18:49:28 +02:00
a88bc6bbd3 Temporarily fix dailyshow+colbertnation media IDs 2011-09-07 23:15:26 +02:00
46c8c43266 Switch around act and episode title (makes -t nicer) 2011-09-07 22:42:33 +02:00
fedf9f3902 Basic comedycentral (The Daily Show) support (Will work as soon as rtmpdump gets fixed) 2011-09-07 22:06:09 +02:00
0f862ea18c comedycentral: include player URL (still broken) 2011-09-07 21:43:19 +02:00
c8e30044b8 Rudimentary support for comedycentral (rtmpdump currently broken) 2011-09-07 21:36:06 +02:00
cec3a53cbd Do not try to re-encode unicode filenames (Closes #13) 2011-09-07 09:35:22 +02:00
6fc5b0bb17 Credit Sören Schulze for myvideo support 2011-09-06 23:58:00 +02:00
9b0a8bc198 myvideo.de support 2011-09-06 23:56:32 +02:00
e5e74ffb97 Fix os.makedirs in Windows 2011-09-06 17:56:05 +02:00
eb99a7ee5f Bump version to 2011.09.06 2011-09-06 17:42:45 +02:00
50891fece7 Use os.makedirs instead of homebrewn pmkdir 2011-09-06 17:32:22 +02:00
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
8f88eb1fa7 Update Makefile to new README format 2011-09-04 11:47:58 +02:00
447b1d7170 Added FAQ to README 2011-09-04 11:41:54 +02:00
dbddab2799 Robust error handling in downloading code 2011-09-03 11:32:05 +02:00
802622ac1c Merge remote-tracking branch 'wise86/master' 2011-08-31 21:28:44 +02:00
e0e56865a0 Remove stable from help wording (There will be only one main branch for now) 2011-08-31 21:28:40 +02:00
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
d207e7cf88 Update update mechanism (Closes #4) 2011-08-28 23:38:40 +02:00
36cf7bccde Merge remote-tracking branch 'rbrito/prefer-webm' 2011-08-28 23:23:42 +02:00
5fd5ce0838 Add default make target 2011-08-28 23:17:32 +02:00
6ae796b1ee Credit Rogério Brito for Vimeo support 2011-08-28 23:17:18 +02:00
9c3e23fb64 Merge Vimeo support 2011-08-28 22:57:50 +02:00
5f9f2b7396 Update: Write downloaded file without modification (allows hashsums) 2011-08-28 22:10:03 +02:00
4618f3da74 Makefile to recreate README 2011-08-25 00:09:28 +02:00
eb0387a848 Fix stty detection 2011-08-25 00:08:59 +02:00
fe6dc08b79 Merge git://github.com/dbb/youtube-dl 2011-08-24 23:34:09 +02:00
4f2a5e06da Use subprocess to call stty size when COLUMNS is not set 2011-08-24 23:28:30 +02:00
2c8d32de33 merge gvalkov/optparse-refactor (minus stty) 2011-08-24 23:21:55 +02:00
2b70537d7b merge upstream 2011-08-24 23:04:10 +02:00
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
5adcaa4385 Refactor main function 2011-08-23 16:48:08 +03:00
51c8e53ffe Set help formatter width to terminal width (prevents wrapping) 2011-08-23 16:42:51 +03:00
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
5fb3df4aff Move update_self out of __main__ for clarity 2011-08-23 15:37:35 +03:00
7a9054ec79 Fix small indentation inconsistencies 2011-08-23 15:01:51 +03:00
2770590d5a Use module metadata variables instead of comments 2011-08-23 14:58:22 +03:00
e9cb9c2811 Add vim modeline 2011-08-23 14:45:26 +03:00
1cab2c6dcf Fix blip.tv regular expression to not match blipXtv 2011-08-18 09:31:36 +02:00
86e709d3de Fix youtu.be links (Closes #142) 2011-08-07 13:01:09 +02:00
8519c32d25 Use parse_qs instead of homebrewn parsing 2011-08-07 00:29:25 +02:00
f3dc18d874 youtube: Better error messages 2011-08-07 00:02:50 +02:00
1293ce58ac Fix Python 2.4 compatibility 2011-08-06 12:16:07 +02:00
0a3c8b6291 Use alternative imports for Python 2.4 (Closes #138) 2011-08-06 11:48:45 +02:00
134cff47ab Remove debugging information 2011-08-06 11:20:28 +02:00
f137bef973 Fix RTMP streams and ignore url-less entries 2011-08-06 11:05:57 +02:00
2bf94b3116 Remove horizontal rules from README.md 2011-08-05 19:15:57 -04:00
6bcd846b52 Add README.md (markdown file) 2011-08-05 19:14:13 -04:00
2fb47e073a Merge https://github.com/rg3/youtube-dl into vimeo 2011-08-04 17:44:49 -03:00
05b4029662 Merge https://github.com/rg3/youtube-dl into prefer-webm 2011-08-04 17:44:33 -03:00
33d507f1fe Bump version number 2011-08-04 19:15:14 +02:00
c44b9ee95e Update User-Agent string 2011-08-04 19:14:19 +02:00
8126094cf1 Fix YouTube downloads (code by Philipp Hagemeister) 2011-08-04 19:13:02 +02:00
0ac22e4f5a Fix youtube downloads (Closes #135) 2011-08-04 00:04:55 +02:00
c31b124d7a Suppport for youtube video streams (Mentioned in #108) 2011-07-31 18:09:53 +02:00
47b8dab29e Removed inaccurate warning 2011-07-22 15:28:42 +02:00
91e6a3855b Be lenient about download URLs (Closes #108) 2011-07-18 19:43:21 +02:00
5623100e43 remove debugging code 2011-07-10 23:41:19 +02:00
6eb08fbf8b + --write-info-json 2011-07-10 21:39:36 +02:00
437d76c19a blip.tv support for python 2.5 with trivialjson 2011-07-10 17:31:54 +02:00
2152ee8601 Update youtube playlist for use playlist?list=id format 2011-07-09 14:05:36 +00:00
a1cab7cead call increment_downloads in blip.tv extractor 2011-07-07 14:10:25 +02:00
8b95c38707 --writedescription option 2011-07-07 12:47:36 +02:00
c6b55a8d48 Full youtube video descriptions, including special characters (2.6+, with fallback for older Pythons) 2011-07-07 12:12:20 +02:00
aded78d9e2 Support for blip.tv/file URLs 2011-06-25 19:26:29 +02:00
7745f5d881 Basic blip.tv support 2011-06-21 22:24:58 +02:00
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
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
2fc31a4872 vimeo: Apparently, all videos in vimeo are served in ISO containers. 2011-04-20 21:29:29 -03:00
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
1e055db69c vimeo: Ignore if we are using HTTP/S or not. 2011-04-20 21:15:57 -03:00
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
43c0a396a2 Merge branch 'master' into prefer-webm 2011-04-20 21:01:31 -03:00
00f3977f77 Merge branch 'master' into vimeo 2011-04-20 21:00:46 -03:00
e26005adea Deletes duplicate entry in process_info dictionary of YahooIE 2011-04-07 23:25:47 +02:00
4b0d9eed45 Bump version number 2011-03-29 20:32:07 +02:00
3efa45c3a2 Fix upload date regexp (closes #93) 2011-03-15 20:12:10 +01:00
2727dbf78d Split a couple of lines to make the code more readable 2011-03-15 20:04:20 +01:00
e3f7e05c27 Avoid crash reported in issue #86 2011-03-15 20:03:52 +01:00
da54ed4412 Support youtube.com/e/ URLs (closes #88) 2011-02-28 19:37:58 +01:00
d8edbf3a93 Merge branch 'master' into vimeo 2011-02-26 10:09:36 -03:00
a62db07f58 Merge branch 'master' into prefer-webm 2011-02-26 10:09:18 -03:00
b58faab5e7 Bump version number 2011-02-26 00:47:29 +01:00
854cad639e Merge branch 'master' into prefer-webm 2011-02-25 20:08:33 -03:00
cb25a0e30c Merge branch 'master' into vimeo 2011-02-25 20:08:12 -03:00
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
820eedcb50 Bump version number 2011-02-25 21:54:16 +01:00
da273188f3 Catch possible exceptions when running ffprobe 2011-02-25 21:53:26 +01:00
1bd9258272 Fix stderr print when ffmpeg fails 2011-02-25 22:30:22 +02:00
c076845454 Bump version number 2011-02-25 20:12:32 +01:00
afd233c05c Update User-Agent string 2011-02-25 20:11:53 +01:00
3072fab115 Add an audio extracting PostProcessor using ffmpeg (closes #2) 2011-02-25 19:06:58 +01:00
87cbd21323 Fix date parsing for YouTube (patch by Drake Wyrm) 2011-02-25 19:05:35 +01:00
3b84a43076 Merge branch 'prefer-webm' of ssh://github.com/rbrito/youtube-dl into prefer-webm 2011-02-21 21:59:23 -03:00
2c8bedd12c Merge branch 'master' into prefer-webm 2011-02-21 21:59:08 -03:00
1a3fe4212f Merge branch 'vimeo' of ssh://github.com/rbrito/youtube-dl into vimeo 2011-02-21 21:52:48 -03:00
c4cfbdf5a5 Merge branch 'master' into vimeo 2011-02-21 21:51:33 -03:00
ef9f8451c8 Add Gergely Imreh to the author list 2011-02-20 18:01:57 +01:00
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
a4a590b5b1 Merge branch 'master' into prefer-webm 2011-02-17 09:19:12 -02:00
7f69fd3b39 Merge branch 'master' into vimeo 2011-02-17 09:17:30 -02:00
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
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
d119b54df6 Support more common YouTube playlist URLs 2011-02-12 20:19:20 +01:00
8cc98b2358 vimeo: Also accept URLs prefixed by www.
I hope that this doesn't break anything. `:)`
2011-02-04 06:15:27 -02:00
f24c674b04 Make some of the comments more descriptive. 2011-02-04 04:02:29 -02:00
58b53721af Merge branch 'master' into vimeo 2011-02-04 03:51:16 -02:00
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
16c73c2e51 Fix SyntaxError problem (oops) 2011-01-30 13:03:15 +01:00
5776c3295b Update User-Agent string 2011-01-30 12:59:18 +01:00
9e0dd8692e Bump version number 2011-01-30 12:58:01 +01:00
5aba6ea4fe Add YoutubeUserIE (code courtesy of Paweł Paprota) 2011-01-29 11:55:20 +01:00
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
92743d423a Preliminary downloading from vimeo 2011-01-29 03:59:18 -02:00
9e1ee3364a Merge branch 'master' into prefer-webm 2011-01-29 03:45:21 -02:00
e0edf1e041 Give preference to WebM formats.
This patch gives preference to formats that are Free.
2011-01-29 03:41:44 -02:00
6025795d95 Split some long lines 2011-01-28 19:59:47 +01:00
e30189021d Make the file timestamp feature optional 2011-01-28 19:59:18 +01:00
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
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
b940c84a24 Support for youtube.com/embed/XXXXX URLs (closes #63) 2011-01-25 19:00:45 +01:00
0f7099a59b Fix omission of Witold Baryluk as the Dailymotion InfoExtractor author 2011-01-21 18:18:48 +01:00
c02d8e4040 Fix dailymotion support (closes #60) 2011-01-21 18:16:33 +01:00
0f6b00b587 Improve addinfourl_wrapper for compatibility with older Python versions 2011-01-20 20:36:42 +01:00
7b531c0be6 Wrap call to addinfourl for compatibility with Python 2.4 2011-01-18 20:52:37 +01:00
0d14e225fa Omit code argument in addinfourl for Python 2.4 2011-01-18 19:16:42 +01:00
0fe64c04f8 Make the self-updating function a bit more robust 2011-01-12 21:07:56 +01:00
0d8d9877ad Add support for embedded YouTube playist URLs (closes #54) 2011-01-12 20:22:37 +01:00
8cc42e7c1a Fix "unable to rename file" Windows error (closes #56) 2011-01-12 20:21:43 +01:00
1987c2325a Add proper support for "gzip" and "deflate" encodings 2011-01-12 20:20:37 +01:00
aac3fe0f4a Fix bug in regular expression for youtu.be links 2011-01-10 12:54:14 +01:00
3fb2c487c0 Add --no-part option (closes #48) 2011-01-07 10:23:18 +01:00
d3975459d1 Remove trailing whitespace 2011-01-07 10:22:01 +01:00
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
e7cf18cb6b Add --dump-user-agent option (patch provided by Benjamin Johnson) 2011-01-03 11:47:23 +01:00
09cc744c90 Print new line before a few error messages 2011-01-03 11:22:49 +01:00
a57ed21f6d Request page compression by default, like Firefox does 2010-12-16 07:09:58 +01:00
975a91d0ac Take into account resume_len when calculating speed and ETA 2010-12-15 21:42:11 +01:00
b905e5f583 Fix erroneous "content too short" error message 2010-12-12 19:21:09 +01:00
ef4f4544a2 Remove deprecated -b option and nonworking -m option (closes #39) 2010-12-11 11:35:45 +01:00
5c1327931a Stop attempting to use get_video and detect missing formats ourselves 2010-12-11 11:34:10 +01:00
106d091e80 Do not use 0% as the starting point in resumed downloads (closes #40) 2010-12-11 11:32:33 +01:00
f83ae7816b Fix problem when requesting an existing format explicitly with -f 2010-12-09 19:57:39 +01:00
f148ea4473 Bump version number 2010-12-09 19:37:07 +01:00
7d950ca1d6 Improve temporary filename handling of special cases 2010-12-09 19:33:04 +01:00
d157d2597a Fix YoutubeIE after recent YouTube changes (closes #34) 2010-12-09 19:22:32 +01:00
e567ef93d8 Add Vasyl' Vavrychuk to the list of authors 2010-12-08 11:04:22 +01:00
27179cfdba Implemented depositfiles.com support 2010-12-08 11:18:33 +02:00
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
a9806fd83d report_extraction was never called for GenericIE 2010-12-05 20:56:27 +02:00
62cf7aaf9a Use a temporary filename to download files 2010-12-04 10:38:53 +01:00
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
f8dc441430 Bump version number 2010-11-19 19:41:09 +01:00
010ebaf783 Update User-Agent string 2010-11-19 19:40:18 +01:00
138b11f36e Rework upload date mechanism after detecting problems in several tests 2010-11-19 19:31:26 +01:00
05df0c1d4a Restore file permissions 2010-11-19 18:37:07 +01:00
b04bb07c94 Merge branch 'master' of https://github.com/psi-neamf/youtube-dl into psi-neamf 2010-11-19 18:30:58 +01:00
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
b3a27b5217 Added 'uploaddate' output sequence for YoutubeIE. 2010-11-17 20:55:30 +02:00
5e596cac0a Minor help text correction 2010-11-06 22:13:59 +01:00
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
817e8f523f Allow comments in batch file. 2010-11-06 22:21:45 +02:00
8cc4434116 Add playlist-end option (courtesy of Nevar Angelo) 2010-11-04 23:19:09 +01:00
893a13df55 Modify autoupdate URLs to match the ones from github.com 2010-10-31 15:46:58 +01:00
c34e358456 Bump version number 2010-10-31 11:28:45 +01:00
a6a61601de Update User-Agent string 2010-10-31 11:28:45 +01:00
e0c982c8d0 Load cookies if the cookie file exists when starting the program 2010-10-31 11:28:45 +01:00
331ce0a05d Use stderr for output when the output file is "-" (fixes issue #216) 2010-10-31 11:28:45 +01:00
80066952bc Add new --cookies option to be able to save cookies to disk (fixes issue #208) 2010-10-31 11:28:45 +01:00
e08878f498 Set stdout to binary mode under Windows (fixes issue #218) 2010-10-31 11:28:45 +01:00
a949a3ae6b Support "https" in YouTube video URLs (fixes issue #215) 2010-10-31 11:28:45 +01:00
7df4635faf Use HTTPS for the login URL (fixes issue #163) 2010-10-31 11:28:45 +01:00
f79007e542 Bump version number 2010-10-31 11:28:41 +01:00
ac249f421f Retry on any 5xx server error 2010-10-31 11:28:41 +01:00
e86e9474bf Treat HTTP error 500 the same way as 503 (fixes issue #209) 2010-10-31 11:28:41 +01:00
bbd4bb037a Support the -nocookie suffix in youtube domain name (fixes issue #200) 2010-10-31 11:28:41 +01:00
5c44af1875 Do not print file name on warning message (fixes issue #197) 2010-10-31 11:28:41 +01:00
33407be7d6 Fix "unable to extract uploader nickname" error with Dailymotion 2010-10-31 11:28:41 +01:00
8e686771af Decode the reason given on YouTube errors to avoid crashes (fixes issue #193) 2010-10-31 11:28:41 +01:00
2933532c5b Allow the #! notation for YouTube URLs found in many links in their website 2010-10-31 11:28:41 +01:00
6b57e8c5ac Extract the video extension from the media URL in metacafe.com 2010-10-31 11:28:41 +01:00
c6c555cf8a Fix metacafe.com downloads for some videos (fixes issue #189) 2010-10-31 11:28:41 +01:00
db7e31b853 Use unicode strings for several error messages that were missing the "u" 2010-10-31 11:28:41 +01:00
d67e097462 Abort download in case of error writing file data to disk 2010-10-31 11:28:41 +01:00
38ed13444a Improve error message on invalid output template and abort execution 2010-10-31 11:28:40 +01:00
8a9f53bebf Fix typo in report_resuming_byte doc string (fixes issue #188) 2010-10-31 11:28:40 +01:00
80cc23304f Bump version number 2010-10-31 11:28:36 +01:00
813962f85a Update user-agent string 2010-10-31 11:28:36 +01:00
109626fcc0 Fix metacafe.com code not working due to gdaKey again (fixes issue #185) 2010-10-31 11:28:36 +01:00
204c9398ab Merge Gavin van Lelyveld's patch for --playlist-start option 2010-10-31 11:28:36 +01:00
2962317dea Put back -b option as a placeholder with a warning message 2010-10-31 11:28:36 +01:00
268fb2bdd8 Consider the file downloaded if the size differs in less than 100 bytes (fixes issue #175) 2010-10-31 11:28:36 +01:00
101e0d1e91 Reorganize request code to make it a bit more robust 2010-10-31 11:28:36 +01:00
f95f29fd25 Properly detect YouTube error messages to print them on screen (fixes issue #172) 2010-10-31 11:28:36 +01:00
06f34701fe Bump version number 2010-10-31 11:28:33 +01:00
5ce7d172d7 Restore support for the get_video method, fixing many issues 2010-10-31 11:28:33 +01:00
2e3a32e4ac Restore proper support for webm formats (fixes issue #166) 2010-10-31 11:28:32 +01:00
8190e3631b Bump version number 2010-10-31 11:28:29 +01:00
e4db6fd042 Update user agent string 2010-10-31 11:28:29 +01:00
497cd3e68e Partially rewrite YouTube InfoExtractor after it stopped working
As part of the changes, the program now downloads the highest quality version
by default and uses fmt_url_map to decide which formats are really available.
2010-10-31 11:28:29 +01:00
460d8acbaa Remove some format command line options 2010-10-31 11:28:29 +01:00
9bf7fa5213 Do not check for self._downloader being None in several places 2010-10-31 11:28:29 +01:00
73f4e7afba Rename UnavailableFormatError to UnavailableVideoError 2010-10-31 11:28:29 +01:00
9715661c19 Use www. instead of uk. in the language setting webpage for YouTube 2010-10-31 11:28:29 +01:00
14912efbb7 Bump version number 2010-10-31 11:28:25 +01:00
96942e6224 Modify User-Agent and Accept headers 2010-10-31 11:28:18 +01:00
df372a655f Improve video ordinal assignment method (fixes issue #149) 2010-10-31 11:28:17 +01:00
9e9647d9a1 Add comment about weird .video extension for format 38 2010-10-31 11:28:17 +01:00
8da0080d36 Support youtu.be URLs (fixes issue #143) 2010-10-31 11:28:17 +01:00
57edaa5bac Support the watch_popup syntax in YouTube URLs 2010-10-31 11:28:17 +01:00
823fcda12a Improve swf player URL detection for RTMP (fixes issue #144) 2010-10-31 11:28:17 +01:00
f2413e6793 Add a --max-quality flag to limit the highest quality (fixes issue #145) 2010-10-31 11:28:17 +01:00
c833bb97dc Add support for "original" format in YouTube (fixes issue #155) 2010-10-31 11:28:17 +01:00
7e2dd306fe Make the Dailymotion uploader regexp more flexible because it fails sometimes 2010-10-31 11:28:17 +01:00
dea147f78e Remove unused methods from the Dailymotion InfoExtractor 2010-10-31 11:28:17 +01:00
08cf5cb80b Remove dead disclaimer/confirmation code.
I do knot know how to perform age confirmation, so just
removeing it for the time we will know how to do this.
2010-10-31 11:28:17 +01:00
4135fa4585 Add support for the Dailymotion
Based slightly (idea and one regular expression) on the old anonymous
dailymotion-dl.pl, but with fixes (more robust regular expression,
extracting author, support for domains other than .com). Simpler
due to the fact that youtube-dl provides all needed functionalities.
2010-10-31 11:28:17 +01:00
fd8ede223e Include format 43 in best quality list (fixes issue #150) 2010-10-31 11:28:17 +01:00
2b06c33d19 Improve some metavar names 2010-10-31 11:28:17 +01:00
ca6a11fa59 Forbid forward slash in win32 file names (fixes issue #147) 2010-10-31 11:28:17 +01:00
de3ed1f84a Bump version number 2010-10-31 11:28:13 +01:00
0b59bf4a5e Add webm extension to formats 43 and 45 (fixes issue #139) 2010-10-31 11:28:10 +01:00
896a6ea9e2 Fix for all-formats exception by Valentin Hilbig 2010-10-31 11:28:10 +01:00
7031008c98 Add a number of retries with tweaked patch, originally from Neil Channen 2010-10-31 11:28:03 +01:00
e616ec0ca6 Add player signature verification to rtmpdump support (fixes issue #63) 2010-10-31 11:27:01 +01:00
2a7353b87a Make -a understand dash means stdin 2010-10-31 11:27:01 +01:00
787f2a5d95 Also try no "el" option in get_video_info (fixes issue #130) 2010-10-31 11:27:01 +01:00
42e3546fb5 Increment number of downloads when the file has already been fully downloaded 2010-10-31 11:27:01 +01:00
0228ee9788 Print EOL before "unable to write data" error 2010-10-31 11:27:01 +01:00
131efd1ae0 Detect errors in video data writes 2010-10-31 11:27:01 +01:00
2bebb386b8 Make "all-formats" and "best-quality" download the version without specific format too 2010-10-31 11:27:01 +01:00
7e58d56888 Merge changes by obeythepenguin 2010-10-31 11:27:01 +01:00
554bbdc48c Bump version number 2010-10-31 11:26:57 +01:00
37dfa1e0df Also try el=vevo on YouTube if everything else fails (fixes issue #115) 2010-10-31 11:26:57 +01:00
4dd63be193 Bump version number 2010-10-31 11:26:53 +01:00
7d8d06122d Add the "ord" template parameter (fixes issue #101) 2010-10-31 11:26:53 +01:00
9177ce4d8c Support new playlist style URL (fixes issue #114) 2010-10-31 11:26:52 +01:00
ce5cafea40 Change method to detect end of playlist (fixes issue #113) 2010-10-31 11:26:52 +01:00
ae3fc475eb Bump version number 2010-10-31 11:26:48 +01:00
d063db3810 Try el=detailpage if el=embedded fails for YouTube 2010-10-31 11:26:48 +01:00
6194531831 Add Yahoo! Video InfoExtractor, merged from "obeythepenguin" 2010-10-31 11:26:48 +01:00
2ed1ddd0a0 Request video info webpage using "embedded" instead of "detailpage"
In the request for get_video_info, use el=embedded instead of el=detailpage, as
if the request was coming from an embedded video player instead of the video
webpage. This created problems for some videos, with YouTube replying with
"Invalid parameters". This fixes issue #109 and fixes issue #110.
2010-10-31 11:26:48 +01:00
eaf4a7288d Solve minor aesthetical problem in rtmpdump error messages 2010-10-31 11:26:48 +01:00
6ba562b0e4 Added --all-format option from tweaked patch (fixes issue #102) 2010-10-31 11:26:48 +01:00
131bc7651a Make the "-" output file name equivalent to /dev/stdout (fixes issue #103) 2010-10-31 11:26:48 +01:00
5caacaddc6 Bump version number 2010-10-31 11:26:42 +01:00
79f193e5d8 Do not use the final URL for -g 2010-10-31 11:26:38 +01:00
44e16fa17f Bump version number 2010-10-31 11:26:34 +01:00
d983524781 Add --no-progress option (fixes issue #98) 2010-10-31 11:26:34 +01:00
1392f3f52c Give preference to format 34 before format 5 in quality list 2010-10-31 11:26:34 +01:00
43ab0ca432 Do not error out on problems printing the file name 2010-10-31 11:26:34 +01:00
31cbdaafd4 Properly support simple titles in the newest InfoExtractors 2010-10-31 11:26:34 +01:00
bd3cdf6dc4 Do not pass URLs around in Unicode form (fixes issue #92) 2010-10-31 11:26:34 +01:00
44 changed files with 8063 additions and 1659 deletions

19
.gitignore vendored Normal file
View File

@ -0,0 +1,19 @@
*.pyc
*.pyo
*~
*.DS_Store
wine-py2exe/
py2exe.log
*.kate-swp
build/
dist/
MANIFEST
README.txt
youtube-dl.1
youtube-dl.bash-completion
youtube-dl
youtube-dl.exe
youtube-dl.tar.gz
.coverage
cover/
updates_key.pem

14
.travis.yml Normal file
View File

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

14
CHANGELOG Normal file
View File

@ -0,0 +1,14 @@
2013.01.02 Codename: GIULIA
* Add support for ComedyCentral clips <nto>
* Corrected Vimeo description fetching <Nick Daniels>
* Added the --no-post-overwrites argument <Barbu Paul - Gheorghe>
* --verbose offers more environment info
* New info_dict field: uploader_id
* New updates system, with signature checking
* New IEs: NBA, JustinTV, FunnyOrDie, TweetReel, Steam, Ustream
* Fixed IEs: BlipTv
* Fixed for Python 3 IEs: Xvideo, Youku, XNXX, Dailymotion, Vimeo, InfoQ
* Simplified IEs and test code
* Various (Python 3 and other) fixes
* Revamped and expanded tests

View File

@ -1 +1 @@
2010.02.13
2012.12.99

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

3
MANIFEST.in Normal file
View File

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

61
Makefile Normal file
View File

@ -0,0 +1,61 @@
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/ .coverage cover/ youtube-dl.tar.gz
PREFIX=/usr/local
BINDIR=$(PREFIX)/bin
MANDIR=$(PREFIX)/man
SYSCONFDIR=/etc
PYTHON=/usr/bin/env python
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:
#nosetests --with-coverage --cover-package=youtube_dl --cover-html --verbose --processes 4 test
nosetests --verbose test
tar: youtube-dl.tar.gz
.PHONY: all clean install test tar
youtube-dl: youtube_dl/*.py
zip --quiet youtube-dl youtube_dl/*.py
zip --quiet --junk-paths youtube-dl youtube_dl/__main__.py
echo '#!$(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.in
python devscripts/bash-completion.py
youtube-dl.tar.gz: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-completion
@tar -czf youtube-dl.tar.gz --transform "s|^|youtube-dl/|" --owner 0 --group 0 \
--exclude '*.DS_Store' \
--exclude '*.kate-swp' \
--exclude '*.pyc' \
--exclude '*.pyo' \
--exclude '*~' \
--exclude '__pycache' \
--exclude '.git' \
-- \
bin devscripts test youtube_dl \
CHANGELOG LICENSE README.md README.txt \
Makefile MANIFEST.in youtube-dl.1 youtube-dl.bash-completion setup.py \
youtube-dl

211
README.md Normal file
View File

@ -0,0 +1,211 @@
% 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.6, 2.7, or 3.3+, and it is not platform specific. It should work on
your Unix box, on Windows or on 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
--min-filesize SIZE Do not download any videos smaller than SIZE (e.g.
50k or 44.6m)
--max-filesize SIZE Do not download any videos larger than SIZE (e.g.
50k or 44.6m)
## 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,
%(uploader_id)s for the uploader nickname if
different, %(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/%(upl
oader)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", "opus", 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)
--recode-video FORMAT Encode the video to another format if necessary
(currently supported: mp4|flv|ogg|webm)
-k, --keep-video keeps the video file on disk after the post-
processing; the video is erased by default
--no-post-overwrites do not overwrite post-processed files; the post-
processed files are overwritten 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 `~/.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.
* If possible re-run the command with `--verbose`, and include the full output, it is really helpful to us.
* 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,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

26
devscripts/bash-completion.py Executable file
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.in"
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,33 @@
#!/usr/bin/env python3
import json
import sys
import hashlib
import urllib.request
if len(sys.argv) <= 1:
print('Specify the version number as parameter')
sys.exit()
version = sys.argv[1]
with open('update/LATEST_VERSION', 'w') as f:
f.write(version)
versions_info = json.load(open('update/versions.json'))
if 'signature' in versions_info:
del versions_info['signature']
new_version = {}
filenames = {'bin': 'youtube-dl', 'exe': 'youtube-dl.exe', 'tar': 'youtube-dl-%s.tar.gz' % version}
for key, filename in filenames.items():
print('Downloading and checksumming %s...' %filename)
url = 'http://youtube-dl.org/downloads/%s/%s' % (version, filename)
data = urllib.request.urlopen(url).read()
sha256sum = hashlib.sha256(data).hexdigest()
new_version[key] = (url, sha256sum)
versions_info['versions'][version] = new_version
versions_info['latest'] = version
json.dump(versions_info, open('update/versions.json', 'w'), indent=4, sort_keys=True)

View File

@ -0,0 +1,32 @@
#!/usr/bin/env python3
import hashlib
import shutil
import subprocess
import tempfile
import urllib.request
import json
versions_info = json.load(open('update/versions.json'))
version = versions_info['latest']
URL = versions_info['versions'][version]['bin'][0]
data = urllib.request.urlopen(URL).read()
# Read template page
with open('download.html.in', 'r', encoding='utf-8') as tmplf:
template = tmplf.read()
md5sum = hashlib.md5(data).hexdigest()
sha1sum = hashlib.sha1(data).hexdigest()
sha256sum = hashlib.sha256(data).hexdigest()
template = template.replace('@PROGRAM_VERSION@', version)
template = template.replace('@PROGRAM_URL@', URL)
template = template.replace('@PROGRAM_MD5SUM@', md5sum)
template = template.replace('@PROGRAM_SHA1SUM@', sha1sum)
template = template.replace('@PROGRAM_SHA256SUM@', sha256sum)
template = template.replace('@EXE_URL@', versions_info['versions'][version]['exe'][0])
template = template.replace('@EXE_SHA256SUM@', versions_info['versions'][version]['exe'][1])
template = template.replace('@TAR_URL@', versions_info['versions'][version]['tar'][0])
template = template.replace('@TAR_SHA256SUM@', versions_info['versions'][version]['tar'][1])
with open('download.html', 'w', encoding='utf-8') as dlf:
dlf.write(template)

View File

@ -0,0 +1,32 @@
#!/usr/bin/env python3
import rsa
import json
from binascii import hexlify
try:
input = raw_input
except NameError:
pass
versions_info = json.load(open('update/versions.json'))
if 'signature' in versions_info:
del versions_info['signature']
print('Enter the PKCS1 private key, followed by a blank line:')
privkey = b''
while True:
try:
line = input()
except EOFError:
break
if line == '':
break
privkey += line.encode('ascii') + b'\n'
privkey = rsa.PrivateKey.load_pkcs1(privkey)
signature = hexlify(rsa.pkcs1.sign(json.dumps(versions_info, sort_keys=True).encode('utf-8'), privkey, 'SHA-256')).decode()
print('signature: ' + signature)
versions_info['signature'] = signature
json.dump(versions_info, open('update/versions.json', 'w'), indent=4, sort_keys=True)

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python
# coding: utf-8
from __future__ import with_statement
import datetime
import glob
import io # For Python 2 compatibilty
import os
import re
year = str(datetime.datetime.now().year)
for fn in glob.glob('*.html*'):
with io.open(fn, encoding='utf-8') as f:
content = f.read()
newc = re.sub(u'(?P<copyright>Copyright © 2006-)(?P<year>[0-9]{4})', u'Copyright © 2006-' + year, content)
if content != newc:
tmpFn = fn + '.part'
with io.open(tmpFn, 'wt', encoding='utf-8') as outf:
outf.write(newc)
os.rename(tmpFn, fn)

20
devscripts/make_readme.py Executable 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

89
devscripts/release.sh Executable file
View File

@ -0,0 +1,89 @@
#!/bin/bash
# IMPORTANT: the following assumptions are made
# * the GH repo is on the origin remote
# * the gh-pages branch is named so locally
# * the git config user.signingkey is properly set
# You will need
# pip install coverage nose rsa
# TODO
# release notes
# make hash on local files
set -e
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 | grep -v CHANGELOG`" ]; then echo 'ERROR: the working directory is not clean; commit or stash changes'; exit 1; fi
if [ ! -f "updates_key.pem" ]; then echo 'ERROR: updates_key.pem missing'; exit 1; fi
echo "\n### First of all, testing..."
make clean
nosetests --with-coverage --cover-package=youtube_dl --cover-html test || exit 1
echo "\n### Changing version in version.py..."
sed -i "s/__version__ = '.*'/__version__ = '$version'/" youtube_dl/version.py
echo "\n### Committing CHANGELOG README.md and youtube_dl/version.py..."
make README.md
git add CHANGELOG README.md youtube_dl/version.py
git commit -m "release $version"
echo "\n### Now tagging, signing and pushing..."
git tag -s -m "Release $version" "$version"
git show "$version"
read -p "Is it good, can I push? (y/n) " -n 1
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
echo
MASTER=$(git rev-parse --abbrev-ref HEAD)
git push origin $MASTER:master
git push origin "$version"
echo "\n### OK, now it is time to build the binaries..."
REV=$(git rev-parse HEAD)
make youtube-dl youtube-dl.tar.gz
wget "http://jeromelaheurte.net:8142/download/rg3/youtube-dl/youtube-dl.exe?rev=$REV" -O youtube-dl.exe || \
wget "http://jeromelaheurte.net:8142/build/rg3/youtube-dl/youtube-dl.exe?rev=$REV" -O youtube-dl.exe
mkdir -p "build/$version"
mv youtube-dl youtube-dl.exe "build/$version"
mv youtube-dl.tar.gz "build/$version/youtube-dl-$version.tar.gz"
RELEASE_FILES="youtube-dl youtube-dl.exe youtube-dl-$version.tar.gz"
(cd build/$version/ && md5sum $RELEASE_FILES > MD5SUMS)
(cd build/$version/ && sha1sum $RELEASE_FILES > SHA1SUMS)
(cd build/$version/ && sha256sum $RELEASE_FILES > SHA2-256SUMS)
(cd build/$version/ && sha512sum $RELEASE_FILES > SHA2-512SUMS)
git checkout HEAD -- youtube-dl youtube-dl.exe
echo "\n### Signing and uploading the new binaries to youtube-dl.org..."
for f in $RELEASE_FILES; do gpg --detach-sig "build/$version/$f"; done
scp -r "build/$version" ytdl@youtube-dl.org:html/downloads/
echo "\n### Now switching to gh-pages..."
git clone --branch gh-pages --single-branch . build/gh-pages
ROOT=$(pwd)
(
set -e
ORIGIN_URL=$(git config --get remote.origin.url)
cd build/gh-pages
"$ROOT/devscripts/gh-pages/add-version.py" $version
"$ROOT/devscripts/gh-pages/sign-versions.py" < "$ROOT/updates_key.pem"
"$ROOT/devscripts/gh-pages/generate-download.py"
"$ROOT/devscripts/gh-pages/update-copyright.py"
git add *.html *.html.in update
git commit -m "release $version"
git show HEAD
read -p "Is it good, can I push? (y/n) " -n 1
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
echo
git push "$ROOT" gh-pages
git push "$ORIGIN_URL" gh-pages
)
rm -rf build
echo "Uploading to PyPi ..."
pip sdist upload
echo "\n### DONE!"

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,102 @@
#!/usr/bin/env python
import sys, os
import urllib2
import json, hashlib
def rsa_verify(message, signature, key):
from struct import pack
from hashlib import sha256
from sys import version_info
def b(x):
if version_info[0] == 2: return x
else: return x.encode('latin1')
assert(type(message) == type(b('')))
block_size = 0
n = key[0]
while n:
block_size += 1
n >>= 8
signature = pow(int(signature, 16), key[1], key[0])
raw_bytes = []
while signature:
raw_bytes.insert(0, pack("B", signature & 0xFF))
signature >>= 8
signature = (block_size - len(raw_bytes)) * b('\x00') + b('').join(raw_bytes)
if signature[0:2] != b('\x00\x01'): return False
signature = signature[2:]
if not b('\x00') in signature: return False
signature = signature[signature.index(b('\x00'))+1:]
if not signature.startswith(b('\x30\x31\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')): return False
signature = signature[19:]
if signature != sha256(message).digest(): return False
return True
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'From now on, get the binaries from http://rg3.github.com/youtube-dl/download.html, not from the git repository.\n\n')
raw_input()
filename = sys.argv[0]
UPDATE_URL = "http://rg3.github.com/youtube-dl/update/"
VERSION_URL = UPDATE_URL + 'LATEST_VERSION'
JSON_URL = UPDATE_URL + 'versions.json'
UPDATES_RSA_KEY = (0x9d60ee4d8f805312fdb15a62f87b95bd66177b91df176765d13514a0f1754bcd2057295c5b6f1d35daa6742c3ffc9a82d3e118861c207995a8031e151d863c9927e304576bc80692bc8e094896fcf11b66f3e29e04e3a71e9a11558558acea1840aec37fc396fb6b65dc81a1c4144e03bd1c011de62e3f1357b327d08426fe93, 65537)
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:
versions_info = urllib2.urlopen(JSON_URL).read().decode('utf-8')
versions_info = json.loads(versions_info)
except:
sys.exit(u'ERROR: can\'t obtain versions info. Please try again later.')
if not 'signature' in versions_info:
sys.exit(u'ERROR: the versions file is not signed or corrupted. Aborting.')
signature = versions_info['signature']
del versions_info['signature']
if not rsa_verify(json.dumps(versions_info, sort_keys=True), signature, UPDATES_RSA_KEY):
sys.exit(u'ERROR: the versions file signature is invalid. Aborting.')
version = versions_info['versions'][versions_info['latest']]
try:
urlh = urllib2.urlopen(version['exe'][0])
newcontent = urlh.read()
urlh.close()
except (IOError, OSError) as err:
sys.exit('ERROR: unable to download latest version')
newcontent_hash = hashlib.sha256(newcontent).hexdigest()
if newcontent_hash != version['exe'][1]:
sys.exit(u'ERROR: the downloaded file hash does not match. Aborting.')
try:
with open(exe + '.new', 'wb') as outf:
outf.write(newcontent)
except (IOError, OSError) as err:
sys.exit(u'ERROR: unable to write the new 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

78
setup.py Normal file
View File

@ -0,0 +1,78 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
import pkg_resources
import sys
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
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
)

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": true,
"writesubtitles": false
}

27
test/test_all_urls.py Normal file
View File

@ -0,0 +1,27 @@
#!/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 TestAllURLsMatching(unittest.TestCase):
def test_youtube_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'))
def test_youtube_extract(self):
self.assertEqual(YoutubeIE()._extract_id('http://www.youtube.com/watch?&v=BaW_jenozKc'), 'BaW_jenozKc')
self.assertEqual(YoutubeIE()._extract_id('https://www.youtube.com/watch?&v=BaW_jenozKc'), 'BaW_jenozKc')
self.assertEqual(YoutubeIE()._extract_id('https://www.youtube.com/watch?feature=player_embedded&v=BaW_jenozKc'), 'BaW_jenozKc')
if __name__ == '__main__':
unittest.main()

128
test/test_download.py Normal file
View File

@ -0,0 +1,128 @@
#!/usr/bin/env python
import errno
import hashlib
import io
import os
import json
import unittest
import sys
import hashlib
import socket
# Allow direct execution
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 *
DEF_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tests.json')
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
# 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(10)
def _try_rm(filename):
""" Remove a file if it exists """
try:
os.remove(filename)
except OSError as ose:
if ose.errno != errno.ENOENT:
raise
class FileDownloader(youtube_dl.FileDownloader):
def __init__(self, *args, **kwargs):
self.to_stderr = self.to_screen
self.processed_info_dicts = []
return youtube_dl.FileDownloader.__init__(self, *args, **kwargs)
def process_info(self, info_dict):
self.processed_info_dicts.append(info_dict)
return youtube_dl.FileDownloader.process_info(self, info_dict)
def _file_md5(fn):
with open(fn, 'rb') as f:
return hashlib.md5(f.read()).hexdigest()
with io.open(DEF_FILE, encoding='utf-8') as deff:
defs = json.load(deff)
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
parameters = json.load(pf)
class TestDownload(unittest.TestCase):
def setUp(self):
self.parameters = parameters
self.defs = defs
### Dynamically generate tests
def generator(test_case):
def test_template(self):
ie = getattr(youtube_dl.InfoExtractors, test_case['name'] + 'IE')
if not ie._WORKING:
print('Skipping: IE marked as not _WORKING')
return
if 'playlist' not in test_case and not test_case['file']:
print('Skipping: No output file specified')
return
if 'skip' in test_case:
print('Skipping: {0}'.format(test_case['skip']))
return
params = self.parameters.copy()
params.update(test_case.get('params', {}))
fd = FileDownloader(params)
fd.add_info_extractor(ie())
for ien in test_case.get('add_ie', []):
fd.add_info_extractor(getattr(youtube_dl.InfoExtractors, ien + 'IE')())
finished_hook_called = set()
def _hook(status):
if status['status'] == 'finished':
finished_hook_called.add(status['filename'])
fd.add_progress_hook(_hook)
test_cases = test_case.get('playlist', [test_case])
for tc in test_cases:
_try_rm(tc['file'])
_try_rm(tc['file'] + '.part')
_try_rm(tc['file'] + '.info.json')
try:
fd.download([test_case['url']])
for tc in test_cases:
if not test_case.get('params', {}).get('skip_download', False):
self.assertTrue(os.path.exists(tc['file']), msg='Missing file ' + tc['file'])
self.assertTrue(tc['file'] in finished_hook_called)
self.assertTrue(os.path.exists(tc['file'] + '.info.json'))
if 'md5' in tc:
md5_for_file = _file_md5(tc['file'])
self.assertEqual(md5_for_file, tc['md5'])
with io.open(tc['file'] + '.info.json', encoding='utf-8') as infof:
info_dict = json.load(infof)
for (info_field, value) in tc.get('info_dict', {}).items():
self.assertEqual(value, info_dict.get(info_field))
finally:
for tc in test_cases:
_try_rm(tc['file'])
_try_rm(tc['file'] + '.part')
_try_rm(tc['file'] + '.info.json')
return test_template
### And add them to TestDownload
for test_case in defs:
test_method = generator(test_case)
test_method.__name__ = "test_{0}".format(test_case["name"])
setattr(TestDownload, test_method.__name__, test_method)
del test_method
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.assertEqual(sanitize_filename('_n_cd26wFpw', is_id=True), '_n_cd26wFpw')
self.assertEqual(sanitize_filename('_BD_eEpuzXw', is_id=True), '_BD_eEpuzXw')
self.assertEqual(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,77 @@
#!/usr/bin/env python
# coding: utf-8
import json
import os
import sys
import unittest
# Allow direct execution
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 *
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
# 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)
class FileDownloader(youtube_dl.FileDownloader):
def __init__(self, *args, **kwargs):
youtube_dl.FileDownloader.__init__(self, *args, **kwargs)
self.to_stderr = self.to_screen
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
params = json.load(pf)
params['writeinfojson'] = True
params['skip_download'] = True
params['writedescription'] = True
TEST_ID = 'BaW_jenozKc'
INFO_JSON_FILE = TEST_ID + '.mp4.info.json'
DESCRIPTION_FILE = TEST_ID + '.mp4.description'
EXPECTED_DESCRIPTION = u'''test chars: "'/\ä↭𝕐
This is a test video for youtube-dl.
For more information, contact phihag@phihag.de .'''
class TestInfoJSON(unittest.TestCase):
def setUp(self):
# Clear old files
self.tearDown()
def test_info_json(self):
ie = youtube_dl.InfoExtractors.YoutubeIE()
fd = FileDownloader(params)
fd.add_info_extractor(ie)
fd.download([TEST_ID])
self.assertTrue(os.path.exists(INFO_JSON_FILE))
with io.open(INFO_JSON_FILE, 'r', encoding='utf-8') as jsonf:
jd = json.load(jsonf)
self.assertEqual(jd['upload_date'], u'20121002')
self.assertEqual(jd['description'], EXPECTED_DESCRIPTION)
self.assertEqual(jd['id'], TEST_ID)
self.assertEqual(jd['extractor'], 'youtube')
self.assertEqual(jd['title'], u'''youtube-dl test video "'/\ä↭𝕐''')
self.assertEqual(jd['uploader'], 'Philipp Hagemeister')
self.assertTrue(os.path.exists(DESCRIPTION_FILE))
with io.open(DESCRIPTION_FILE, 'r', encoding='utf-8') as descf:
descr = descf.read()
self.assertEqual(descr, EXPECTED_DESCRIPTION)
def tearDown(self):
if os.path.exists(INFO_JSON_FILE):
os.remove(INFO_JSON_FILE)
if os.path.exists(DESCRIPTION_FILE):
os.remove(DESCRIPTION_FILE)
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,73 @@
#!/usr/bin/env python
import sys
import unittest
import json
# Allow direct execution
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from youtube_dl.InfoExtractors import YoutubeUserIE,YoutubePlaylistIE
from youtube_dl.utils import *
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
parameters = json.load(pf)
# 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)
class FakeDownloader(object):
def __init__(self):
self.result = []
self.params = parameters
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 = YoutubeUserIE(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,57 @@
#!/usr/bin/env python
import sys
import unittest
import json
import io
import hashlib
# Allow direct execution
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from youtube_dl.InfoExtractors import YoutubeIE
from youtube_dl.utils import *
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
parameters = json.load(pf)
# 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)
class FakeDownloader(object):
def __init__(self):
self.result = []
self.params = parameters
def to_screen(self, s):
print(s)
def trouble(self, s):
raise Exception(s)
def download(self, x):
self.result.append(x)
md5 = lambda s: hashlib.md5(s.encode('utf-8')).hexdigest()
class TestYoutubeSubtitles(unittest.TestCase):
def test_youtube_subtitles(self):
DL = FakeDownloader()
DL.params['writesubtitles'] = True
IE = YoutubeIE(DL)
info_dict = IE.extract('QRS8MkLhQmM')
self.assertEqual(md5(info_dict[0]['subtitles']), 'c3228550d59116f3c29fba370b55d033')
def test_youtube_subtitles_it(self):
DL = FakeDownloader()
DL.params['writesubtitles'] = True
DL.params['subtitleslang'] = 'it'
IE = YoutubeIE(DL)
info_dict = IE.extract('QRS8MkLhQmM')
self.assertEqual(md5(info_dict[0]['subtitles']), '132a88a0daf8e1520f393eb58f1f646a')
if __name__ == '__main__':
unittest.main()

279
test/tests.json Normal file
View File

@ -0,0 +1,279 @@
[
{
"name": "Youtube",
"url": "http://www.youtube.com/watch?v=BaW_jenozKc",
"file": "BaW_jenozKc.mp4",
"info_dict": {
"title": "youtube-dl test video \"'/\\ä↭𝕐",
"uploader": "Philipp Hagemeister",
"uploader_id": "phihag",
"upload_date": "20121002",
"description": "test chars: \"'/\\ä↭𝕐\n\nThis is a test video for youtube-dl.\n\nFor more information, contact phihag@phihag.de ."
}
},
{
"name": "Dailymotion",
"md5": "392c4b85a60a90dc4792da41ce3144eb",
"url": "http://www.dailymotion.com/video/x33vw9_tutoriel-de-youtubeur-dl-des-video_tech",
"file": "x33vw9.mp4"
},
{
"name": "Metacafe",
"add_ie": ["Youtube"],
"url": "http://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": "YouPorn",
"md5": "c37ddbaaa39058c76a7e86c6813423c1",
"url": "http://www.youporn.com/watch/505835/sex-ed-is-it-safe-to-masturbate-daily/",
"file": "505835.mp4"
},
{
"name": "Pornotube",
"md5": "374dd6dcedd24234453b295209aa69b6",
"url": "http://pornotube.com/c/173/m/1689755/Marilyn-Monroe-Bathing",
"file": "1689755.flv"
},
{
"name": "YouJizz",
"md5": "07e15fa469ba384c7693fd246905547c",
"url": "http://www.youjizz.com/videos/zeichentrick-1-2189178.html",
"file": "2189178.flv"
},
{
"name": "Vimeo",
"md5": "8879b6cc097e987f02484baf890129e5",
"url": "http://vimeo.com/56015672",
"file": "56015672.mp4",
"info_dict": {
"title": "youtube-dl test video - ★ \" ' 幸 / \\ ä ↭ 𝕐",
"uploader": "Filippo Valsorda",
"uploader_id": "user7108434",
"upload_date": "20121220",
"description": "This is a test case for youtube-dl.\nFor more information, see github.com/rg3/youtube-dl\nTest chars: ★ \" ' 幸 / \\ ä ↭ 𝕐"
}
},
{
"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",
"skip": "Currently offline"
},
{
"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 }
},
{
"name": "NBA",
"url": "http://www.nba.com/video/games/nets/2012/12/04/0021200253-okc-bkn-recap.nba/index.html",
"file": "0021200253-okc-bkn-recap.nba.mp4",
"md5": "c0edcfc37607344e2ff8f13c378c88a4"
},
{
"name": "JustinTV",
"url": "http://www.twitch.tv/thegamedevhub/b/296128360",
"file": "296128360.flv",
"md5": "ecaa8a790c22a40770901460af191c9a"
},
{
"name": "MyVideo",
"url": "http://www.myvideo.de/watch/8229274/bowling_fail_or_win",
"file": "8229274.flv",
"md5": "2d2753e8130479ba2cb7e0a37002053e"
},
{
"name": "Escapist",
"url": "http://www.escapistmagazine.com/videos/view/the-escapist-presents/6618-Breaking-Down-Baldurs-Gate",
"file": "6618-Breaking-Down-Baldurs-Gate.flv",
"md5": "c6793dbda81388f4264c1ba18684a74d"
},
{
"name": "GooglePlus",
"url": "https://plus.google.com/u/0/108897254135232129896/posts/ZButuJc6CtH",
"file": "ZButuJc6CtH.flv"
},
{
"name": "FunnyOrDie",
"url": "http://www.funnyordie.com/videos/0732f586d7/heart-shaped-box-literal-video-version",
"file": "0732f586d7.mp4",
"md5": "f647e9e90064b53b6e046e75d0241fbd"
},
{
"name": "TweetReel",
"url": "http://tweetreel.com/?77smq",
"file": "77smq.mov",
"md5": "56b4d9ca9de467920f3f99a6d91255d6",
"info_dict": {
"uploader": "itszero",
"uploader_id": "itszero",
"upload_date": "20091225",
"description": "Installing Gentoo Linux on Powerbook G4, it turns out the sleep indicator becomes HDD activity indicator :D"
}
},
{
"name": "Steam",
"url": "http://store.steampowered.com/video/105600/",
"playlist": [
{
"file": "81300.flv",
"md5": "f870007cee7065d7c76b88f0a45ecc07",
"info_dict": {
"title": "Terraria 1.1 Trailer"
}
},
{
"file": "80859.flv",
"md5": "61aaf31a5c5c3041afb58fb83cbb5751",
"info_dict": {
"title": "Terraria Trailer"
}
}
]
},
{
"name": "Ustream",
"url": "http://www.ustream.tv/recorded/20274954",
"file": "20274954.flv",
"md5": "088f151799e8f572f84eb62f17d73e5c",
"info_dict": {
"title": "Young Americans for Liberty February 7, 2012 2:28 AM"
}
},
{
"name": "InfoQ",
"url": "http://www.infoq.com/presentations/A-Few-of-My-Favorite-Python-Things",
"file": "12-jan-pythonthings.mp4",
"info_dict": {
"title": "A Few of My Favorite [Python] Things"
},
"params": {
"skip_download": true
}
},
{
"name": "ComedyCentral",
"url": "http://www.thedailyshow.com/watch/thu-december-13-2012/kristen-stewart",
"file": "422212.mp4",
"md5": "4e2f5cb088a83cd8cdb7756132f9739d",
"info_dict": {
"title": "thedailyshow-kristen-stewart part 1"
}
},
{
"name": "RBMARadio",
"url": "http://www.rbmaradio.com/shows/ford-lopatin-live-at-primavera-sound-2011",
"file": "ford-lopatin-live-at-primavera-sound-2011.mp3",
"md5": "6bc6f9bcb18994b4c983bc3bf4384d95",
"info_dict": {
"title": "Live at Primavera Sound 2011",
"description": "Joel Ford and Daniel \u2019Oneohtrix Point Never\u2019 Lopatin fly their midified pop extravaganza to Spain. Live at Primavera Sound 2011.",
"uploader": "Ford & Lopatin",
"uploader_id": "ford-lopatin",
"location": "Spain"
}
},
{
"name": "Facebook",
"url": "https://www.facebook.com/photo.php?v=120708114770723",
"file": "120708114770723.mp4",
"md5": "48975a41ccc4b7a581abd68651c1a5a8",
"info_dict": {
"title": "PEOPLE ARE AWESOME 2013",
"duration": 279
}
},
{
"name": "EightTracks",
"url": "http://8tracks.com/ytdl/youtube-dl-test-tracks-a",
"playlist": [
{
"file": "11885610.m4a",
"md5": "96ce57f24389fc8734ce47f4c1abcc55",
"info_dict": {
"title": "youtue-dl project<>\"' - youtube-dl test track 1 \"'/\\\u00e4\u21ad",
"uploader_id": "ytdl"
}
},
{
"file": "11885608.m4a",
"md5": "4ab26f05c1f7291ea460a3920be8021f",
"info_dict": {
"title": "youtube-dl project - youtube-dl test track 2 \"'/\\\u00e4\u21ad",
"uploader_id": "ytdl"
}
},
{
"file": "11885679.m4a",
"md5": "d30b5b5f74217410f4689605c35d1fd7",
"info_dict": {
"title": "youtube-dl project as well - youtube-dl test track 3 \"'/\\\u00e4\u21ad"
}
},
{
"file": "11885680.m4a",
"md5": "4eb0a669317cd725f6bbd336a29f923a",
"info_dict": {
"title": "youtube-dl project as well - youtube-dl test track 4 \"'/\\\u00e4\u21ad"
}
},
{
"file": "11885682.m4a",
"md5": "1893e872e263a2705558d1d319ad19e8",
"info_dict": {
"title": "PH - youtube-dl test track 5 \"'/\\\u00e4\u21ad"
}
},
{
"file": "11885683.m4a",
"md5": "b673c46f47a216ab1741ae8836af5899",
"info_dict": {
"title": "PH - youtube-dl test track 6 \"'/\\\u00e4\u21ad"
}
},
{
"file": "11885684.m4a",
"md5": "1d74534e95df54986da7f5abf7d842b7",
"info_dict": {
"title": "phihag - youtube-dl test track 7 \"'/\\\u00e4\u21ad"
}
},
{
"file": "11885685.m4a",
"md5": "f081f47af8f6ae782ed131d38b9cd1c0",
"info_dict": {
"title": "phihag - youtube-dl test track 8 \"'/\\\u00e4\u21ad"
}
}
]
}
]

1744
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,817 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import math
import io
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.
keepvideo: Keep the video file after post-processing
min_filesize: Skip files smaller than this size
max_filesize: Skip files larger than this size
"""
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._progress_hooks = []
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, tb=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.
tb, if given, is additional traceback information.
"""
if message is not None:
self.to_stderr(message)
if self.params.get('verbose'):
if tb is None:
tb_data = traceback.format_list(traceback.extract_stack())
tb = u''.join(tb_data)
self.to_stderr(tb)
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)
with io.open(encodeFilename(descfn), 'w', encoding='utf-8') as descfile:
descfile.write(info_dict['description'])
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)
with io.open(encodeFilename(srtfn), 'w', encoding='utf-8') as srtfile:
srtfile.write(info_dict['subtitles'])
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_info_dict = dict((k, v) for k,v in info_dict.items() if not k in ['urlhandle'])
write_json_file(json_info_dict, encodeFilename(infofn))
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.to_stderr(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
try:
videos = ie.extract(url)
except ExtractorError as de: # An error we somewhat expected
self.trouble(u'ERROR: ' + compat_str(de), de.format_traceback())
break
except Exception as e:
if self.params.get('ignoreerrors', False):
self.trouble(u'ERROR: ' + compat_str(e), tb=compat_str(traceback.format_exc()))
break
else:
raise
if len(videos or []) > 1 and self.fixed_template():
raise SameFileError(self.params['outtmpl'])
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 all the postprocessors on the given file."""
info = dict(ie_info)
info['filepath'] = filename
keep_video = None
for pp in self._pps:
try:
keep_video_wish,new_info = pp.run(info)
if keep_video_wish is not None:
if keep_video_wish:
keep_video = keep_video_wish
elif keep_video is None:
# No clear decision yet, let IE decide
keep_video = keep_video_wish
except PostProcessingError as e:
self.to_stderr(u'ERROR: ' + e.msg)
if keep_video is False and not self.params.get('keepvideo', False):
try:
self.to_stderr(u'Deleting original file %s (pass -k to keep)' % filename)
os.remove(encodeFilename(filename))
except (IOError, OSError):
self.to_stderr(u'WARNING: Unable to remove downloaded video file')
def _download_with_rtmpdump(self, filename, url, player_url, page_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', '-r', url, '-o', tmpfilename]
if player_url is not None:
basic_args += ['-W', player_url]
if page_url is not None:
basic_args += ['--pageUrl', page_url]
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:
fsize = os.path.getsize(encodeFilename(tmpfilename))
self.to_screen(u'\r[rtmpdump] %s bytes' % fsize)
self.try_rename(tmpfilename, filename)
self._hook_progress({
'downloaded_bytes': fsize,
'total_bytes': fsize,
'filename': filename,
'status': 'finished',
})
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']
# 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)
self._hook_progress({
'filename': filename,
'status': 'finished',
})
return True
# Attempt to download using rtmpdump
if url.startswith('rtmp'):
return self._download_with_rtmpdump(filename, url,
info_dict.get('player_url', None),
info_dict.get('page_url', None))
tmpfilename = self.temp_name(filename)
stream = None
# Do not include the Accept-Encoding header
headers = {'Youtubedl-no-compression': 'True'}
if 'user_agent' in info_dict:
headers['Youtubedl-user-agent'] = info_dict['user_agent']
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)
self._hook_progress({
'filename': filename,
'status': 'finished',
})
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
min_data_len = self.params.get("min_filesize", None)
max_data_len = self.params.get("max_filesize", None)
if min_data_len is not None and data_len < min_data_len:
self.to_screen(u'\r[download] File is smaller than min-filesize (%s bytes < %s bytes). Aborting.' % (data_len, min_data_len))
return False
if max_data_len is not None and data_len > max_data_len:
self.to_screen(u'\r[download] File is larger than max-filesize (%s bytes > %s bytes). Aborting.' % (data_len, max_data_len))
return False
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)
self._hook_progress({
'downloaded_bytes': byte_counter,
'total_bytes': data_len,
'tmpfilename': tmpfilename,
'filename': filename,
'status': 'downloading',
})
# 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))
self._hook_progress({
'downloaded_bytes': byte_counter,
'total_bytes': byte_counter,
'filename': filename,
'status': 'finished',
})
return True
def _hook_progress(self, status):
for ph in self._progress_hooks:
ph(status)
def add_progress_hook(self, ph):
""" ph gets called on download progress, with a dictionary with the entries
* filename: The final filename
* status: One of "downloading" and "finished"
It can also have some of the following entries:
* downloaded_bytes: Bytes on disks
* total_bytes: Total bytes, None if unknown
* tmpfilename: The filename we're currently writing to
Hooks are guaranteed to be called at least once (with status "finished")
if the download is successful.
"""
self._progress_hooks.append(ph)

3952
youtube_dl/InfoExtractors.py Executable file

File diff suppressed because it is too large Load Diff

232
youtube_dl/PostProcessor.py Normal file
View File

@ -0,0 +1,232 @@
#!/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.
This method returns a tuple, the first element of which describes
whether the original file should be kept (i.e. not deleted - None for
no preference), and the second of which is the updated information.
In addition, this method may raise a PostProcessingError
exception if post processing fails.
"""
return None, information # by default, keep file and do nothing
class FFmpegPostProcessorError(PostProcessingError):
pass
class AudioConversionError(PostProcessingError):
pass
class FFmpegPostProcessor(PostProcessor):
def __init__(self,downloader=None):
PostProcessor.__init__(self, downloader)
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 run_ffmpeg(self, path, out_path, opts):
if not self._exes['ffmpeg'] and not self._exes['avconv']:
raise FFmpegPostProcessorError(u'ffmpeg or avconv not found. Please install one.')
cmd = ([self._exes['avconv'] or self._exes['ffmpeg'], '-y', '-i', encodeFilename(path)]
+ opts +
[encodeFilename(self._ffmpeg_filename_argument(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 FFmpegPostProcessorError(msg.decode('utf-8', 'replace'))
def _ffmpeg_filename_argument(self, fn):
# ffmpeg broke --, see https://ffmpeg.org/trac/ffmpeg/ticket/2127 for details
if fn.startswith(u'-'):
return u'./' + fn
return fn
class FFmpegExtractAudioPP(FFmpegPostProcessor):
def __init__(self, downloader=None, preferredcodec=None, preferredquality=None, nopostoverwrites=False):
FFmpegPostProcessor.__init__(self, downloader)
if preferredcodec is None:
preferredcodec = 'best'
self._preferredcodec = preferredcodec
self._preferredquality = preferredquality
self._nopostoverwrites = nopostoverwrites
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(self._ffmpeg_filename_argument(path))]
handle = subprocess.Popen(cmd, stderr=compat_subprocess_get_DEVNULL(), 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.decode('ascii', 'ignore').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]
opts = ['-vn'] + acodec_opts + more_opts
try:
FFmpegPostProcessor.run_ffmpeg(self, path, out_path, opts)
except FFmpegPostProcessorError as err:
raise AudioConversionError(err.message)
def run(self, information):
path = information['filepath']
filecodec = self.get_audio_codec(path)
if filecodec is None:
raise PostProcessingError(u'WARNING: unable to obtain file audio codec with ffprobe')
more_opts = []
if self._preferredcodec == 'best' or self._preferredcodec == filecodec or (self._preferredcodec == 'm4a' and filecodec == 'aac'):
if filecodec == 'aac' and self._preferredcodec in ['m4a', 'best']:
# Lossless, but in another container
acodec = 'copy'
extension = 'm4a'
more_opts = [self._exes['avconv'] and '-bsf:a' or '-absf', 'aac_adtstoasc']
elif filecodec in ['aac', 'mp3', 'vorbis', 'opus']:
# 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', 'opus': 'opus', '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
try:
if self._nopostoverwrites and os.path.exists(encodeFilename(new_path)):
self._downloader.to_screen(u'[youtube] Post-process file %s exists, skipping' % new_path)
else:
self._downloader.to_screen(u'[' + (self._exes['avconv'] and 'avconv' or 'ffmpeg') + '] Destination: ' + new_path)
self.run_ffmpeg(path, new_path, acodec, more_opts)
except:
etype,e,tb = sys.exc_info()
if isinstance(e, AudioConversionError):
msg = u'audio conversion failed: ' + e.message
else:
msg = u'error running ' + (self._exes['avconv'] and 'avconv' or 'ffmpeg')
raise PostProcessingError(msg)
# 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')
information['filepath'] = new_path
return False,information
class FFmpegVideoConvertor(FFmpegPostProcessor):
def __init__(self, downloader=None,preferedformat=None):
super(FFmpegVideoConvertor, self).__init__(downloader)
self._preferedformat=preferedformat
def run(self, information):
path = information['filepath']
prefix, sep, ext = path.rpartition(u'.')
outpath = prefix + sep + self._preferedformat
if information['ext'] == self._preferedformat:
self._downloader.to_screen(u'[ffmpeg] Not converting video file %s - already is in target format %s' % (path, self._preferedformat))
return True,information
self._downloader.to_screen(u'['+'ffmpeg'+'] Converting video from %s to %s, Destination: ' % (information['ext'], self._preferedformat) +outpath)
self.run_ffmpeg(path, outpath, [])
information['filepath'] = outpath
information['format'] = self._preferedformat
information['ext'] = self._preferedformat
return False,information

518
youtube_dl/__init__.py Normal file
View File

@ -0,0 +1,518 @@
#!/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',
'Dave Vasilevsky',
'Jaime Marquínez Ferrándiz',
'Jeff Crouse',
)
__license__ = 'Public Domain'
import getpass
import optparse
import os
import re
import shlex
import socket
import subprocess
import sys
import warnings
import platform
from .utils import *
from .update import update_self
from .version import __version__
from .FileDownloader import *
from .InfoExtractors import gen_extractors
from .PostProcessor import *
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)
selection.add_option('--min-filesize', metavar='SIZE', dest='min_filesize', help="Do not download any videos smaller than SIZE (e.g. 50k or 44.6m)", default=None)
selection.add_option('--max-filesize', metavar='SIZE', dest='max_filesize', help="Do not download any videos larger than SIZE (e.g. 50k or 44.6m)", 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, %(uploader_id)s for the uploader nickname if different, %(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", "opus", 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('--recode-video', metavar='FORMAT', dest='recodevideo', default=None,
help='Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm)')
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')
postproc.add_option('--no-post-overwrites', action='store_true', dest='nopostoverwrites', default=False,
help='do not overwrite post-processed files; the post-processed files are overwritten 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 _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.access(opts.cookiefile, os.R_OK):
jar.load()
except (IOError, OSError) as err:
if opts.verbose:
traceback.print_exc()
sys.stderr.write(u'ERROR: unable to open cookie file\n')
sys.exit(101)
# 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 = [url for url in all_urls if ie.suitable(url)]
all_urls = [url for url in all_urls if url not in matchedUrls]
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.min_filesize is not None:
numeric_limit = FileDownloader.parse_bytes(opts.min_filesize)
if numeric_limit is None:
parser.error(u'invalid min_filesize specified')
opts.min_filesize = numeric_limit
if opts.max_filesize is not None:
numeric_limit = FileDownloader.parse_bytes(opts.max_filesize)
if numeric_limit is None:
parser.error(u'invalid max_filesize specified')
opts.max_filesize = 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', 'm4a', 'opus', 'vorbis', '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')
if opts.recodevideo is not None:
if opts.recodevideo not in ['mp4', 'flv', 'webm', 'ogg']:
parser.error(u'invalid video recode format specified')
if sys.version_info < (3,):
# In Python 2, sys.argv is a bytestring (also note http://bugs.python.org/issue2128 for Windows systems)
if opts.outtmpl is not None:
opts.outtmpl = opts.outtmpl.decode(preferredencoding())
outtmpl =((opts.outtmpl is not None and opts.outtmpl)
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')
# 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': outtmpl,
'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,
'keepvideo': opts.keepvideo,
'min_filesize': opts.min_filesize,
'max_filesize': opts.max_filesize
})
if opts.verbose:
fd.to_screen(u'[debug] youtube-dl version ' + __version__)
try:
sp = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], stdout=subprocess.PIPE, stderr=subprocess.PIPE,
cwd=os.path.dirname(os.path.abspath(__file__)))
out, err = sp.communicate()
out = out.decode().strip()
if re.match('[0-9a-f]+', out):
fd.to_screen(u'[debug] Git HEAD: ' + out)
except:
pass
fd.to_screen(u'[debug] Python version %s - %s' %(platform.python_version(), platform.platform()))
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, nopostoverwrites=opts.nopostoverwrites))
if opts.recodevideo:
fd.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.recodevideo))
# Update version
if opts.update_self:
update_self(fd.to_screen, opts.verbose, 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()

160
youtube_dl/update.py Normal file
View File

@ -0,0 +1,160 @@
import json
import traceback
import hashlib
from zipimport import zipimporter
from .utils import *
from .version import __version__
def rsa_verify(message, signature, key):
from struct import pack
from hashlib import sha256
from sys import version_info
def b(x):
if version_info[0] == 2: return x
else: return x.encode('latin1')
assert(type(message) == type(b('')))
block_size = 0
n = key[0]
while n:
block_size += 1
n >>= 8
signature = pow(int(signature, 16), key[1], key[0])
raw_bytes = []
while signature:
raw_bytes.insert(0, pack("B", signature & 0xFF))
signature >>= 8
signature = (block_size - len(raw_bytes)) * b('\x00') + b('').join(raw_bytes)
if signature[0:2] != b('\x00\x01'): return False
signature = signature[2:]
if not b('\x00') in signature: return False
signature = signature[signature.index(b('\x00'))+1:]
if not signature.startswith(b('\x30\x31\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')): return False
signature = signature[19:]
if signature != sha256(message).digest(): return False
return True
def update_self(to_screen, verbose, filename):
"""Update the program file with the latest version from the repository"""
UPDATE_URL = "http://rg3.github.com/youtube-dl/update/"
VERSION_URL = UPDATE_URL + 'LATEST_VERSION'
JSON_URL = UPDATE_URL + 'versions.json'
UPDATES_RSA_KEY = (0x9d60ee4d8f805312fdb15a62f87b95bd66177b91df176765d13514a0f1754bcd2057295c5b6f1d35daa6742c3ffc9a82d3e118861c207995a8031e151d863c9927e304576bc80692bc8e094896fcf11b66f3e29e04e3a71e9a11558558acea1840aec37fc396fb6b65dc81a1c4144e03bd1c011de62e3f1357b327d08426fe93, 65537)
if not isinstance(globals().get('__loader__'), zipimporter) and not hasattr(sys, "frozen"):
to_screen(u'It looks like you installed youtube-dl with pip, setup.py or a tarball. Please use that to update.')
return
# Check if there is a new version
try:
newversion = compat_urllib_request.urlopen(VERSION_URL).read().decode('utf-8').strip()
except:
if verbose: to_screen(compat_str(traceback.format_exc()))
to_screen(u'ERROR: can\'t find the current version. Please try again later.')
return
if newversion == __version__:
to_screen(u'youtube-dl is up-to-date (' + __version__ + ')')
return
# Download and check versions info
try:
versions_info = compat_urllib_request.urlopen(JSON_URL).read().decode('utf-8')
versions_info = json.loads(versions_info)
except:
if verbose: to_screen(compat_str(traceback.format_exc()))
to_screen(u'ERROR: can\'t obtain versions info. Please try again later.')
return
if not 'signature' in versions_info:
to_screen(u'ERROR: the versions file is not signed or corrupted. Aborting.')
return
signature = versions_info['signature']
del versions_info['signature']
if not rsa_verify(json.dumps(versions_info, sort_keys=True).encode('utf-8'), signature, UPDATES_RSA_KEY):
to_screen(u'ERROR: the versions file signature is invalid. Aborting.')
return
to_screen(u'Updating to version ' + versions_info['latest'] + '...')
version = versions_info['versions'][versions_info['latest']]
if version.get('notes'):
to_screen(u'PLEASE NOTE:')
for note in version['notes']:
to_screen(note)
if not os.access(filename, os.W_OK):
to_screen(u'ERROR: no write permissions on %s' % filename)
return
# Py2EXE
if hasattr(sys, "frozen"):
exe = os.path.abspath(filename)
directory = os.path.dirname(exe)
if not os.access(directory, os.W_OK):
to_screen(u'ERROR: no write permissions on %s' % directory)
return
try:
urlh = compat_urllib_request.urlopen(version['exe'][0])
newcontent = urlh.read()
urlh.close()
except (IOError, OSError) as err:
if verbose: to_screen(compat_str(traceback.format_exc()))
to_screen(u'ERROR: unable to download latest version')
return
newcontent_hash = hashlib.sha256(newcontent).hexdigest()
if newcontent_hash != version['exe'][1]:
to_screen(u'ERROR: the downloaded file hash does not match. Aborting.')
return
try:
with open(exe + '.new', 'wb') as outf:
outf.write(newcontent)
except (IOError, OSError) as err:
if verbose: to_screen(compat_str(traceback.format_exc()))
to_screen(u'ERROR: unable to write the new version')
return
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:
if verbose: to_screen(compat_str(traceback.format_exc()))
to_screen(u'ERROR: unable to overwrite current version')
return
# Zip unix package
elif isinstance(globals().get('__loader__'), zipimporter):
try:
urlh = compat_urllib_request.urlopen(version['bin'][0])
newcontent = urlh.read()
urlh.close()
except (IOError, OSError) as err:
if verbose: to_screen(compat_str(traceback.format_exc()))
to_screen(u'ERROR: unable to download latest version')
return
newcontent_hash = hashlib.sha256(newcontent).hexdigest()
if newcontent_hash != version['bin'][1]:
to_screen(u'ERROR: the downloaded file hash does not match. Aborting.')
return
try:
with open(filename, 'wb') as outf:
outf.write(newcontent)
except (IOError, OSError) as err:
if verbose: to_screen(compat_str(traceback.format_exc()))
to_screen(u'ERROR: unable to overwrite current version')
return
to_screen(u'Updated youtube-dl. Restart youtube-dl to use the new version.')

558
youtube_dl/utils.py Normal file
View File

@ -0,0 +1,558 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import gzip
import io
import json
import locale
import os
import re
import sys
import traceback
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 subprocess import DEVNULL
compat_subprocess_get_DEVNULL = lambda: DEVNULL
except ImportError:
compat_subprocess_get_DEVNULL = lambda: open(os.path.devnull, 'w')
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)
# In Python 2.x, json.dump expects a bytestream.
# In Python 3.x, it writes to a character stream
if sys.version_info < (3,0):
def write_json_file(obj, fn):
with open(fn, 'wb') as f:
json.dump(obj, f)
else:
def write_json_file(obj, fn):
with open(fn, 'w', encoding='utf-8') as f:
json.dump(obj, f)
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 AttrParser(compat_html_parser.HTMLParser):
"""Modified HTMLParser that isolates a tag with the specified attribute"""
def __init__(self, attribute, value):
self.attribute = attribute
self.value = value
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 self.attribute in attrs and attrs[self.attribute] == self.value:
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()
# Hack for https://github.com/rg3/youtube-dl/issues/662
if sys.version_info < (2, 7, 3):
AttrParser.parse_endtag = (lambda self, i:
i + len("</scr'+'ipt>")
if self.rawdata[i:].startswith("</scr'+'ipt>")
else compat_html_parser.HTMLParser.parse_endtag(self, i))
def get_element_by_id(id, html):
"""Return the content of the tag with the specified ID in the passed HTML document"""
return get_element_by_attribute("id", id, html)
def get_element_by_attribute(attribute, value, html):
"""Return the content of the tag with the specified attribute in the passed HTML document"""
parser = AttrParser(attribute, value)
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(r'\s*<\s*br\s*/?\s*>\s*', '\n', html)
html = re.sub(r'<\s*/\s*p\s*>\s*<\s*p[^>]*>', '\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:
encoding = sys.getfilesystemencoding()
if encoding is None:
encoding = 'utf-8'
return s.encode(encoding, 'ignore')
class ExtractorError(Exception):
"""Error during info extraction."""
def __init__(self, msg, tb=None):
""" tb, if given, is the original traceback (so that it can be printed out). """
super(ExtractorError, self).__init__(msg)
self.traceback = tb
def format_traceback(self):
if self.traceback is None:
return None
return u''.join(traceback.format_tb(self.traceback))
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.
"""
def __init__(self, msg):
self.msg = msg
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 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,v in std_headers.items():
if h in req.headers:
del req.headers[h]
req.add_header(h, v)
if 'Youtubedl-no-compression' in req.headers:
if 'Accept-encoding' in req.headers:
del req.headers['Accept-encoding']
del req.headers['Youtubedl-no-compression']
if 'Youtubedl-user-agent' in req.headers:
if 'User-agent' in req.headers:
del req.headers['User-agent']
req.headers['User-agent'] = req.headers['Youtubedl-user-agent']
del req.headers['Youtubedl-user-agent']
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__ = '2013.02.02'