Compare commits

...

1280 Commits

Author SHA1 Message Date
Philipp Hagemeister
f3711edcf1 release 2015.12.13 2015-12-13 10:52:59 +01:00
Yen Chi Hsuan
22d07ba4e4 [infoq] Fix extraction for HTTP URLs (closes #7739) 2015-12-13 17:29:27 +08:00
Yen Chi Hsuan
f6abca506e [nowvideo] Skip deleted test case 2015-12-13 15:43:20 +08:00
Yen Chi Hsuan
b5424acdb9 [novamov] Improve existence checking 2015-12-13 15:43:20 +08:00
Yen Chi Hsuan
47c7f3d995 [novamov] Fix filekey extraction (closes #7764) 2015-12-13 15:43:20 +08:00
Sergey M․
0014ffa829 [funimation] Improve login 2015-12-13 07:17:42 +06:00
Sergey M․
c03943f394 Credit @Slyneth for funimation (#7775) 2015-12-12 15:19:23 +06:00
Yen Chi Hsuan
deb1e8d20e [youku] Put the missing item to get_hd 2015-12-12 15:49:19 +08:00
Yen Chi Hsuan
174964a7bc Credit @Celthi for fixing Youku extractor 2015-12-12 15:34:40 +08:00
Yen Chi Hsuan
9c568178fb Merge branch 'Celthi-youku_bugfix' 2015-12-12 15:26:43 +08:00
Yen Chi Hsuan
dbb7d7e26c [youku] Reorder format items 2015-12-12 15:24:58 +08:00
Yen Chi Hsuan
ade2340971 [youku] Simplify 2015-12-12 15:19:14 +08:00
Yen Chi Hsuan
4d77550cf0 [youku] Fix tests 2015-12-12 14:57:14 +08:00
Yen Chi Hsuan
c683454e7e [youku] MD5 is unstable 2015-12-12 14:48:46 +08:00
Yen Chi Hsuan
f133fd326b [youku] Cleanup and PEP8 2015-12-12 14:41:53 +08:00
Yen Chi Hsuan
1faa66f005 Merge branch 'youku_bugfix' of https://github.com/Celthi/youtube-dl into Celthi-youku_bugfix 2015-12-12 14:36:29 +08:00
Yen Chi Hsuan
8773f3158f [safari] Use postdata_urlencode (#7465) 2015-12-12 14:28:05 +08:00
Celthi
7e37c39485 merge data1 and data2 2015-12-12 11:26:15 +08:00
Celthi
14c17cafa1 add support to video protected by password 2015-12-12 11:21:44 +08:00
Celthi
8696a7fd13 fix the keyerror(mp4hd), todo support download the video protected by password 2015-12-12 10:44:21 +08:00
Sergey M․
d63cfc3f0f [beeg] API v5 (Closes #7846) 2015-12-12 02:52:20 +06:00
Sergey M․
f377f44dae [funimation] Improve extraction 2015-12-12 01:02:54 +06:00
Sergey M․
0b1bb1ac3a [funimation] Add test for promotional video 2015-12-12 00:52:00 +06:00
Sergey M․
f208e52a76 [funimation] Fix promotional videos extraction 2015-12-12 00:48:09 +06:00
Sergey M․
b091529a3c [funimation] Extend _VALID_URL to match promotional videos 2015-12-12 00:43:03 +06:00
Sergey M․
b323a3cbff [funimation] Remove unused import 2015-12-12 00:39:44 +06:00
Sergey M․
b59623ef43 [funimation] Use mobile webpage for workaround hulu error 2015-12-12 00:38:58 +06:00
Sergey M․
9c163950da [funimation] Improve _VALID_URL 2015-12-11 23:20:10 +06:00
Sergey M․
d357bbd375 [funimation] Update test 2015-12-11 23:06:44 +06:00
Sergey M?
f542a3d26b [funimation] Improve extraction (Closes #7775) 2015-12-11 23:00:38 +06:00
Sergey M?
59a4ff482a [funimation] Real UA is required for login 2015-12-11 23:00:37 +06:00
Sergey M?
40ca5b04f4 [funimation] Remove unnecessary login form field 2015-12-11 23:00:37 +06:00
Sergey M?
411e5b88c9 [funimation] Fix login message 2015-12-11 23:00:37 +06:00
Sergey M?
b4c299bad0 [funimation] PEP 8 2015-12-11 23:00:36 +06:00
Muratcan Simsek
ab4bdc913f [funimation] Add new extractor
Update funimation.py

Update funimation.py

Removed unnecessary lines.

Update funimation.py

Added thumbnail and description.

Filename improvement.

fixed TEST.
2015-12-11 23:00:35 +06:00
remitamine
1fe248a51b Merge pull request #7833 from remitamine/ooyala
[ooyala] improve extraction
2015-12-11 17:55:32 +01:00
remitamine
2559b9d017 [wdr] extract all formats(closes #7788) 2015-12-11 17:31:33 +01:00
Sergey M․
4db43567e8 [downloader/f4m] Decode manifest before fixing 2015-12-11 20:28:44 +06:00
Celthi
5333842a1d According the blog and you-get fixed the issues #7627. 2015-12-11 20:08:14 +08:00
Celthi
98c3806b15 fix some not important codesnips 2015-12-11 19:18:14 +08:00
Yen Chi Hsuan
b6afc225c8 [vevo] Use _download_smil to provide informative error messages 2015-12-11 19:16:51 +08:00
Yen Chi Hsuan
ad30dc1e20 [vevo] Allow calling API without https
Not all proxies allow CONNECT
2015-12-11 19:07:13 +08:00
Yen Chi Hsuan
ff51983e15 [vevo] Handle videos without video_info (#7802) 2015-12-11 18:52:03 +08:00
Celthi
fdf01663d1 able to download first part of the video, but fail in the left part 2015-12-11 17:48:40 +08:00
Yen Chi Hsuan
4b94288301 [vevo] Use _match_id 2015-12-11 17:32:29 +08:00
Yen Chi Hsuan
4bf99ade15 [vevo] Catch the georestriction message (#7802) 2015-12-11 14:25:01 +08:00
remitamine
75ed53320e [ooyala] improve extraction 2015-12-10 19:08:16 +01:00
Sergey M․
17b786ae73 [downloader/f4m] Fix malformed manifests (Closes #7823) 2015-12-10 22:59:50 +06:00
Sergey M
dfd42a43c3 Merge pull request #7821 from joksnet/patch-1
[FFmpegPostProcessor] Default of prefer ffmpeg
2015-12-10 22:10:20 +06:00
Philipp Hagemeister
f7b8dd63f0 release 2015.12.10 2015-12-10 17:05:13 +01:00
Sergey M․
a8abf124c8 [dailymotion] Add subtitles test URL for reference 2015-12-10 21:54:48 +06:00
Sergey M․
176ccefcd8 [pbs] PEP 8 2015-12-10 21:33:40 +06:00
Sergey M․
cbd2ffd031 [dailymotion] Fix subtitles extraction 2015-12-10 21:29:07 +06:00
Sergey M․
0b534d2adc [dailymotion] Restrict player v5 regex (Closes #7826) 2015-12-10 21:27:47 +06:00
Sergey M․
526a20bd16 [pbs] Clarify member stations' URLs 2015-12-10 21:04:26 +06:00
Celthi
51094b1b08 add cookie and referer in headers, change the video url 2015-12-10 21:42:12 +08:00
Philipp Hagemeister
f1ac2033ab Merge pull request #7827 from habi/master
Updating README.md
2015-12-10 13:54:18 +01:00
David Haberthür
a1b8d815f5 Reverting markup changes 2015-12-10 13:45:53 +01:00
David Haberthür
8b756bd98e Merge branch 'update-readme' 2015-12-10 13:20:25 +01:00
David Haberthür
46047c58d0 Updating README.md
- Harmonizing mentions of **youtube-dl** in the text
- Removing unnecessary Markdown markup for headers
- Adding some links
2015-12-10 13:19:26 +01:00
Juan M Martínez
374c761e77 [FFmpegPostProcessor] Default of prefer ffmpeg
When no `downloader` is passed to `FFmpegPostProcessor`
an exception was raised trying to get the prefer ffmpeg param.

    AttributeError: 'NoneType' object has no attribute 'params'

This fixes and defaults to `False`.
2015-12-09 20:56:00 -03:00
Sergey M․
6c7b26e13f [pbs] Make URLs lowercase 2015-12-09 21:28:04 +06:00
Sergey M․
b51b108045 [pbs] Clean up stations list from duplicates 2015-12-09 21:23:19 +06:00
Philipp Hagemeister
86e8c89488 release 2015.12.09 2015-12-09 15:32:26 +01:00
Jaime Marquínez Ferrándiz
47f48f5d85 [test/test_all_urls] Update pbs extractor name
It's in lowercase now (since e15e2ef7a0).
2015-12-08 21:12:13 +01:00
Sergey M․
e15e2ef7a0 [pbs] Add support for all member stations (#7674) 2015-12-09 01:51:34 +06:00
Sergey M․
d0c8b279da [pbs] Add another coveplayer pattern (Closes #7674) 2015-12-08 23:34:43 +06:00
Sergey M․
612d83b51d [pbs] Extend _VALID_URL 2015-12-08 23:28:36 +06:00
Sergey M
9c30efeb7e Merge pull request #7792 from jindaxia/fix_sohu_403forbidden
[sohu] Fix 403 forbidden
2015-12-08 22:54:14 +06:00
Sergey M․
39fa4cc107 [cliphunter] Fix extraction (Closes #7796) 2015-12-08 21:56:00 +06:00
Sergey M?
b09c122373 [nbc] Add another theplatform pattern 2015-12-08 21:35:42 +06:00
Sergey M
3348243b7b [README.md] Clarify verbose log requirements 2015-12-08 21:34:26 +06:00
Sergey M․
b46b65ed37 [nbc] Smuggle referer (Closes #7791) 2015-12-08 21:16:14 +06:00
Sergey M․
18e4088fad [theplatform] Add support for referer protected videos wuth explicit SMIL 2015-12-08 21:15:45 +06:00
虾哥哥
5fd6cd64f9 [sohu]fix 403 forbidden 2015-12-08 14:14:14 +08:00
Sergey M․
3d24bbfbe4 [YoutubeDL] Check formats for merge to be opposite (#7786) 2015-12-07 23:10:57 +06:00
Sergey M․
1775612512 [wimp] Improve video URL regex 2015-12-07 22:18:00 +06:00
Sergey M․
0d2d967cc7 [wimp] Fix extraction (Closes #7784) 2015-12-07 22:14:45 +06:00
Sergey M․
a5e52a1fd4 [vk] Add test for pladform embed 2015-12-07 22:05:54 +06:00
Sergey M․
291a93bafa [vk] Remove unnecessary message 2015-12-07 22:04:47 +06:00
Sergey M․
c4737bea17 [vk] Add support for pladform embeds (Closes #7780) 2015-12-07 22:03:52 +06:00
Sergey M․
45dad7ba1b [extractor/generic] Use _extract_url for pladform 2015-12-07 22:03:21 +06:00
Sergey M․
db7c9da871 [pladform] Add _extract_url routine 2015-12-07 22:02:45 +06:00
Philipp Hagemeister
bc92621ade release 2015.12.06 2015-12-06 18:51:25 +01:00
Sergey M․
fd8e559c3a [beeg] Switch to api v4 (Closes #7774) 2015-12-06 23:47:10 +06:00
Sergey M․
222e11d4ae [bbc] Add another pattern for playlist.sxml (Closes #7743) 2015-12-06 16:41:12 +06:00
Sergey M․
7d682f0acb [nowtv] Extend _VALID_URL to support jahr URLs (Closes #7755) 2015-12-06 16:18:59 +06:00
Yen Chi Hsuan
8364b6b0b1 [iqiyi] Update key
Closes #7772
2015-12-06 16:41:02 +08:00
Sergey M․
7ac40e5521 [nowvideo] Update test 2015-12-06 09:42:20 +06:00
Sergey M․
36066dd3ee [movshare] Rename to wholecloud 2015-12-06 09:42:00 +06:00
Sergey M․
636aa83ed3 [cloudtime] Add extractor 2015-12-06 09:37:38 +06:00
Sergey M․
33d152b6cc [novamov] Move all novamov based extractors to a single place
For easier navigation
2015-12-06 09:29:41 +06:00
remitamine
51c4fec0d5 [nba] use int_or_none for tbr 2015-12-05 21:04:22 +01:00
remitamine
0017486dca [nba] use int instead of int_or_none 2015-12-05 20:58:44 +01:00
Sergey M․
edc70f4aaf [pluralsight] Fix format code split while guessing quality 2015-12-06 01:40:13 +06:00
Sergey M․
756926ff00 [pluralsight] Add support for widescreen videos (Closes #7766) 2015-12-06 01:39:28 +06:00
remitamine
cb160dd531 [nba] handle format info properly 2015-12-05 18:47:15 +01:00
Jaime Marquínez Ferrándiz
77334ccb44 [metacafe] Fix age limit extraction 2015-12-05 16:12:50 +01:00
Jaime Marquínez Ferrándiz
796db21295 [metacafe] Fix video url extraction (closes #7763) 2015-12-05 16:12:02 +01:00
Philipp Hagemeister
535d7b681b release 2015.12.05 2015-12-05 16:01:37 +01:00
Sergey M․
960e038886 [hypem] Modernize 2015-12-05 20:46:57 +06:00
Sergey M․
ea14422ff1 [hypem] Correctly handle cookies (Closes #7762) 2015-12-05 20:42:21 +06:00
Yen Chi Hsuan
38d05d17e5 [fc2] Fix test_FC2_1 2015-12-05 21:10:26 +08:00
Yen Chi Hsuan
db9bd5267f [keezmovies] Fix extraction
Also fixes #7752
2015-12-05 17:26:13 +08:00
remitamine
ab3b773bbe [acast] change tests into more stable casts and work with channel extractor only if it didn't match cast regex 2015-12-05 10:14:34 +01:00
Yen Chi Hsuan
0bc4ee60e0 [bbc] Fix test_BBC_6 2015-12-05 16:55:53 +08:00
Yen Chi Hsuan
a3ef0e1cdd [bbc.co.uk] Skip removed test video 2015-12-05 16:55:53 +08:00
Yen Chi Hsuan
679bacf0b5 [bbc.co.uk] Fix test_BBCCoUk
This is similar to the one in #7756, So also fixes #7756.
2015-12-05 16:55:53 +08:00
remitamine
02e3952f3b [trilulilu] handle errors 2015-12-05 09:42:00 +01:00
Yen Chi Hsuan
64b7e89c0c [srf] Support audios (closes #7760) 2015-12-05 16:26:30 +08:00
remitamine
bee4c5571a [clipfish] improve extraction 2015-12-04 16:38:05 +01:00
remitamine
96929dd1e8 [skynewsarabia] fix extractor name 2015-12-04 16:23:44 +01:00
remitamine
53e06b2507 [ooyala] fix duration scale 2015-12-04 16:18:02 +01:00
remitamine
b80d4bebf3 [nba] fix extraction errors 2015-12-04 16:04:22 +01:00
Jaime Marquínez Ferrándiz
55bec9b658 [clipfish] Remove unused import and style fix 2015-12-04 14:29:37 +01:00
Jaime Marquínez Ferrándiz
2a63b0f110 [mixcloud] Fix extraction of the audio url (fixes #7751) 2015-12-04 14:26:34 +01:00
remitamine
07b88cffce Merge pull request #7686 from remitamine/acast
[acast] Add new extractor
2015-12-04 09:10:02 +01:00
remitamine
58c8451f36 Merge pull request #7660 from remitamine/gameinformer
[gameinformer] Add new extractor(closes #3376)
2015-12-04 09:03:21 +01:00
remitamine
3047121c63 Merge pull request #7320 from remitamine/adobetv
[adobetv] improve extraction and add support specific language video,show and channel extraction
2015-12-04 08:54:06 +01:00
remitamine
7079f8ff1f [adobetv] use compat_str 2015-12-04 08:44:18 +01:00
remitamine
2c3b9f3570 [adobetv] use a variable for api base url 2015-12-04 08:37:08 +01:00
remitamine
fad2428f47 [gameinformer] split long line 2015-12-04 08:24:04 +01:00
remitamine
c3d3110f6a Merge pull request #7185 from remitamine/ooyala
[ooyala] extract more formats and metadata
2015-12-04 08:23:21 +01:00
remitamine
79ec00276c Merge pull request #7326 from remitamine/clipfish
[clipfish] improve info extraction
2015-12-04 07:57:58 +01:00
remitamine
9c117d345f [nba] improve(fixes #7068)
* extract more formats
* extract videos from team mini sites
* extract more metadata
2015-12-04 07:20:27 +01:00
remitamine
46cc1c65a4 [nba] use xpath utils 2015-12-04 07:09:48 +01:00
remitamine
71d9fe7818 [trilulilu] improve extraction 2015-12-04 06:53:33 +01:00
remitamine
4ccabf93db [trilulilu] fix info extraction 2015-12-04 00:51:02 +01:00
remitamine
6612a34939 [bilibili] flake8 2015-12-03 22:43:19 +01:00
remitamine
e5b4225f7c [audimedia] flake8 2015-12-03 22:25:08 +01:00
remitamine
b2ca35ddbc Merge pull request #7745 from remitamine/bilibili
[bilibili] use xpath_text and catch errors in xml document
2015-12-03 22:11:41 +01:00
remitamine
76ab842d9b [bilibili] use xpath_text and catch errors in xml document 2015-12-03 22:01:32 +01:00
remitamine
24dc1ed715 Merge pull request #7659 from remitamine/audimedia
[audimedia] Add new extractor(closes #7654)
2015-12-03 20:28:52 +01:00
remitamine
682d8dcd21 Merge pull request #7210 from remitamine/bilibili
[bilibili] fix info extraction(fixes #7182)
2015-12-03 20:16:54 +01:00
remitamine
640bb54e73 Merge branch 'master' of https://github.com/rg3/youtube-dl into bilibili 2015-12-03 20:05:11 +01:00
Sergey M․
e0977d7686 [beeg] Decrypt URL (Closes #7736) 2015-12-04 00:59:32 +06:00
remitamine
112ab398db Merge pull request #7681 from remitamine/skynewarabia
[skynewsarabia] Add new extractor
2015-12-03 18:41:38 +01:00
Sergey M․
af93fcfa05 [beeg] Update API URL (Closes #7736) 2015-12-03 23:23:36 +06:00
Sergey M․
62d231c004 [extractor/common] Clarify duration can be float 2015-12-03 20:55:02 +06:00
Sergey M․
49358274d7 [bbc] Fix _VALID_URL 2015-12-03 20:49:14 +06:00
Jaime Marquínez Ferrándiz
7b1e379ca9 [gametrailers] Fix extraction (fixes #7722)
They have stopped using the MTV system.
2015-12-03 13:47:21 +01:00
Sergey M․
22d7368dfb [bbc] Extract _ID_REGEX and ad one more video id pattern (Closes #7724) 2015-12-02 02:34:31 +06:00
Sergey M․
24121bc703 [udemy] Make lecture downloading fatal 2015-12-02 00:53:03 +06:00
Sergey M․
9fc87fa767 [udemy] Remove unused import 2015-12-02 00:51:47 +06:00
Sergey M․
328f82d59a [udemy] Semi-switch to api 2.0 (Closes #7704)
* Use api 2.0 to get lectures since it provides more formats
* Fix authorization for api 2.0
* Autotry enrolling in the course for single lectures
* Extract additional metadata rom asset['data']['outputs']
2015-12-02 00:48:27 +06:00
Sergey M․
78717fc328 [udemy] Allow authentication via cookies 2015-12-01 22:10:10 +06:00
Sergey M․
3b35c3425e [udemy] Extract formats from data.outputs (#7704) 2015-12-01 20:35:46 +06:00
Sergey M․
874ae0354e [nrk] Extract f4m formats and impose geo restriction only when not media URL (Closes #7715) 2015-12-01 18:35:24 +06:00
Sergey M․
4c6b4764f0 [youtube] Clarify itag 272 possible resolutions (#7699) 2015-11-30 20:42:05 +06:00
Sergey M․
59ee8a8647 [facebook] Make alternative title optional (Closes #7700) 2015-11-30 20:10:09 +06:00
Sergey M․
af284305d5 [vodlocker] Capture file not found error (Closes #7696) 2015-11-30 03:58:39 +06:00
Sergey M․
d53a4af1a4 [pornhub:playlist] Allow alphanumeric viewkeys (Closes #7695) 2015-11-30 03:47:01 +06:00
Sergey M․
2e1b928540 [youtube:playlist] Extend _VALID_URL 2015-11-29 21:04:11 +06:00
Sergey M․
040ac68679 [youtube] Extend _VALID_URL (Closes #7694) 2015-11-29 21:01:59 +06:00
Yen Chi Hsuan
049d71d874 [youtube] Simplify and make sure header values are strings 2015-11-29 19:52:48 +08:00
Sergey M․
bf2c8c8f82 [spiegel] Fix extraction (Closes #7693) 2015-11-29 17:03:33 +06:00
Yen Chi Hsuan
ef428960c9 Merge pull request #7691 from ryandesign/use-PYTHON-env-var
Always use PYTHON env var in Makefile
2015-11-29 13:08:46 +08:00
Yen Chi Hsuan
992fc9d6e1 [utils] Refactor handle_youtubedl_headers for future extension 2015-11-29 12:58:29 +08:00
Ryan Schmidt
8639f89f51 Always use PYTHON env var in Makefile 2015-11-28 22:56:24 -06:00
Yen Chi Hsuan
0424ec307b [utils] Correct docstring of YoutubeDLHandler 2015-11-29 12:46:04 +08:00
Yen Chi Hsuan
ac5a69af45 [youtube] Disable compression for live streams 2015-11-29 12:44:24 +08:00
Yen Chi Hsuan
94e8c80473 [downloader/hls] Respect Youtubedl-* headers 2015-11-29 12:43:59 +08:00
Yen Chi Hsuan
87f0e62d94 [utils] Separate codes for handling Youtubedl-* headers 2015-11-29 12:42:50 +08:00
remitamine
46b4070f3f Merge pull request #7057 from remitamine/cspan
[cspan] correct the clip info extraction (fixes #7335)
2015-11-28 21:36:52 +01:00
remitamine
2a776f9788 [cspan] change into a function 2015-11-28 20:22:31 +01:00
remitamine
f4c7ef9862 [skynewsarabia] return empty categories array if there is no topic 2015-11-28 18:20:44 +01:00
remitamine
50e12e9df1 [acast] Add new extractor 2015-11-28 18:10:37 +01:00
Sergey M․
b7faebbac8 [bloomberg] Improve formats extraction 2015-11-28 22:45:19 +06:00
Sergey M․
4191fdf147 [bloomberg] Improve video id regex 2015-11-28 22:41:39 +06:00
Sergey M․
9a4f12be98 [bloomberg] Modernize 2015-11-28 22:40:29 +06:00
Sergey M․
7ad4258add [bloomberg] Relax _VALID_URL even more (Closes #7685) 2015-11-28 22:39:36 +06:00
Sergey M․
9945c4994c Credit @reiv for soundcloud:search 2015-11-28 20:21:03 +06:00
Sergey M․
5faf9fed7e [youtube] Clarify rationale for yt:stretch validation 2015-11-28 18:50:21 +06:00
Sergey M
13a9b69b09 Merge pull request #7677 from lalinsky/yt-stretch-zero-height
[youtube] Ignore yt:stretch with zero width/height
2015-11-28 18:14:06 +06:00
remitamine
4975650e00 [skynewsarabia] fix IE_NAME 2015-11-28 12:20:39 +01:00
remitamine
0cc7178546 [skynewsarabia] Add new extractor 2015-11-28 11:48:18 +01:00
Lukáš Lalinský
41f24c321d [youtube] Use the existing w and h variables 2015-11-28 08:16:46 +01:00
Yen Chi Hsuan
4b3fbafdd2 [options] Changed wording for --list-formats
As proposed by @dstftw at 9bff48a0e7
2015-11-28 14:14:20 +08:00
Sergey M․
7ac40086f5 [dbtv] Expand _VALID_URL (Closes #7645) 2015-11-28 08:44:13 +06:00
Lukáš Lalinský
313dfc45f5 [youtube] Ignore yt:stretch with zero width/height 2015-11-28 01:07:07 +01:00
Philipp Hagemeister
78a55d7a28 release 2015.11.27.1 2015-11-27 16:39:59 +01:00
Philipp Hagemeister
bb6ac83698 release 2015.11.27 2015-11-27 16:32:51 +01:00
Yen Chi Hsuan
9d0e366880 [downloader/hls] Remove Accept-encoding from headers passed to ffmpeg
Fails for Youtube Gaming live streams (#7671)
2015-11-27 21:37:45 +08:00
Yen Chi Hsuan
9bff48a0e7 [options] Clarify --list-formats needs videos (closes #7669) 2015-11-27 21:24:39 +08:00
remitamine
60121eb514 [gameinformer] Add new extractor 2015-11-26 22:43:31 +01:00
remitamine
527ca1da4f [audimedia] Add new extractor(closes #7654) 2015-11-26 21:24:10 +01:00
Sergey M
7689413e42 [README.md] Mention mplayer and mpv in "other programs" question 2015-11-24 23:06:21 +06:00
Philipp Hagemeister
ba7a92b0ce release 2015.11.24 2015-11-24 07:46:38 +01:00
Philipp Hagemeister
4c7d816dd7 [jsinterp] Adapt to updated YouTube code generation (Fixes #7623, fixes #7624, fixes #7625, fixes #7626) 2015-11-24 07:45:38 +01:00
Philipp Hagemeister
032f2f260f README: Document which other programs may be helpful (Fixes #7621) 2015-11-24 03:38:46 +01:00
Philipp Hagemeister
20e98bf6c0 release 2015.11.23 2015-11-23 18:07:58 +01:00
Sergey M?
5c2266df4b Switch codebase to use sanitized_Request instead of
compat_urllib_request.Request

[downloader/dash] Use sanitized_Request

[downloader/http] Use sanitized_Request

[atresplayer] Use sanitized_Request

[bambuser] Use sanitized_Request

[bliptv] Use sanitized_Request

[brightcove] Use sanitized_Request

[cbs] Use sanitized_Request

[ceskatelevize] Use sanitized_Request

[collegerama] Use sanitized_Request

[extractor/common] Use sanitized_Request

[crunchyroll] Use sanitized_Request

[dailymotion] Use sanitized_Request

[dcn] Use sanitized_Request

[dramafever] Use sanitized_Request

[dumpert] Use sanitized_Request

[eitb] Use sanitized_Request

[escapist] Use sanitized_Request

[everyonesmixtape] Use sanitized_Request

[extremetube] Use sanitized_Request

[facebook] Use sanitized_Request

[fc2] Use sanitized_Request

[flickr] Use sanitized_Request

[4tube] Use sanitized_Request

[gdcvault] Use sanitized_Request

[extractor/generic] Use sanitized_Request

[hearthisat] Use sanitized_Request

[hotnewhiphop] Use sanitized_Request

[hypem] Use sanitized_Request

[iprima] Use sanitized_Request

[ivi] Use sanitized_Request

[keezmovies] Use sanitized_Request

[letv] Use sanitized_Request

[lynda] Use sanitized_Request

[metacafe] Use sanitized_Request

[minhateca] Use sanitized_Request

[miomio] Use sanitized_Request

[meovideo] Use sanitized_Request

[mofosex] Use sanitized_Request

[moniker] Use sanitized_Request

[mooshare] Use sanitized_Request

[movieclips] Use sanitized_Request

[mtv] Use sanitized_Request

[myvideo] Use sanitized_Request

[neteasemusic] Use sanitized_Request

[nfb] Use sanitized_Request

[niconico] Use sanitized_Request

[noco] Use sanitized_Request

[nosvideo] Use sanitized_Request

[novamov] Use sanitized_Request

[nowness] Use sanitized_Request

[nuvid] Use sanitized_Request

[played] Use sanitized_Request

[pluralsight] Use sanitized_Request

[pornhub] Use sanitized_Request

[pornotube] Use sanitized_Request

[primesharetv] Use sanitized_Request

[promptfile] Use sanitized_Request

[qqmusic] Use sanitized_Request

[rtve] Use sanitized_Request

[safari] Use sanitized_Request

[sandia] Use sanitized_Request

[shared] Use sanitized_Request

[sharesix] Use sanitized_Request

[sina] Use sanitized_Request

[smotri] Use sanitized_Request

[sohu] Use sanitized_Request

[spankwire] Use sanitized_Request

[sportdeutschland] Use sanitized_Request

[streamcloud] Use sanitized_Request

[streamcz] Use sanitized_Request

[tapely] Use sanitized_Request

[tube8] Use sanitized_Request

[tubitv] Use sanitized_Request

[twitch] Use sanitized_Request

[twitter] Use sanitized_Request

[udemy] Use sanitized_Request

[vbox7] Use sanitized_Request

[veoh] Use sanitized_Request

[vessel] Use sanitized_Request

[vevo] Use sanitized_Request

[viddler] Use sanitized_Request

[videomega] Use sanitized_Request

[viewvster] Use sanitized_Request

[viki] Use sanitized_Request

[vk] Use sanitized_Request

[vodlocker] Use sanitized_Request

[voicerepublic] Use sanitized_Request

[wistia] Use sanitized_Request

[xfileshare] Use sanitized_Request

[xtube] Use sanitized_Request

[xvideos] Use sanitized_Request

[yandexmusic] Use sanitized_Request

[youku] Use sanitized_Request

[youporn] Use sanitized_Request

[youtube] Use sanitized_Request

[patreon] Use sanitized_Request

[extractor/common] Remove unused import

[nfb] PEP 8
2015-11-23 21:56:23 +06:00
Sergey M․
67dda51722 Rename compat_urllib_request_Request to sanitized_Request and move to utils 2015-11-23 21:55:15 +06:00
Sergey M․
e4c4bcf36f [vimeo] Use compat_urllib_request_Request 2015-11-23 21:55:14 +06:00
Sergey M․
82d8a8b6e2 [YoutubeDL] Wrap plain-text URL requests in compat_urllib_request_Request 2015-11-23 21:55:13 +06:00
Sergey M․
13a10d5aa3 [compat] Add compat_urllib_request_Request
This is actually not a compatibility routine but rather a workaround for URLs without protocol specified.
The protocol-less URL is treated as HTTP one since it's most probable scenario and it will most likely to
redirect to HTTPS if HTTPS was actually expected. This routine could also be useful for any Request
preprocessing that may be added in future.
2015-11-23 21:55:12 +06:00
Sergey M․
9022726446 [youtube] Fix test 2015-11-23 21:37:21 +06:00
Sergey M․
94bfcd23b7 [youtube] Fix test 2015-11-23 21:35:23 +06:00
Sergey M․
526b3b0716 [youtube] Clarify ytplayer.config extraction rationale 2015-11-23 21:14:03 +06:00
Sergey M․
61f92af1cf [youtube] Add test with '};' in tags 2015-11-23 21:02:37 +06:00
Sergey M․
a72778d364 [youtube] Improve ytplayer.config extraction 2015-11-23 21:00:06 +06:00
Sergey M
5ae17037a3 Merge pull request #7599 from lalinsky/fix-youtube
[youtube] More explicit player config JSON extraction (fixes #7468)
2015-11-23 20:52:23 +06:00
Sergey M․
02f0da20b0 [pluralsight] Add support for alternative webpage layout (Closes #7607) 2015-11-23 03:08:38 +06:00
Lukáš Lalinský
b41631c4e6 [youtube] Send the list of patterns directly to _search_regex 2015-11-22 13:53:26 +01:00
Lukáš Lalinský
0e49d9a6b0 [youtube] Fall back to the original regex for ytplayer.config 2015-11-22 13:49:33 +01:00
Sergey M․
4a7d108ab3 [rutube] Remove unnecessary print 2015-11-22 18:24:17 +06:00
Lukáš Lalinský
3cfd000849 [youtube] More explicit player config JSON extraction (fixes #7468) 2015-11-22 13:14:35 +01:00
Sergey M․
1b38185361 [pornhd] Fix title extraction (Closes #7596) 2015-11-22 18:08:30 +06:00
Sergey M․
9cb9a5df77 [utils] Check ext with trailing slash against the list of known extensions 2015-11-22 17:27:13 +06:00
Sergey M․
5035536e3f [test_utils] Add tests for determine_ext 2015-11-22 06:33:52 +06:00
Sergey M․
3e12bc583a [utils] Improve determine_ext (Closes #7593) 2015-11-22 06:29:39 +06:00
Sergey M․
e568c2233e [youtube] Add test for multi page list of playlists 2015-11-22 05:03:23 +06:00
Sergey M․
061a75edd6 [youtube] Extract base for entry list extractors and support multi page lists of playlists 2015-11-22 05:01:01 +06:00
Philipp Hagemeister
82c4d7b0ce release 2015.11.21 2015-11-21 23:36:27 +01:00
Sergey M․
136dadde95 [youtube:show] Rework in terms of playlists base extractor 2015-11-22 04:18:20 +06:00
Sergey M․
0c14841585 [youtube:user:playlists] Add extractor (Closes #3817) 2015-11-22 04:17:07 +06:00
Sergey M․
0eebf34d9d [pluralsight] Rephrase 2015-11-22 00:58:25 +06:00
Sergey M․
cf186b77a7 [pluralsight] Clarify allowed qualities guessing rationale 2015-11-22 00:56:40 +06:00
Sergey M․
a3372437bf [soundcloud] Remove unused variable 2015-11-22 00:49:58 +06:00
Sergey M․
4c57b4853d [pluralsight] Until listing formats request only single format 2015-11-22 00:42:58 +06:00
Sergey M․
38eb2968ab [pluralsight] Clarify and randomize ViewClip sleep interval 2015-11-22 00:07:09 +06:00
Andrzej Lichnerowicz
bea56c9569 [pluralsight] prevent error 429 when sensing video formats 2015-11-21 23:49:58 +06:00
Sergey M․
7e508ff2cf [pluralsight] Improve login detection 2015-11-21 21:49:37 +06:00
Sergey M․
563772eda4 [pluralsight] Extract base class 2015-11-21 21:37:29 +06:00
Sergey M․
0533915aad [pluralsight] Update some more URLs 2015-11-21 21:35:08 +06:00
Sergey M․
c3a227d1c4 [pluralsight] Update _LOGIN_URL 2015-11-21 21:25:48 +06:00
Sergey M․
f6c903e708 [soundcloud:search] Simplify (Closes #7213) 2015-11-21 21:21:21 +06:00
Sergey M․
7dc011c063 [soundcloud:search] Remove no track results message 2015-11-21 21:00:42 +06:00
Sergey M․
4e3b303016 [soundcloud:search] Fix non-ASCII searches 2015-11-21 20:55:48 +06:00
Sergey M․
7e1f5447e7 [utils] Improve encode_dict 2015-11-21 20:46:33 +06:00
Sergey M․
7e3472758b [soundcloud:search] PEP 8 2015-11-21 20:04:35 +06:00
reiv
328a22e175 [soundcloud] Remove limit on search results 2015-11-21 19:41:36 +06:00
reiv
417b453699 [soundcloud] Use correct error message conventions 2015-11-21 19:41:31 +06:00
reiv
6ea7190a3e Rewrite as list comprehension. 2015-11-21 19:41:26 +06:00
reiv
b54b08c91b Simplify with itertools.islice(). 2015-11-21 19:41:19 +06:00
reiv
c30943b1c0 Fix some compatibility issues, cleanup. 2015-11-21 19:41:15 +06:00
reiv
2abf7cab80 [soundcloud] Add Soundcloud search extractor 2015-11-21 19:41:08 +06:00
Sergey M․
4137196899 [rutube] Extract all formats 2015-11-21 18:02:52 +06:00
Sergey M․
019839faaa [extractor/common] Use baseURL from f4m manifest for recursive manifest extraction 2015-11-21 18:01:39 +06:00
Sergey M․
f52183a878 [rutube:embed] Extend _VALID_URL (Closes #7588) 2015-11-21 17:39:24 +06:00
Yen Chi Hsuan
750b9ff032 [generic] Extract M3U8 formats (closes #7582) 2015-11-21 16:43:01 +08:00
Yen Chi Hsuan
28602e747c [generic] Refactor 2015-11-21 16:08:54 +08:00
Yen Chi Hsuan
6cc37c69e2 [generic] Unescape URLs from JWPlayer (#7582) 2015-11-21 14:12:34 +08:00
Sergey M․
a5cd0eb8a4 [pluralsight:course] Improve _VALID_URL 2015-11-21 08:32:48 +06:00
Sergey M․
c23e266427 [pluralsight] Do not require pluralsight account
Looks like some courses are available without pluralsight account
2015-11-21 08:25:52 +06:00
Sergey M․
651acffbe5 [pluralsight] Update ViewClip URL 2015-11-21 08:21:33 +06:00
Sergey M․
71bd93b89c [pluralsight] Do not rely on argument order in query (Closes #7583) 2015-11-21 08:08:34 +06:00
Sergey M․
6da620de58 [kaltura] Add test for referrer protected video (#7409) 2015-11-21 01:40:28 +06:00
Sergey M․
bdceea7afd [kaltura] Clean description 2015-11-21 01:39:29 +06:00
Sergey M․
d80a39cec8 [kaltura] Improve 2015-11-21 01:38:08 +06:00
Sergey M․
5b5fae5f20 [generic] Use referrer from source kaltura embed URLs (#7409) 2015-11-21 01:35:58 +06:00
Sergey M․
01b06aedcf [kaltura] Add support for referrer protected videos (#7409) 2015-11-21 01:34:02 +06:00
Sergey M
c711383811 Merge pull request #7579 from ashutosh-mishra/typo_fix
Typo fix, found while going through the code.
2015-11-20 23:24:54 +06:00
ashutosh-mishra
17cc153435 Typo fix, found while going through the code. 2015-11-20 22:51:46 +05:30
Sergey M․
67446fd49b [instagram] Improve _VALID_URL (Closes #7568) 2015-11-20 04:07:39 +06:00
Sergey M․
325bb615a7 [theplatform] Style 2015-11-19 22:58:43 +06:00
Sergey M․
ee5cd8418e [theplatform] Handle protocolless feed URLs (Closes #7532) 2015-11-19 22:58:29 +06:00
Sergey M․
342609a1b4 [bloomberg] Reax _VALID_URL (Closes #7546) 2015-11-19 22:55:06 +06:00
Sergey M
f270cf1a26 Merge pull request #7519 from barlik/master
Clarify that automatic subtitles are generated.
2015-11-19 22:44:08 +06:00
hedii
371c3b796c [YoutubeDL] Add playlist finished downloading message (Closes #7517)
Conflicts:
	youtube_dl/YoutubeDL.py
2015-11-19 22:39:02 +06:00
Sergey M․
6b7ceee1b9 [vimeo] Add test for #7552 2015-11-19 22:31:16 +06:00
Sergey M․
fdb20a27a3 [vimeo:group] Improve _VALID_URL (Closes #7552) 2015-11-19 22:30:58 +06:00
Sergey M․
2c94198eb6 [vimeo] Improve playlists extraction 2015-11-19 21:29:32 +06:00
Philipp Hagemeister
e8110b8125 release 2015.11.19 2015-11-19 15:35:13 +01:00
Yen Chi Hsuan
c39fd7b1ca [UDNEmbed] Fix generic UDN pages
Closes #7547
2015-11-19 22:32:56 +08:00
Sergey M․
a9c09a7c62 [pbs] Update API URL (Closes #7565) 2015-11-19 20:25:28 +06:00
Philipp Hagemeister
82beaabb41 release 2015.11.18 2015-11-18 19:23:04 +01:00
Jaime Marquínez Ferrándiz
63b4295d20 [youtube:playlist] fix title extraction (fixes #7544 and #7545) 2015-11-18 18:28:05 +01:00
Sergey M․
312a3f389b [pbs] Extend _VALID_URL 2015-11-18 00:46:41 +06:00
Jaime Marquínez Ferrándiz
609af1ae1c [dplay] Add 'encoding: utf-8' line 2015-11-17 17:58:16 +01:00
Jaime Marquínez Ferrándiz
4cd759f73d [dplay] Add extractor (closes #7515)
Since I haven't figured out how to download the hds stream, we use the hls one instead.
2015-11-17 17:52:29 +01:00
Jaime Marquínez Ferrándiz
e156e70281 [rtve] Remove unused import 2015-11-17 16:23:29 +01:00
Sergey M․
9b464929fe [rtve.es:alacarta] Fix extraction 2015-11-17 21:11:42 +06:00
Sergey M
0c176d7bde Merge pull request #7514 from ping/patch-7301
[neteasemusic] Fixes #7301
2015-11-16 14:25:29 +00:00
Sergey M․
7a3f0c00ad [utils] Style 2015-11-16 20:24:09 +06:00
Sergey M․
7aefc49c40 [utils] Skip invalid/non HTML entities (Closes #7518) 2015-11-16 20:20:16 +06:00
Rastislav Barlik
741dd8ea65 Clarify that automatic subtitles are generated.
It wasn't clear what automatic word mean.
2015-11-16 14:15:25 +00:00
ping
76adc82068 [neteasemusic] Fixes #7301 2015-11-16 11:39:18 +08:00
Philipp Hagemeister
bd1512d196 release 2015.11.15 2015-11-15 22:16:08 +01:00
Sergey M․
9a4acbfaf5 [theplatform] Add test for #7385 2015-11-16 00:28:04 +06:00
Sergey M․
ad1f4e7902 [theplatform] Handle explicitly specified SMIL (#7385) 2015-11-15 23:43:23 +06:00
Sergey M
b328295910 Merge pull request #7436 from davidbz/add_proxy_to_update_procedure
Add proxy support for update_self
2015-11-15 11:13:22 +00:00
David Ben Zakai
828b2a5cd9 Removing an unnecessary import 2015-11-15 09:40:32 +02:00
Sergey M․
2ff7cbeaaa [nowtv:list] Add extrator (Closes #7147) 2015-11-15 08:30:13 +06:00
Sergey M․
b2f7738830 [dumpert] Use original protocol 2015-11-15 02:25:00 +06:00
Sergey M․
dc0279532a [dumpert] Disable SSL (Closes #7504) 2015-11-15 02:21:24 +06:00
Sergey M․
0c59d02bdc [periscope] Relax _VALID_URL (Closes #7503) 2015-11-15 00:20:17 +06:00
Jaime Marquínez Ferrándiz
0f72beb515 [periscope] Remove unused imports 2015-11-14 18:31:33 +01:00
Sergey M․
d781e29316 [bbc] Allow selectionunavailable errors (Closes #7502) 2015-11-14 23:08:13 +06:00
Sergey M․
3b3e8ed332 [quickscope] Remove extractor (2) 2015-11-14 22:34:30 +06:00
Sergey M․
dcdfeb33d2 [quickscope] Remove extractor 2015-11-14 22:32:54 +06:00
Sergey M․
0d85c3a732 [lynda] Style 2015-11-14 16:44:24 +06:00
Sergey M․
903d136942 [lynda] Logout only when login info present (Closes #7500) 2015-11-14 16:43:58 +06:00
Yen Chi Hsuan
9d584da7d0 [xfileshare] Correct _VALID_URL 2015-11-14 17:27:32 +08:00
Yen Chi Hsuan
31752f76f7 [twitter:card] Add add_ie for the external test 2015-11-14 17:03:26 +08:00
Yen Chi Hsuan
5f1b2aea80 [twitter:card] Support vine.co embeds (closes #7496) 2015-11-14 17:02:07 +08:00
Sergey M․
4479600d57 [instagram] Add test for #7497 2015-11-14 07:21:20 +06:00
Sergey M․
a90189c3ad [instagram] Relax _VALID_URL (Closes #7497) 2015-11-14 07:20:33 +06:00
Sergey M․
d8a1caf04f [brightcove:new] Style 2015-11-14 06:22:12 +06:00
Sergey M․
cb33d389ed [brightcove:new] Add test with rtmp streams 2015-11-14 06:20:09 +06:00
Sergey M․
967e0955f0 Merge branch 'remitamine-brightcove_in_page_embed' 2015-11-14 06:11:49 +06:00
Sergey M․
e01b432ad3 [brightcove:new] Fix test 2015-11-14 06:11:17 +06:00
Sergey M․
fd91257c40 [brightcove] Order imports alphabetically 2015-11-14 06:08:36 +06:00
Sergey M․
c7b959ce38 [utils] Remove unused function 2015-11-14 06:07:44 +06:00
Sergey M․
75eac8961e [brightcove] Remove unused import 2015-11-14 06:07:24 +06:00
Sergey M․
3b7d9aa487 Rename all references to legacy studio Brightcove extractor 2015-11-14 06:05:46 +06:00
Sergey M․
1f4b722b00 [generic] Clarify Brightcove Legacy Studio comment 2015-11-14 06:03:32 +06:00
Sergey M․
f6519f89b0 [generic] Extract Brightcove New Studio embeds 2015-11-14 06:03:07 +06:00
Sergey M․
24af85298e [brightcove] Fix _extract_urls 2015-11-14 06:01:56 +06:00
Sergey M․
e721d857c2 [brightcove] Clarify IE_NAMEs 2015-11-14 05:56:51 +06:00
Sergey M․
5c17f0a67a [brightcove:embedinpage] Rename extractor to brightcove new
It's not actually embed_in_page but "New Studio" and allows both iframe and embed_in_page embeds
2015-11-14 05:55:59 +06:00
Sergey M․
4fcaa4f4a5 [brightcove] Rename extractor to brightcove legacy
Old embedding approaches are now "Legacy Studio"
2015-11-14 05:54:16 +06:00
Sergey M․
536f819eda [brightcove] Imrove extraction of new embeds 2015-11-14 05:51:05 +06:00
Sergey M․
a662489877 [brightcove:embedinpage] Make more robust and extract rtmp streams 2015-11-14 05:09:50 +06:00
Sergey M․
a2973eb597 Merge branch 'brightcove_in_page_embed' of https://github.com/remitamine/youtube-dl into remitamine-brightcove_in_page_embed 2015-11-14 01:23:15 +06:00
Sergey M․
4e21b3a94f [cbs] Use android UA for higher quality streams (Closes #7490) 2015-11-14 00:25:28 +06:00
Jaime Marquínez Ferrándiz
b703ebeeaf [twitter] Don't fail if the description doesn't contain an URL (fixes #7489) 2015-11-13 19:09:42 +01:00
Jaime Marquínez Ferrándiz
b84a5f0337 [twitter] Update tests checksums 2015-11-13 18:55:07 +01:00
Philipp Hagemeister
a1ec9a7553 release 2015.11.13 2015-11-13 11:07:30 +01:00
Sergey M․
91d644b5ba [ruutu] Relax formats extraction 2015-11-13 02:43:27 +06:00
Sergey M․
5d6c3d6a66 [ruutu] Skip NOT-USED URLs(Closes #7478) 2015-11-13 02:41:38 +06:00
Jaime Marquínez Ferrándiz
1ebb4717df [cbsnews] Fix construction of 'play_path' in some videos (fixes #7394) 2015-11-12 21:02:56 +01:00
Yen Chi Hsuan
cf5881fc4d Credit @ferama
For providing idea for vidto.me (#7167) and extending nowvideo support (#6760)
2015-11-12 21:33:46 +08:00
Sergey M․
fcd817a326 [vimeo] Fix extraction (Closes #7460) 2015-11-12 03:56:11 +06:00
Sergey M․
031ec536f0 [gorillavid] Rename to xfileshare 2015-11-11 23:00:53 +06:00
Sergey M․
668db403f9 [gorillavid] Add test for vidto.me and strip title 2015-11-11 22:47:28 +06:00
Sergey M․
b9ad101926 [gorillavid] Add support for vidto.me 2015-11-11 22:44:03 +06:00
Sergey M․
435911029f [vidto] Remove extractor 2015-11-11 22:43:17 +06:00
Sergey M․
699ed30cee [novamov] Modernize 2015-11-11 22:34:49 +06:00
Sergey M․
9eab37dca0 [vimeo] Simplify set cookie 2015-11-11 22:32:13 +06:00
Sergey M․
9a8a12b7d8 [vimeo] Append cookies instead of overriding 2015-11-11 22:23:23 +06:00
Yen Chi Hsuan
a4c2ab35c1 Merge remote-tracking branch 'upstream/master' 2015-11-12 00:08:42 +08:00
Sergey M․
3d9c4bf09a [vimeo] Fix password protected videos (Closes #7451) 2015-11-11 21:21:21 +06:00
Yen Chi Hsuan
8b8a39e279 [vidto] Several simplifications and improvements
1. Use InfoExtractor._hidden_inputs
2. Fetch title from <title> tag
3. Cookies are preserved automatically
4. Use single quotes everywhere
5. Do not declare variables for one-time use only
2015-11-11 23:17:59 +08:00
Sergey M․
82393e2bb2 [novamov] Follow continue-to-the-video button if any (Closes #7330) 2015-11-11 21:02:05 +06:00
Sergey M․
2eb99a4b98 [nowvideo] Replace main host to resolvable one 2015-11-11 21:00:23 +06:00
Yen Chi Hsuan
6abce58a12 Credit @sieben for fixing wsj extractor 2015-11-11 20:16:18 +08:00
Yen Chi Hsuan
990e6e8fa3 [vidto] Minor fixes
1. import order
2. fatal is already True in helper functions
2015-11-11 20:13:03 +08:00
Yen Chi Hsuan
bfd88516eb Merge pull request #7454 from sieben/duplicate_keys
Remove duplicate key
2015-11-11 20:00:13 +08:00
Rémy Léone
d8b7e80d29 Remove duplicate key 2015-11-11 12:00:31 +01:00
Yen Chi Hsuan
37120974dc [vidto] PEP8 2015-11-11 02:02:46 +08:00
Marco Ferragina
42fc93c709 vidto extractor: code cleanup 2015-11-11 01:58:47 +08:00
Marco Ferragina
a625e56543 [vidto] Add extractor 2015-11-11 01:52:43 +08:00
Sergey M․
9b738b2caa [funnyordie] Fix extraction and extract m3u8 formats 2015-11-10 21:32:54 +06:00
David Ben Zakai
90bb5667bf Using internal opener 2015-11-10 17:15:23 +02:00
David Ben Zakai
d3d3e2e3aa Adding proxy to update procedure 2015-11-10 16:31:42 +02:00
Philipp Hagemeister
37ca7b22b5 release 2015.11.10 2015-11-10 11:39:06 +01:00
Yen Chi Hsuan
50f84a9ae1 [youtube] Support new base.js html5 player 2015-11-10 12:55:01 +08:00
Yen Chi Hsuan
ff29bf81f8 [jsinterp] Support alternative function definition form 2015-11-10 12:54:02 +08:00
Sergey M․
b25f753397 [kaltura] Relax _VALID_URL 2015-11-09 20:50:43 +06:00
Sergey M․
6a5d6de1e3 [generic] Improve kaltura embed detection (2) 2015-11-09 20:49:42 +06:00
Sergey M․
1c31a5b0e0 [generic] Improve kaltura embed detection (Closes #7409) 2015-11-09 20:49:06 +06:00
Sergey M․
4f5cdf7c9b [cmt] Extend _VALID_URL to support shows (Closes #7407) 2015-11-09 01:48:46 +06:00
Sergey M․
f09a767d31 [mit] Allow external embeds (Closes #7406) 2015-11-08 19:19:13 +06:00
Sergey M․
cc8034cc4c [extremetube] Modernize 2015-11-08 19:14:39 +06:00
Sergey M․
50506cb607 [extremetube] Fix extraction (Closes #7163) 2015-11-08 19:01:37 +06:00
Sergey M․
aa8d2d5be6 [rtbf] Make www optional in _VALID_URL 2015-11-08 17:03:21 +06:00
Sergey M․
114e6025b0 [rtbf] Expand _VALID_URL (Closes #7402) 2015-11-08 17:01:45 +06:00
Sergey M․
fda2717ef9 [movieclips] Add coding cookie 2015-11-08 16:56:20 +06:00
Frans de Jonge
937511dfc0 Added support for the RTBF OUFtivi subpage 2015-11-08 16:51:13 +06:00
Jaime Marquínez Ferrándiz
d5c181a14e [movieclips] Fix extraction (fixes #7404)
They use theplatform now.
Changed the test, because the old one seems to be georestricted.
2015-11-08 11:49:51 +01:00
Sergey M?
e8ce2375e0 [viidea] Improve and cleanup (Closes #7390)
* Optimize requests for multipart videos
* Fix cfg regex
* Improve titles and identifiers
2015-11-08 06:55:52 +06:00
remitamine
6fdb39ded1 [viidia] Cleaup
[viidea] extract playlist if lecture is an event

[viidia] use compat_str
2015-11-08 06:55:51 +06:00
remitamine
8e3a2bd620 [viidea] fix _VALID_URL regex and tests 2015-11-08 06:55:51 +06:00
remitamine
a06bf87a2c [viidea] add support for sites using viidea service 2015-11-08 06:55:50 +06:00
remitamine
ee4337d100 [videolecture] add support for multi part videos 2015-11-08 06:55:50 +06:00
Jaime Marquínez Ferrándiz
cff551c0b0 [googleplus] Fix extraction of formats 2015-11-07 18:43:22 +01:00
Sergey M․
6d02b9a392 [crunchyroll] Fix description extraction 2015-11-07 20:02:39 +06:00
Sergey M․
2c740cf28d [crunchyroll] Simplify description extraction 2015-11-07 19:29:49 +06:00
Sergey M․
5214f1e31d [crunchyroll] Fix title extraction (Closes #7396) 2015-11-07 19:29:42 +06:00
Sergey M․
5d0f84d32c [beeg] Skip empty URLs (Closes #7392) 2015-11-07 06:23:00 +06:00
Mister Hat
ee223abb88 [vidzi] fixed. finds url from hash and host in script
Closes #7386.
2015-11-06 20:24:02 +01:00
Sergey M․
21d0c33ecd [pbs] Make flp embed lookup non fatal 2015-11-07 01:08:40 +06:00
Sergey M․
8b6d9406db [pbs] Add test for flp frontline embeds 2015-11-07 00:42:30 +06:00
Sergey M․
686f98816e [pbs] Add support for flp frontlines (Closes #7369) 2015-11-07 00:39:16 +06:00
Sergey M․
0fa6b17dcc [pbs] Simplify and speed up player URL search 2015-11-06 23:45:26 +06:00
Sergey M․
472404953a [miomio] PEP 8 2015-11-06 23:28:14 +06:00
Sergey M․
ae4ddf9efa [lynda] PEP 8 2015-11-06 23:27:38 +06:00
Sergey M․
ea8ed40b2f [lynda] Modernize and make more robust 2015-11-06 23:24:39 +06:00
Sergey M․
71bb016160 [lynda:course] Modernize and make more robust 2015-11-06 23:10:07 +06:00
Sergey M․
179ffab69c [lynda:course] Force log out (Closes #7361) 2015-11-06 23:06:13 +06:00
Sergey M․
deb85c32bb [postprocessor/ffmpeg] Use ffmpeg as prefix since it's used all over the places (Closes #7371) 2015-11-06 21:56:31 +06:00
Sergey M․
92366d189e [njoy:embed] Relax _VALID_URL 2015-11-06 21:09:17 +06:00
Sergey M․
81413c0165 [ndr:embed] Relax _VALID_URL 2015-11-06 21:08:52 +06:00
Sergey M․
1e2eb4b40d [njoy] Relax _VALID_URL 2015-11-06 21:08:21 +06:00
Sergey M․
01003d072c [ndr] Add test for #7383 2015-11-06 21:07:52 +06:00
Sergey M․
5003e4283b [ndr] Relax _VALID_URL (Closes #7383) 2015-11-06 21:06:44 +06:00
Yen Chi Hsuan
123c781044 Merge pull request #7382 from remitamine/miomio
[miomio] fix info extraction (fixes #7366)
2015-11-06 14:50:39 +08:00
remitamine
e68dd1921a [miomio] use the formats urls headers for downloading xml 2015-11-06 06:33:05 +01:00
remitamine
6953d8e95a [miomio] fix info extraction (fixes #7366) 2015-11-06 02:09:55 +01:00
Sergey M․
b3613d36da [YoutubeDL] Sanitize path after output template substitution (Closes #7367) 2015-11-05 04:39:21 +06:00
Sergey M․
53472df857 [periscope] Add note on where to find alive example URLs 2015-11-05 02:56:44 +06:00
Sergey M․
2549e113b8 [periscope] Add test for broadcast_id based URL 2015-11-05 02:55:53 +06:00
Sergey M․
b15c44cd36 [periscope] Add support for videos with broadcast_id (Closes #7359) 2015-11-05 02:51:30 +06:00
Sergey M․
f93ded9852 [prosiebensat1] Add support for .ch domains (Closes #7365) 2015-11-05 01:54:49 +06:00
Sergey M․
89ea063eeb [youtube] Clarify rationale for preferring a video info with token (#7362) 2015-11-04 22:49:23 +06:00
Sergey M․
44b2264fea [youtube] Prefer video_info with token available 2015-11-04 22:12:24 +06:00
Jaime Marquínez Ferrándiz
cb5a470635 [vimeo] Remove unused import 2015-11-04 16:18:51 +01:00
Sergey M․
17d1900581 [vk] Fix view count extraction (Closes #7353) 2015-11-04 17:57:46 +06:00
Sergey M․
5d501a0901 [globo] Add more tests 2015-11-04 17:42:11 +06:00
Sergey M․
c13722480b [globo:article] Fix test 2015-11-04 17:13:35 +06:00
Sergey M․
e7d34c03f2 [globo] Force uploader id to be string 2015-11-04 17:12:42 +06:00
Sergey M․
264cd00fff [globo] Update tests 2015-11-04 17:10:45 +06:00
Sergey M․
a4a6b7b80f [globo] Improve http formats 2015-11-04 17:03:45 +06:00
Sergey M․
aebb42d32b [globo] Remove like count
It's no longer provided
2015-11-04 17:01:55 +06:00
Sergey M․
b4ef6a0038 [globo] Remove non available test 2015-11-04 17:01:27 +06:00
Sergey M․
5d235ca7f6 [globo] Prefer native m3u8 2015-11-04 16:55:39 +06:00
Sergey M․
c3459d24f1 [globo] Skip unsupported smooth streaming 2015-11-04 16:53:21 +06:00
Sergey M․
e3778cce0e [globo] Improve m3u8 extraction 2015-11-04 16:51:19 +06:00
Sergey M․
ad607563a2 [globo] Separate article extractor 2015-11-04 16:46:26 +06:00
Yen Chi Hsuan
236cb2131b Merge remote-tracking branch 'upstream/master' 2015-11-04 00:54:27 +08:00
Yen Chi Hsuan
66d041f250 [test/subtitles] Add test for DemocracynowIE 2015-11-04 00:53:30 +08:00
Yen Chi Hsuan
f3cb54e6d9 Merge branch 'atomicdryad-pr-democracynow' 2015-11-04 00:45:18 +08:00
Yen Chi Hsuan
0aeb9a106e [democracynow] Prevent required fields to be None 2015-11-04 00:13:00 +08:00
Yen Chi Hsuan
fd8102820c [democracynow] Rename js to json_data 2015-11-04 00:09:55 +08:00
Sergey M․
bfdf891fd3 [vimeo] Fix non-ASCII album passwords 2015-11-03 21:09:24 +06:00
Sergey M․
3fa3ff1bc3 [vimeo] Fix non-ASCII login 2015-11-03 21:06:36 +06:00
Sergey M․
0a0110fc6b [vimeo] Fix non-ASCII video passwords (2) 2015-11-03 21:01:09 +06:00
Sergey M․
852fad922f [vimeo] Fix non-ASCII video passwords (Closes #7352) 2015-11-03 20:53:17 +06:00
Yen Chi Hsuan
fc68d52bb9 [democracynow] Add MD5 sums 2015-11-03 21:24:10 +08:00
Yen Chi Hsuan
dde9fe9788 [democracynow] Simplify 2015-11-03 21:16:42 +08:00
Philipp Hagemeister
a230068ff7 release 2015.11.02 2015-11-02 16:18:54 +01:00
Jaime Marquínez Ferrándiz
6a75040278 [utils] unified_strdate: Return None if the date format can't be recognized (fixes #7340)
This issue was introduced with ae12bc3ebb, it returned 'None'.
2015-11-02 14:08:38 +01:00
remitamine
c514b0ec65 [videofy.me] fix info extraction
Closes #7339.
2015-11-02 13:55:21 +01:00
Jaime Marquínez Ferrándiz
eb97f46e8b [mitele] Fix extraction and update test checksum (fixes #7343) 2015-11-02 12:46:10 +01:00
Sergey M․
c90d16cf36 [utils:sanitize_path] Disallow trailing whitespace in path segment (Closes #7332) 2015-11-02 04:26:20 +06:00
Philipp Hagemeister
ab6ca04802 release 2015.11.01 2015-11-01 14:20:10 +01:00
Sergey M․
999079b454 [eitb] Improve hds extraction 2015-11-01 15:49:11 +06:00
Sergey M․
8a06999ba0 [eitb] Improve, make more robust and extract f4m formats (Closes #7328) 2015-11-01 01:52:33 +06:00
remitamine
80dcee5cd5 [eitb] fix info extraction 2015-11-01 00:56:16 +06:00
remitamine
9550ca506f [utils] change extract_attributes to work in python 2 2015-10-31 19:36:04 +01:00
Sergey M
30eecc6a04 Merge pull request #7296 from jaimeMF/xml_attrib_unicode
Use a wrapper around xml.etree.ElementTree.fromstring in python 2.x (…
2015-10-31 18:15:21 +00:00
Sergey M․
dbd82a1d4f [extractor/common] Fix m3u8 extraction on failure 2015-11-01 00:01:34 +06:00
Sergey M․
76f0c50d3d [mdr] Fix failed formats processing 2015-11-01 00:01:08 +06:00
Sergey M․
dc519b5421 [extractor/common] Make ie_key and IE_NAME return unicode string 2015-10-31 23:12:57 +06:00
Sergey M․
ae12bc3ebb [utils] Make unified_strdate always return unicode string 2015-10-31 23:07:37 +06:00
Sergey M․
e327b736ca [generic] Update test 2015-10-31 23:05:30 +06:00
Sergey M․
82b69a5cbb [mdr] PEP 8 2015-10-31 23:00:36 +06:00
Sergey M․
11465da702 [mdr] Simplify xpath 2015-10-31 22:45:45 +06:00
Sergey M․
578c074575 [utils] Support list of xpath in xpath_element 2015-10-31 22:39:44 +06:00
Sergey M․
8cdb5c8453 [mdr] Add audio test 2015-10-31 22:24:21 +06:00
Sergey M․
2b1b2d83ca [mdr] Modernize and include kika.de 2015-10-31 22:17:09 +06:00
Jaime Marquínez Ferrándiz
c3040bd00a [kika] Cleanup
Closes #6957.
2015-10-31 16:32:35 +01:00
Jaime Marquínez Ferrándiz
8c1aa28c27 [kika] Replace non working tests and recognize 'einzelsendung' urls. 2015-10-31 16:14:36 +01:00
Yen Chi Hsuan
78d7ee19dc [democracynow] Fix _TESTS 2015-10-31 22:21:52 +08:00
Lucas
892015b088 replaced inefficient code 2015-10-31 15:18:23 +01:00
Lucas
47f2d01a5a Add new extractor 2015-10-31 15:18:23 +01:00
Yen Chi Hsuan
33a513faf7 Merge branch 'pr-democracynow' of https://github.com/atomicdryad/youtube-dl into atomicdryad-pr-democracynow 2015-10-31 22:06:05 +08:00
remitamine
240384afe6 [clipfish] improve info extraction 2015-10-30 20:06:38 +01:00
Sergey M․
6722ebd437 [anitube] Relax key regex (Closes #7303)
Another variant seen http://anitubebr.xpg.uol.com.br/embed/
2015-10-30 21:00:36 +06:00
remitamine
9a605c8859 [adobetv] add support for show and channel extraction 2015-10-29 20:00:27 +01:00
remitamine
402ca40c9d [adobetv] extract AdobeTVVideo info from json directly 2015-10-29 19:55:04 +01:00
remitamine
30bd1c16c8 [adobetv] use api for extraction and add support specific language videos 2015-10-29 19:44:26 +01:00
Sergey M․
721f5a277c [moniker] Add tests for #7244 2015-10-29 22:47:18 +06:00
Sergey M․
6fb8ace671 [moniker] Add support for builtin embedded videos (Closes #7244) 2015-10-29 22:44:01 +06:00
Jaime Marquínez Ferrándiz
ae37338e68 [compat] compat_etree_fromstring: clarify comment 2015-10-29 13:58:40 +01:00
Sergey M․
03c2c162f9 [clyp] Improve and cleanup (Closes #7194) 2015-10-28 21:42:01 +06:00
Sergey M․
52c3a6e49d [utils] Improve parse_iso8601 2015-10-28 21:40:22 +06:00
Cian Ruane
4e16c1f80b [clyp] Add extractor
Update __init__.py

[clyp.it] Extract ID idiomatically and make duration and description optional
2015-10-28 20:37:19 +06:00
Jaime Marquínez Ferrándiz
7ccb2b84dd [francetv] fix style issues reported by flake8
* Don't redefine variable in list comprehension
* Line missing indentation
2015-10-28 08:22:04 +01:00
Sergey M․
0a192fbea7 [pluzz] Fix mobile support and modernize (Closes #7305) 2015-10-27 21:43:29 +06:00
Pierre Fenoll
a526167d40 [francetv] Accept mobile URLs 2015-10-27 21:39:29 +06:00
Jaime Marquínez Ferrándiz
f78546272c [compat] compat_etree_fromstring: also decode the text attribute
Deletes parse_xml from utils, because it also does it.
2015-10-26 16:41:24 +01:00
Sergey M․
c137cc0d33 [francetv] Add subtitles test 2015-10-26 20:35:45 +06:00
Sergey M․
6e4b8b2891 [francetv] Make subtitles more robust (Closes #7298) 2015-10-26 20:35:28 +06:00
Frans de Jonge
5dadae079b [francetv] Add subtitles support 2015-10-26 20:20:15 +06:00
Sergey M
cd08d806b1 Merge pull request #7297 from lalinsky/vidme-deleted
[vidme] Check for deleted videos
2015-10-26 13:47:42 +00:00
Lukáš Lalinský
5f9f87c06f [vidme] Check for deleted videos 2015-10-26 14:42:17 +01:00
Jaime Marquínez Ferrándiz
387db16a78 [compat] compat_etree_fromstring: only decode bytes objects 2015-10-25 20:30:54 +01:00
Jaime Marquínez Ferrándiz
36e6f62cd0 Use a wrapper around xml.etree.ElementTree.fromstring in python 2.x (#7178)
Attributes aren't unicode objects, so they couldn't be directly used in info_dict fields (for example '--write-description' doesn't work with bytes).
2015-10-25 20:13:16 +01:00
Sergey M․
755ff8d22c [youporn] Extract comment count 2015-10-25 23:41:10 +06:00
Sergey M․
7b3a19e533 [stitcher] Remove origEpisodeURL
It's always 404
2015-10-25 23:17:23 +06:00
Sergey M․
4f13f8f798 [youporn] Improve uploader extraction 2015-10-25 23:12:12 +06:00
Sergey M․
feb7711cf5 [youporn] Make description optional
Some videos does not contain any description
2015-10-25 23:01:12 +06:00
Sergey M․
589c33dade [youporn] Improve and make more robust (Closes #6888, closes #7214) 2015-10-25 22:56:35 +06:00
Erik
e572a1010b [youporn] Fix extraction
[youporn] Added description and thumbnail

[youporn] Added uploader and date

[youporn] Removed Try and Except lines

[youporn] Fixed date, fatal, formats and /s*

[youporn] Undid removing comment about video url components & Undid and fixed removal of encrypted URL detection

[youporn] Fix: Add encrypted link to links array only if not already in it

[youporn] Fix: Add encrypted link to links array only if not already in it

[youporn] Fix: cleanup
2015-10-25 20:57:08 +06:00
Sergey M․
7e0dc61334 [njoy] Add support for URLs without display id 2015-10-25 20:48:29 +06:00
Sergey M․
8e82ecfe8f [dailymotion] Extract f4m formats 2015-10-24 21:04:09 +06:00
Sergey M․
ec29539e06 [senateisvp] Pass extra param as query segment without ? 2015-10-24 21:03:45 +06:00
Sergey M․
8cd9614abf [downloader/f4m] More accurate fragment URL construction 2015-10-24 21:02:31 +06:00
remitamine
324ac0a243 [downloader/f4m] get the redirected f4m_url and handle url query string properly 2015-10-24 20:05:46 +06:00
remitamine
3711304510 [extractor/common] get the redirected m3u8_url in _extract_m3u8_formats 2015-10-24 19:01:54 +06:00
Jaime Marquínez Ferrándiz
50b936936d [tutv] Fix test 2015-10-24 14:22:47 +02:00
Jaime Marquínez Ferrándiz
d97da29da2 [abc] Support more URL formats 2015-10-24 12:43:02 +02:00
remitamine
7687b354c5 [abc] add support for audio extraction 2015-10-24 12:42:56 +02:00
Jaime Marquínez Ferrándiz
36d7281037 [spiegeltv] Fix style issue
Use two spaces before comment.
2015-10-24 12:42:08 +02:00
Jaime Marquínez Ferrándiz
865d1fbafc [extractor/common] Remove unused import 2015-10-24 12:39:23 +02:00
Sergey M․
ac21e71968 [spiegeltv] Check formats 2015-10-24 16:25:44 +06:00
Sergey M․
943a1e24b8 [extractor/common] Use more generic URLError in _is_valid_url 2015-10-24 16:25:04 +06:00
Sergey M․
50f01302d3 [spiegeltv] Do not extract m3u8 formats since it's already a format 2015-10-24 16:24:08 +06:00
Christoph Döpmann
0198807ef9 [spiegeltv] Fix Accept-Encoding issue (server chokes on gzip) 2015-10-24 16:21:14 +06:00
Jaime Marquínez Ferrándiz
6856139705 [mitele] Fix test checksum 2015-10-24 12:13:26 +02:00
Jaime Marquínez Ferrándiz
c93153852f [mitele] Don't encode the URL query (closes #7280)
This seems to produce sporadic errors when trying to access the URL, because on python 3.x when you do '%s' % b'somedata' you get "b'somedata'".
2015-10-24 12:10:53 +02:00
Philipp Hagemeister
ab9c7214ee release 2015.10.24 2015-10-24 00:10:41 +02:00
Philipp Hagemeister
dae69640d0 Fix py2exe build (#7276) 2015-10-24 00:10:28 +02:00
Sergey M
edeb3e7cb1 [README.md] Fix typo 2015-10-23 15:58:24 +00:00
Jaime Marquínez Ferrándiz
5c43afd40f [youtube:channel] Support age restricted channels (fixes #7277) 2015-10-23 14:23:45 +02:00
Jaime Marquínez Ferrándiz
9170ca5b16 [youtube:channel] Fix test 2015-10-23 14:16:08 +02:00
Jaime Marquínez Ferrándiz
65d49afa48 [test/test_download] Use extract_flat = 'in_playlist' for playlist items
Some playlist extractors return a 'url' result, which wouldn't be resolved.
2015-10-23 14:12:46 +02:00
Philipp Hagemeister
ab03c0b47c release 2015.10.23 2015-10-23 09:33:05 +02:00
Sergey M․
7690787553 [crunchyroll] Improve subtitle regex (Closes #7262) 2015-10-22 20:34:11 +06:00
Yen Chi Hsuan
a65402ef42 [bbc.co.uk:article] Add new extractor (#7257) 2015-10-22 21:13:03 +08:00
Yen Chi Hsuan
7033bc1a51 [bbc] Fix test_BBC_9 2015-10-22 21:12:29 +08:00
Yen Chi Hsuan
89d5fbf354 [iqiyi] Update key 2015-10-22 17:47:11 +08:00
Jaime Marquínez Ferrándiz
8c3533ba97 [adultswim] Don't default to the native m3u8 downloader (closes #7243)
Some of the streams are encrypted, which is not supported .
2015-10-21 23:57:23 +02:00
Sergey M․
44d6dd08b2 [facebook] Fix extraction (Closes #7252) 2015-10-21 21:35:57 +06:00
Sergey M․
cc449417c4 [vine] Use _search_regex for JSON data (Closes #7254, closes #7255) 2015-10-21 20:35:22 +06:00
remitamine
497f5fd93f [bilibili] extract multiple backup_urls 2015-10-21 08:24:05 +01:00
Sergey M․
7308b8cb3d [stitcher] Improve (Closes #7162, closes #7228) 2015-10-20 23:12:13 +06:00
mjdubell
4211c83aa4 [stitcher] Add extractor
Stitcher review updates

Removed re import

Stitcher review updates
2015-10-20 23:11:26 +06:00
Sergey M․
d01949dc89 [utils:js_to_json] Fix bad escape in double quoted strings 2015-10-20 23:09:51 +06:00
Sergey M.
63a6494834 Merge pull request #7242 from lalinsky/vimeo-seed-status
[vimeo] Fix error parsing
2015-10-20 20:57:12 +06:00
Lukáš Lalinský
8bea039b83 [vimeo] New test, fixed one older test 2015-10-20 16:38:44 +02:00
Sergey M․
d65889bbc0 [vidme] Update test 2015-10-20 20:18:23 +06:00
Sergey M․
4a8963770e [vidme] Use original vid.me title template for untitled videos 2015-10-20 20:17:54 +06:00
Sergey M.
5b0aa2c7b1 Merge pull request #7237 from lalinsky/vidme2
[vidme] Stream URL fallback, better error message for suspended videos
2015-10-20 20:14:12 +06:00
Lukáš Lalinský
b6aa99aff8 [vimeo] Fix error parsing 2015-10-20 10:31:32 +02:00
remitamine
4bf5614195 [cspan] move get_text_attr to CSpanIE 2015-10-20 07:43:39 +01:00
Lukáš Lalinský
0be30bafa4 [vidme] Stream URL fallback, better error message for suspended videos 2015-10-19 20:53:27 +02:00
Sergey M․
7b091c370c [zdf] Modernize and PEP 8 2015-10-19 01:48:05 +06:00
Sergey M.
334b5c3b72 Merge pull request #7225 from kennell/master
[zdf] Extract thumbnails
2015-10-19 01:30:14 +06:00
kennell
b7cedb1604 simplify thumbnail dict building 2015-10-18 21:25:26 +02:00
Sergey M.
2038ad6ee7 [README.md] Add uploader extraction sample in example extractor 2015-10-19 01:12:41 +06:00
kennell
b243340f0c check if key attrib matches resolution pattern 2015-10-18 21:07:52 +02:00
kennell
8cc83d301d use int_or_none, check if attrib exists, remove thumbnail 2015-10-18 20:47:42 +02:00
Sergey M․
d762f86e94 [ok] Extend _VALID_URL 2015-10-19 00:11:16 +06:00
kennell
264b23e1a4 adds thumbnail support for ZDF Mediathek extractor 2015-10-18 19:56:22 +02:00
Philipp Hagemeister
a6e0afa2bb release 2015.10.18 2015-10-18 19:23:40 +02:00
Yen Chi Hsuan
4285a47f40 Merge pull request #7208 from yan12125/letv-fix
[Letv] Fix extraction
2015-10-18 22:32:10 +08:00
Sergey M․
e36963e0eb [eagleplatform] Identify hls formats 2015-10-18 20:24:33 +06:00
Sergey M․
dedd35c6bc [viewster] Fix failing m3u8 2015-10-18 19:59:18 +06:00
Sergey M․
608945d44a [canalc2] Fix test 2015-10-18 19:27:22 +06:00
Sergey M․
b1bf063503 [canalc2] Extract duration 2015-10-18 19:27:05 +06:00
Sergey M․
14bddf35fb [canalc2] Add ext 2015-10-18 19:23:52 +06:00
Sergey M․
ef6c868f23 [canalc2] Improve some regexes 2015-10-18 19:23:31 +06:00
Sergey M․
6682049dee [canalc2] Improve rtmp extraction 2015-10-18 19:19:43 +06:00
remitamine
b0f001a6cb [canalc2] fix info extraction 2015-10-18 19:06:53 +06:00
Jaime Marquínez Ferrándiz
dd67702a3e [imdb] Fix extraction (fixes #7220) 2015-10-18 14:13:06 +02:00
Yen Chi Hsuan
05a3879f1c [letv] Update M3U8's MIME type
The new MIME type appears in the following places:
https://www.iana.org/assignments/media-types/media-types.xhtml#application
https://hg.python.org/cpython/file/tip/Lib/mimetypes.py
2015-10-18 19:19:46 +08:00
Yen Chi Hsuan
4a7b790384 [twitter:card] Support YouTube embeds 2015-10-18 19:07:37 +08:00
Yen Chi Hsuan
09ff81316e Merge branch 'atomicdryad-pr-twitter' 2015-10-18 18:44:21 +08:00
Yen Chi Hsuan
c88aec845a [twitter] Fix short URL extraction 2015-10-18 18:23:56 +08:00
Yen Chi Hsuan
77a54b6a65 [twitter:card] Use _html_search_regex 2015-10-18 18:08:24 +08:00
Yen Chi Hsuan
575036b405 [twitter] Simplify and improve 2015-10-18 18:04:13 +08:00
Yen Chi Hsuan
f6dfd6603a [twitter] Use _html_search_regex 2015-10-18 17:18:01 +08:00
Yen Chi Hsuan
e04edad621 [twitter] Inherit from InfoExtractor directly 2015-10-18 17:16:57 +08:00
Yen Chi Hsuan
f322bfb063 [twitter:card] Remove unneeded 'ext' 2015-10-18 17:15:47 +08:00
Yen Chi Hsuan
014e880372 [twitter] Add IE_NAMEs 2015-10-18 17:13:58 +08:00
Yen Chi Hsuan
01d22d4703 [twitter] Use _download_xml 2015-10-18 17:11:55 +08:00
Yen Chi Hsuan
48aae2d2cf [twitter] Update tests 2015-10-18 17:07:48 +08:00
Yen Chi Hsuan
c571dea953 Merge branch 'pr-twitter' of https://github.com/atomicdryad/youtube-dl into atomicdryad-pr-twitter 2015-10-18 16:49:56 +08:00
Yen Chi Hsuan
8b172c2e10 [YoutubeDL] Use DataHandler 2015-10-18 13:44:22 +08:00
Yen Chi Hsuan
0a67a3632b [compat] Add compat_urllib_request_DataHandler 2015-10-18 13:44:21 +08:00
Yen Chi Hsuan
985e4fdc07 [downloader/hls] Add headers only for http(s) URLs
ffmpeg 2.8.1 raises an error with -headers and non-http input files.
2015-10-18 13:44:21 +08:00
Yen Chi Hsuan
1e399778ee [letv] Fix extraction
Using data URIs for passing the decrypted M3U8 manifest, which is
supported by ffmpeg only.
2015-10-18 13:42:57 +08:00
Sergey M․
2e022397c4 [vine] Add counters to tests 2015-10-18 09:36:19 +06:00
Sergey M․
02835c6bf4 [extractor/common] Document repost_count 2015-10-18 09:34:54 +06:00
Sergey M․
91816e8f16 [vine] Remove duplicate metadata, make more robust and modernize (Closes #7215) 2015-10-18 09:32:08 +06:00
Lukáš Lalinský
10c38c7ca2 [vine] Fix download tests 2015-10-18 09:20:54 +06:00
Lukáš Lalinský
94a773feb9 [vine] Use JS data to get title/alt_title 2015-10-18 09:20:46 +06:00
Sergey M․
448ef1f31c [extractor/common] Allow angle brackets in attributes in _og_regexes (#7215) 2015-10-18 09:11:02 +06:00
Sergey M․
49941c4e4f [crunchyroll] Add maturity wall reference tests (#7202) 2015-10-18 07:06:47 +06:00
Sergey M․
80f48920c8 [crunchyroll] Bypass maturity wall (Closes #7202) 2015-10-18 06:57:57 +06:00
remitamine
520e753390 [bilibili] add support for specefic page extraction 2015-10-17 23:12:58 +01:00
remitamine
355c7ad361 [cspan] handle error massages and extract qualities 2015-10-17 21:30:38 +01:00
Sergey M․
5a11b793fe [lynda] Extract all prioritized streams 2015-10-18 01:36:03 +06:00
Sergey M․
7593fbaa12 [dailymotion] Error spelling 2015-10-18 01:00:37 +06:00
Sergey M.
2eb0f72a0e Merge pull request #7212 from lalinsky/dailymotion-error
[dailymotion] Report errors from player v5
2015-10-18 00:54:27 +06:00
Sergey M․
8e5b121948 [test_youtube_lists] Add test flat playlist entries' titles 2015-10-18 00:27:06 +06:00
Sergey M․
648e6a1ffe [youtube] Generalize playlist entries extraction (Closes #6699, closes #6992) 2015-10-18 00:11:34 +06:00
remitamine
55af2b26e0 [bilibili] extract backup url 2015-10-17 18:30:51 +01:00
Lukáš Lalinský
583882fdce [dailymotion] Report errors from player v5 2015-10-17 19:26:30 +02:00
Sergey M․
9eb31b265f [vidme] Add user-disabled test 2015-10-17 23:01:24 +06:00
Sergey M.
ddeb1b3de2 Merge pull request #7211 from lalinsky/vidme-suspended
[vidme] Better error message for suspended vidme videos
2015-10-17 22:56:51 +06:00
Lukáš Lalinský
59fe4824f8 [vidme] Better error message for suspended vidme videos 2015-10-17 18:52:25 +02:00
Sergey M․
dd8417526b [vimeo] Clarify new react+flux website fallback 2015-10-17 22:48:14 +06:00
Sergey M.
09670d5ba4 Merge pull request #7209 from lalinsky/vimeo-new-page
Extract config URL from (new?) React-based Vimeo's page
2015-10-17 22:39:17 +06:00
Lukáš Lalinský
41a7b00f18 [vimeo] Extract config URL from (new?) React-based Vimeo's page 2015-10-17 18:30:56 +02:00
remitamine
d90e40305b [bilibili] fix info extraction 2015-10-17 17:28:09 +01:00
Sergey M․
350c948133 [twitch:vod] Formatting 2015-10-17 18:43:12 +06:00
Sergey M․
e5e9966199 [twitch:vod] Improve extraction 2015-10-17 18:29:54 +06:00
Sergey M․
fbd9f6ea80 [twitch] Improve authentication 2015-10-17 18:28:21 +06:00
Jaime Marquínez Ferrándiz
6df7179e6c [rte] Actually recognize https urls
There was a missing 's' before the '?'.
2015-10-17 11:53:59 +02:00
Jaime Marquínez Ferrándiz
36eb802baf [rte] Replace expired test
According to their webpage it should be available until October 2035.
2015-10-17 11:49:51 +02:00
Philipp Hagemeister
2ccb37beb9 release 2015.10.16 2015-10-16 21:40:38 +02:00
Sergey M․
246ce10858 [rte] Update _VALID_URL (Closes #7198) 2015-10-17 01:08:23 +06:00
Sergey M․
ba717dca97 [bandcamp] Modernize 2015-10-17 00:51:35 +06:00
Sergey M․
1e52776ac3 [bandcamp] Prepend download URL with scheme when necessary (2) (#7077) 2015-10-17 00:46:38 +06:00
Sergey M․
8daeeedc06 [bbc] Fix FutureWarning 2015-10-17 00:26:45 +06:00
remitamine
cce9d15d01 [ooyala] extract domain,handle errors and change related tests 2015-10-16 16:02:40 +01:00
remitamine
dd414c970b [ooyala] fix sorting and format id 2015-10-16 10:12:42 +01:00
Sergey M․
6744f36db7 [jeuxvideo] Fallback on og:title (Closes #7186, closes #7190) 2015-10-16 08:44:19 +06:00
remitamine
497ca088a6 [ooyala] remove print statment 2015-10-15 14:37:05 +01:00
remitamine
90bddb6cdd [ooyala] extract more formats and metadata 2015-10-15 14:28:56 +01:00
Sergey M․
fafc7950e2 [channel9] Update tests' thumbnails 2015-10-15 01:59:11 +06:00
Sergey M․
a13d06de42 [channel9] Add test for low quality mp4 2015-10-15 01:57:59 +06:00
Sergey M․
1db82381e3 [channel9] Add low quality formats and modernize 2015-10-15 01:52:25 +06:00
Sergey M.
cb8961eeed Merge pull request #7176 from remitamine/megavideoz
[megavideoz] remove extractor
2015-10-14 22:41:27 +06:00
Sergey M․
af98f8ff37 [utils] Return default on fail in int_or_none 2015-10-14 22:37:03 +06:00
Sergey M․
caf80631f0 [utils] Do not fail in float_or_none on non-numeric data 2015-10-14 22:36:37 +06:00
Sergey M․
1812afb7b3 [utils] Do not fail in int_or_none on non-numeric data (Closes #7175) 2015-10-14 22:35:01 +06:00
remitamine
9fb66c780c [megavideoz] remove extractor 2015-10-14 17:25:07 +01:00
Sergey M․
ab953c64a0 [yandexmusic:track] Extract original size thumbnail (Closes #7160) 2015-10-14 21:15:29 +06:00
Sergey M․
db0a8ad979 [test_InfoExtractor] Add test for unquoted attribute 2015-10-14 21:11:06 +06:00
Sergey M․
1c29e81e62 [test_InfoExtractor] Add test for 7a6d76a64d 2015-10-14 20:58:52 +06:00
Sergey M․
7a6d76a64d [extractor/common] Require closing quote in _og_regexes (Closes #7174)
E.g. do not match `property='og:video:type'` when `og:video` is requested.
2015-10-14 20:49:39 +06:00
Sergey M․
4aa353673b Merge branch 'master' of github.com:rg3/youtube-dl 2015-10-14 20:32:11 +06:00
Sergey M.
f49b3d6efc Merge pull request #7173 from remitamine/criterion
[criterion] fix description extraction
2015-10-14 19:49:54 +06:00
remitamine
36bb63fad1 [criterion] fix description extraction 2015-10-14 14:13:53 +01:00
Jaime Marquínez Ferrándiz
1f36085df9 [vimeo] Fix extraction of password protected videos (fixes #7169) 2015-10-14 13:41:39 +02:00
remitamine
26669ea3cf [5min] extract more video info and formats
Closes #7124.
2015-10-14 13:23:58 +02:00
kaspi
3dc582e5ea [fczenit] Add extractor
Closes #7143.
2015-10-13 22:31:12 +02:00
Jaime Marquínez Ferrándiz
506e261d20 [channel9] strip 'session_day' 2015-10-13 21:18:30 +02:00
Jaime Marquínez Ferrándiz
b30c4992a9 [channel9] Return a single dictionary for single videos (closes #7086)
Returning a list is deprecated.
2015-10-13 21:14:33 +02:00
DesweR
3eeff489e8 Extract thumbnail url 2015-10-13 22:17:42 +06:00
Sergey M․
5946cda7c6 [beeg] Fix extraction (Closes #7155) 2015-10-13 21:04:39 +06:00
Philipp Hagemeister
ee2edd838a release 2015.10.13 2015-10-13 00:53:05 +02:00
Yen Chi Hsuan
73e732eb6b [vimeo] Fix m3u8 formats
Reported at
https://github.com/rg3/youtube-dl/pull/7126#issuecomment-147327584
2015-10-12 16:36:17 +08:00
Philipp Hagemeister
cd7364a89c release 2015.10.12 2015-10-12 06:37:20 +02:00
Jaime Marquínez Ferrándiz
57d1db8dd0 [rtbf] Fix extraction (closes #7133) 2015-10-11 22:45:13 +02:00
Sergey M․
964e7b2dd0 [downloader/common] Always skip "already downloaded" check when outputting to stdout 2015-10-12 00:43:54 +06:00
AndroKev
f101079ae0 [downloader/rtmp] Respect --no-continue option
now when downloading a "rtmp-file" the --no-continue option works!
2015-10-12 00:40:37 +06:00
Sergey M․
0f61db4469 [chaturbate] Improve and capture error message 2015-10-11 20:36:20 +06:00
PC
1bd3903582 chaturbate streams 2015-10-11 20:36:12 +06:00
Sergey M․
da4daed5ef [vimeo] Do not fail when no hls formats 2015-10-11 19:36:23 +06:00
Sergey M․
7d49502ab0 [bild] Make more robust and improve hls extraction 2015-10-11 19:28:00 +06:00
remitamine
03e3b4e119 [expotv] parse m3u8 manifest 2015-10-11 19:24:19 +06:00
Sergey M․
d8348c351d [bild] Strip title 2015-10-11 19:17:18 +06:00
remitamine
70cb4d51c9 [bild] extract info from json request 2015-10-11 19:17:09 +06:00
Sergey M․
7c84562945 [vimeo] Fix password protected videos 2015-10-11 02:31:37 +06:00
Sergey M․
68f3b61f0e [vimeo] Update tests 2015-10-11 02:22:42 +06:00
Sergey M.
08fd433f3e Merge pull request #7126 from remitamine/vimeo
[vimeo] extract m3u8 manifest and bitrate
2015-10-11 02:15:01 +06:00
remitamine
35a3ff1d33 [vimeo] always convert width, height and bitrate to int 2015-10-10 21:05:29 +01:00
remitamine
fff496c689 [vimeo] remove check for empty formats 2015-10-10 20:45:34 +01:00
remitamine
e5c209a1bc [vimeo] add parameters to _extract_m3u8_formats and sort formats 2015-10-10 20:34:10 +01:00
Sergey M․
75f105d455 [adultswim] Clarify no media message 2015-10-11 01:04:41 +06:00
remitamine
00cde0b8dc [adultswim] raise ExtractorError if no clips in video_info 2015-10-11 01:03:46 +06:00
remitamine
58cd7e173e [adultswim] detect when video needs authentication 2015-10-11 01:03:39 +06:00
remitamine
f4076bb736 [vimeo] extract m3u8 manifest and bitrate 2015-10-10 19:56:55 +01:00
Sergey M․
6f7893653c [bbc] PEP 8 2015-10-11 00:39:28 +06:00
Sergey M․
55ebae26f9 [bbc] Prefer iptv-all mediaset 2015-10-11 00:37:39 +06:00
Sergey M․
ae8bdfd1a1 [bbc] Extract article JSON and actualize tests 2015-10-11 00:25:09 +06:00
Sergey M․
6a74719060 [bbc] Update tests 2015-10-10 23:56:55 +06:00
Sergey M․
f6295bcb04 [bbc] Remove duration 2015-10-10 23:45:15 +06:00
Sergey M․
78f9d84318 [bbc] Support playlists of data-playable 2015-10-10 23:40:20 +06:00
Sergey M․
b1ec70e4a9 [bbc] Improve data-playable regex 2015-10-10 23:20:30 +06:00
Sergey M․
c936d8cc7b [bbc] Add another test 2015-10-10 23:14:25 +06:00
Sergey M․
e6174ee975 [bbc] Extract legacy playlist embedded media 2015-10-10 23:01:20 +06:00
Sergey M․
f790c43f6e [zingmp3:album] Skip broken items 2015-10-10 22:09:21 +06:00
Sergey M․
8119597d6f [zingmp3] Add fatal flag 2015-10-10 22:08:38 +06:00
Sergey M․
43abd79950 [zingmp3:album] Style 2015-10-10 22:05:01 +06:00
Phạm Ngọc Quang Nam
97ae4d166c [zingmp3:album] Add support for playlists
Update for work with playlist, it same album but different url
Ex:
http://mp3.zing.vn/playlist/Duong-Hong-Loan-apollobee/IWCAACCB.html
http://mp3.zing.vn/album/Duong-Hong-Loan-apollobee/IWCAACCB.html
2015-10-10 22:03:05 +06:00
Sergey M․
87cc0fbd18 [bbc] Make summary optional in legacy playlist 2015-10-10 21:38:09 +06:00
Sergey M․
baf39a1aa8 [bbc] Add one more scenario for data-playable embeds 2015-10-10 21:38:01 +06:00
Sergey M․
975977860d [bbc] Make playlist title optional 2015-10-10 21:37:53 +06:00
Sergey M․
b5d48cb1ef [bbc] Add test for atricle with multiple videos embedded with playlist.sxml 2015-10-10 21:37:43 +06:00
Sergey M․
de66571371 [bbc] Support multiple videos in articles embedded with playlist.sxml 2015-10-10 21:37:34 +06:00
Sergey M․
dab062fb6e [bbc] Add support for videos in news articles embedded with data-playable 2015-10-10 21:37:23 +06:00
Yen Chi Hsuan
6a959f2e52 [iqiyi] Update enc_key 2015-10-10 15:03:01 +08:00
Sergey M․
47c165c3a9 [vimeo] Fix authentication (Closes #7110) 2015-10-10 05:56:01 +06:00
Sergey M․
4180a3d8b7 [extractor/common] Allow quoteless content attribute in og regexes (Closes #7115) 2015-10-10 01:46:01 +06:00
Philipp Hagemeister
ef47b2c15f release 2015.10.09 2015-10-09 09:09:22 +02:00
Yen Chi Hsuan
57935b2564 [extractor/common] Allow HTML5 unquoted attribute values
Fixes #7108

HTML5 allows unquoted attribute values. See the "Unquoted attribute value
syntax" section [1] for more information

[1] http://www.w3.org/TR/html5/syntax.html
2015-10-09 14:11:00 +08:00
Sergey M․
54a5428518 [dailymotion] Update player v5 regex (Closes #7107) 2015-10-09 03:54:49 +06:00
Sergey M․
9d5fb3b58d [pbs] Carry long line 2015-10-09 00:09:10 +06:00
Sergey M.
cbb7287204 Merge pull request #7105 from remitamine/pbs
[pbs] detect errors from http error code
2015-10-09 00:06:49 +06:00
remitamine
0553d0ee40 [pbs] place errors into a dict 2015-10-08 18:57:57 +01:00
remitamine
7faf7e7523 [pbs] detect errors from http error code 2015-10-08 18:39:24 +01:00
Sergey M․
4356d907c1 [4tube] Try extracting sources from the webpage before fetching player.js (Closes #7103) 2015-10-08 21:05:48 +06:00
Sergey M․
c677e49bd1 [4tube] Revert uploader regexes and make non fatal 2015-10-08 21:05:35 +06:00
Sergey M․
96c4855331 [4tube] Style and make more robust 2015-10-08 21:05:27 +06:00
kitty
b90b0c4ffa Fixed 4tube.com extractor to pull metadata from associated Javascript and not the HTML of the desired page. 2015-10-08 21:05:16 +06:00
Sergey M.
c725e2c8b0 Merge pull request #7100 from AndroKev/nowtv-fix
[nowtv] Update rtmp player URL
2015-10-08 19:42:46 +06:00
AndroKev
db7a28eccb FIX: nowtv
now the download works for me
thx to http://board.gulli.com/thread/1251646-videos-von-webseiten-runterladen-sammelthread-alle-fragen-hier-rein-/?p=14980081#post14980081
2015-10-08 09:56:39 +02:00
remitamine
6a11bb77ba [nba] add support for team subsites 2015-10-07 12:17:32 +01:00
Philipp Hagemeister
181f814e57 Merge pull request #7085 from TomGijselinck/patch-1
Fix typos and improve grammar and spelling
2015-10-07 12:14:17 +02:00
Tom Gijselinck
bd5376c182 Fix typos and improve grammar and spelling 2015-10-07 10:32:44 +02:00
remitamine
ecf6de5b02 [nba] extract width,height and bitrate from format key 2015-10-07 07:09:45 +01:00
remitamine
139f27827e [nba] skip Legacy Video Files 2015-10-07 06:53:19 +01:00
Philipp Hagemeister
945e5c56e3 release 2015.10.06.2 2015-10-06 23:46:16 +02:00
Sergey M․
fc10824cb6 [canalplus] PEP 8 2015-10-07 02:43:12 +06:00
Sergey M․
83a5668694 [canalplus] Extend video id regex (Closes #7076) 2015-10-06 23:08:28 +06:00
Sergey M․
f648e682a7 [bandcamp] Prepend download URL with scheme when necessary (Closes #7077) 2015-10-06 22:58:18 +06:00
Sergey M․
f2dbc54066 [compat] Fix wrong lines/columns order
stty size is rows x columns
2015-10-06 22:02:28 +06:00
Philipp Hagemeister
86be82610c release 2015.10.06.1 2015-10-06 17:43:50 +02:00
Philipp Hagemeister
4810c48d6d [compat] Do not compare None <= 0
The result is meaningless (and it emits a warning in cpython2 when called with -3), so handle None before making integer comparisons.
2015-10-06 14:30:43 +02:00
Philipp Hagemeister
c4af7684d8 release 2015.10.06 2015-10-06 09:08:10 +02:00
Sergey M
fcc2546269 [README.md] Markdown improvements 2015-10-06 02:31:49 +06:00
Sergey M․
40fbb05e1c [ustream] Fix tests 2015-10-05 22:52:51 +06:00
Sergey M․
dc5756fd77 [ustream] Fix typo 2015-10-05 22:51:04 +06:00
Sergey M․
41db733308 [ustream] Move filesize 2015-10-05 22:48:47 +06:00
Sergey M․
0bf219889e [ustream] Remove unused import 2015-10-05 22:44:59 +06:00
Sergey M․
f2a7ed77ef [tumblr] Remove redundant field 2015-10-05 22:44:36 +06:00
Sergey M․
4853eb63fe [ustream] Modernize 2015-10-05 22:40:20 +06:00
Sergey M․
5820c4a29e [ustream] Switch extraction to api 2015-10-05 22:30:38 +06:00
David Rabinowitz
7fd4ed9939 Fixed the ustream extractor to use the current ustream API 2015-10-05 22:30:14 +06:00
Sergey M․
88c86d211b [tumblr] Add missing fields for vidme test 2015-10-05 21:54:54 +06:00
Sergey M․
5d84b79a30 [tumblr] Remove redundant test 2015-10-05 21:53:59 +06:00
Sergey M․
140ac73965 [tumblr] Simplify and extract duration 2015-10-05 21:53:01 +06:00
Oli Allen
2a27e66234 [tumblr] Added support for HD video where available (#7036)
[tumblr] Replaced test URL for HD video as old one lead to 404

[tumblr] Don't make assumptions about video resolution, cleaner handling of no HD version available

[tumblr] Removed extraneous resolution key in HD video tests
2015-10-05 21:51:03 +06:00
Sergey M․
e759a00119 [appletrailers] Quotes consistency 2015-10-05 20:21:53 +06:00
Sergey M.
9d5332518c Merge pull request #6963 from remitamine/appledaily
[nextmedia] update AppleDailyIE tests
2015-10-05 20:12:24 +06:00
Sergey M․
90ab741e90 [pbs] Add test for #7059 2015-10-04 21:37:49 +06:00
Sergey M․
96229998c2 [pbs] Allow empty attribute in player regex 2015-10-04 21:19:47 +06:00
Sergey M․
0659dfccfe [pbs] Improve player regex (Closes #7059) 2015-10-04 21:13:13 +06:00
Sergey M․
9c544e2537 [limelight] Add test video with subtitles 2015-10-04 20:48:44 +06:00
Sergey M․
d7fc56318b [limelight] Fix python 2.6, simplify, make more robust (Closes #6734) 2015-10-04 20:42:35 +06:00
Sergey M․
4bba371644 [YoutubeDL] Autocalculate ext for subtitles when missing 2015-10-04 20:42:26 +06:00
remitamine
ef5acfe32d [limelight] Add new extractor 2015-10-04 20:42:18 +06:00
remitamine
30787f7259 [cspan] correct the clip info extraction 2015-10-03 19:28:48 +01:00
Sergey M.
85557f635a Merge pull request #7052 from remitamine/engadget
[engadget] accept short video urls
2015-10-03 22:36:49 +06:00
Naglis Jonaitis
60d23e5e59 [tapely] Improve _VALID_URL 2015-10-03 16:25:33 +03:00
remitamine
97d5bfcba6 [engadget] accept short video urls 2015-10-03 14:17:17 +01:00
remitamine
c233e6bcc3 [nba] extract video info from xml feed 2015-10-03 12:30:05 +01:00
remitamine
28809ab07a [nba] extract more formats 2015-10-03 09:47:19 +01:00
Yen Chi Hsuan
bad84757eb [doc] Better formatting of youtube-dl.1 (closes #6510) 2015-10-03 00:01:10 +02:00
Yen Chi Hsuan
13118a50b8 [compat] Allow overriding by only COLUMNS or LINES in compat_get_terminal_size
Now the semantic of this function is identical to
shutil.get_terminal_size() in Python 3.3+. The new behavior also
corresponds to the old get_term_width(), which is removed in
003c69a84b
2015-10-03 00:00:33 +02:00
Yen Chi Hsuan
5495937f46 [options] Cleanup double spaces in help texts 2015-10-02 23:59:47 +02:00
Jaime Marquínez Ferrándiz
b203095d4c [europa] Style fix: add whitespace after comma 2015-10-02 22:40:35 +02:00
Sergey M․
f3b098fb90 [europa] Add support for audio URLs 2015-10-02 23:22:53 +06:00
Sergey M․
af17794c65 [europa] Improve extraction 2015-10-02 22:29:15 +06:00
remitamine
8fc226ef99 [nba] extract all video formats and extract more info 2015-10-02 17:24:30 +01:00
ngld
3bb3f04108 [europa] Add new extractor 2015-10-02 21:30:07 +06:00
Sergey M․
59a9efe85b [ruutu] Limit resolution split to 2 pieces (Closes #7037, closes #7042) 2015-10-02 20:48:39 +06:00
fluks
0facd2af3e Fix ruutu extractor bug
If there's no resolution attribute in xml, only width gets a
value, height doesn't and ValueError is raised.
2015-10-02 20:48:14 +06:00
Jaime Marquínez Ferrándiz
7d0ada5ff9 [test/helper] Fix style
Use the correct indentation to please flake8
2015-10-02 13:42:11 +02:00
Jaime Marquínez Ferrándiz
44451f22d5 [naver] Remove unused import 2015-10-02 13:41:52 +02:00
Sergey M․
06c6efa970 [videolecturesnet] Add test video with broken direct format links 2015-10-01 23:10:36 +06:00
Sergey M․
e5851b963a [extractor/common] Make f4m extraction for SMIL non fatal 2015-10-01 23:04:56 +06:00
Sergey M․
4de6131090 [extractor/common] Add fatal to _extract_f4m_formats 2015-10-01 23:03:31 +06:00
Sergey M․
3a1341a7bc [extractor/common] Make m3u8 extraction for SMIL non fatal 2015-10-01 22:59:20 +06:00
Sergey M․
c78e48177c [extractor/common] Check validity of direct URLs 2015-10-01 22:54:54 +06:00
Sergey M․
6edaa0e25b [videolecturesnet] Add playlist test 2015-10-01 22:45:10 +06:00
Sergey M․
fb97809e64 [videolecturesnet] Improve playlist extraction 2015-10-01 22:44:51 +06:00
Sergey M․
0c996b9f48 [videolecturesnet] Add support for playlists (Closes #7031) 2015-10-01 22:39:38 +06:00
Sergey M․
acfb717a18 [videolecturesnet] Use generic SMIL extraction 2015-10-01 22:20:35 +06:00
Sergey M․
647eab4541 [extractor/common] Extract upload date from SMIL 2015-10-01 22:20:28 +06:00
Sergey M․
1e5bcdec02 [extractor/common] Extract images from SMIL 2015-10-01 22:20:21 +06:00
Sergey M․
e7d8e98a9f [extractor/common] Allow float bitrates 2015-10-01 22:20:15 +06:00
Sergey M․
2b3f951a2e [nrktv] Rework subtitles and eliminate downloading twice 2015-10-01 20:33:17 +06:00
Sergey M.
6751a1284d Merge pull request #7035 from jfremstad/nrk-fix-spelling
[nrk] Spelling
2015-10-01 19:56:21 +06:00
Joakim Fremstad
b83831df1f [nrk] Spelling 2015-10-01 14:58:49 +02:00
Sergey M․
f540b93706 [naver] Improve error regex 2015-10-01 02:33:48 +06:00
Sergey M․
8466336104 [vk] Detect vimeo embeds (Closes #7021) 2015-09-30 22:12:52 +06:00
Sergey M․
f88f1b40ce [test/helper] Clarify field for list length mismatch 2015-09-30 20:33:59 +06:00
Sergey M․
386a7b52d5 [test/helper] Spelling 2015-09-30 20:33:51 +06:00
Sergey M․
2e885de796 [test/helper] Formatting 2015-09-30 20:33:45 +06:00
Qijiang Fan
687c04cbb8 [test] use descriptive variable name 2015-09-30 20:33:35 +06:00
Qijiang Fan
40c931de4b [test] split expect_dict to two functions 2015-09-30 20:33:30 +06:00
Qijiang Fan
93bc7ef165 [test] recursively check dict and list in expect_info_dict
This allows to use md5:, re:, etc within the str inside a list
or dict.
2015-09-30 20:33:20 +06:00
Sergey M․
ee2d190253 [nfl] Add test for #7012 2015-09-30 20:06:21 +06:00
remitamine
aedb930cfc [nfl] fix content id regex(fixes #7012) 2015-09-30 20:03:10 +06:00
Philipp Hagemeister
c596ce91cd [comedycentral] Fix youtube-dl :thedailyshow
We'll let the generic IE follow the redirect and call back to us with the episode URL
2015-09-30 15:39:52 +02:00
remitamine
c01e1a96aa [brightcove] fix test and fields extraction 2015-09-30 11:20:43 +01:00
Sergey M․
8a64969404 [adultswim] Prefer stream (Closes #7015) 2015-09-29 21:33:21 +06:00
Philipp Hagemeister
c254f75bbb release 2015.09.28 2015-09-28 04:42:11 +02:00
Sergey M․
86692c019c [keek] Strip title 2015-09-28 01:17:28 +06:00
Sergey M․
1ab1c4ef57 [keek] Improve uploader fields regexes 2015-09-28 01:15:13 +06:00
Sergey M․
926fb62eec [keek] Remove description
Since it equals title plus static suffix
2015-09-28 01:14:14 +06:00
Sergey M․
817690ff73 [keek] Make uploader fields non fatal 2015-09-28 01:05:24 +06:00
remitamine
98e1c935a1 [keek] extract uploader and uploader id with _search_regex 2015-09-28 01:03:22 +06:00
remitamine
f30e9976d6 [keek] add utf-8 coding cookie 2015-09-28 01:03:16 +06:00
remitamine
80e98aed69 [keek] fix test title 2015-09-28 01:03:12 +06:00
remitamine
6a24cb3d22 [keek] extract more info 2015-09-28 01:03:08 +06:00
remitamine
e13b9e7885 [keek] fix info extraction 2015-09-28 01:02:59 +06:00
Sergey M․
dd467d33d0 [extractor/generic] Add support for condenast script embeds (Closes #6885, closes #6991) 2015-09-27 05:55:48 +06:00
Sergey M․
c6b8f4d0c9 [condenast] Add support for JS embeds 2015-09-27 05:53:21 +06:00
Yen Chi Hsuan
95240b8093 Use insert for all sys.path manipulations
Closes #6867.
2015-09-26 22:04:41 +02:00
Sergey M․
2f962d0a91 [eagleplatform] Use http scheme for thumbnail 2015-09-27 01:17:44 +06:00
Sergey M․
3c63e1bb57 [eagleplatform] Make _handle_error staticmethod 2015-09-27 01:12:46 +06:00
Sergey M․
c471b34575 [eagleplatform] Simplify secure mp4 construction and clarify rationale 2015-09-27 01:10:39 +06:00
remitamine
d045f0bdb7 [eagleplatform] use http urls explicitly 2015-09-27 01:08:31 +06:00
remitamine
22becac4bd [eagleplatform] return the code to handle errors in all _download_json requests 2015-09-27 01:08:26 +06:00
remitamine
9d632b1b27 [eagleplatform] extract mp4 url and fix thumbnail url 2015-09-27 01:08:22 +06:00
Sergey M․
95c5e10103 [qqmusic] Allow [mm:ss] timestamps 2015-09-26 21:15:34 +06:00
Sergey M․
a5d09d684e [qqmusic] Use release_date 2015-09-26 21:08:23 +06:00
Sergey M․
8aab976bbd [extractor/common] Document release_date field 2015-09-26 21:07:54 +06:00
Sergey M․
26c6d1922e Credit @fqj1994 for qqmusic .lrc support 2015-09-26 21:01:28 +06:00
Sergey M․
cd1bb54990 [qqmusic] Add test for a song with non .lrc lyrics 2015-09-26 21:00:59 +06:00
Sergey M․
d4cd06138c [qqmusic] Do not capture braced text from the middle of the string 2015-09-26 20:54:41 +06:00
Sergey M․
961c5cbf17 [qqmusic] Eliminate _filter_lrc and use single quotes 2015-09-26 20:38:11 +06:00
Qijiang Fan
b65e5bb72f [qqmusic] Add subtitles for QQMusic
Use .lrc lyrics as subtitles if lyrics in lrc format exist.
2015-09-26 20:21:27 +06:00
Sergey M․
54914380c0 [bbc] Add test for programme that fails with iptv-all mediaset 2015-09-26 20:07:12 +06:00
Sergey M․
26ccc68bed [bbc] Clarify iptv-all mediaset rationale 2015-09-26 20:06:21 +06:00
Sergey M․
ee3d5a6d47 [bbc] Skip mediaselection on gelocation error (Closes #6983) 2015-09-26 19:57:17 +06:00
Sergey M․
46fde8a1a2 [extractor/generic] Use _extract_url for mtvservices 2015-09-26 19:47:20 +06:00
Sergey M․
fe1d858e35 [mtvservices:embedded] Add _extract_url 2015-09-26 19:46:42 +06:00
Sergey M․
fc42bc6ec9 [mtv] Look for sm4:video:embed (Closes #6936, closes #6970) 2015-09-26 19:45:43 +06:00
Yen Chi Hsuan
fe6ad195ae Merge pull request #6966 from remitamine/kuwo
[kuwo] fix title extraction and update test
2015-09-26 19:28:16 +08:00
remitamine
7193650641 [kuwo] treat the offline error as an expected ExtractorError 2015-09-26 11:44:35 +01:00
remitamine
5db34f680f [kuwo] check for the offline error page 2015-09-26 10:31:32 +01:00
Sergey M.
a82ba8d0ce Merge pull request #6978 from remitamine/fktv
[fktv] get format_id from video file ext
2015-09-26 13:01:21 +06:00
remitamine
3706fb5dc8 [fktv] get format_id from video file ext 2015-09-26 07:51:11 +01:00
Jaime Marquínez Ferrándiz
08bea4adde Also run tests with python 3.5 2015-09-25 22:34:02 +02:00
Jaime Marquínez Ferrándiz
4c917d0314 [README.md] Document the 'duration' field in the output template (#6929) 2015-09-25 22:02:48 +02:00
Jaime Marquínez Ferrándiz
4866b72eb2 [fktv] Don't redefine 'url' in list comprehension
Detected with flake8.
2015-09-25 21:58:45 +02:00
Yen Chi Hsuan
2d00be0477 Merge branch 'remitamine-fktv' 2015-09-25 19:28:26 +08:00
remitamine
3d09aa4c82 [kuwo] extract title inside element with class title exactly 2015-09-25 11:40:32 +01:00
remitamine
c44c7895b8 [kuwo] fix title extraction and update test 2015-09-25 11:28:26 +01:00
Yen Chi Hsuan
8de28761c4 [fktv] Fix a regex 2015-09-25 18:17:48 +08:00
Yen Chi Hsuan
711762f0b7 [fktv] Coding style 2015-09-25 18:01:08 +08:00
Yen Chi Hsuan
5773803961 [fktv] Correct thumbnail extraction and add the test 2015-09-25 17:58:44 +08:00
Yen Chi Hsuan
140359fc2c [fktv] Correct and improve some regexs 2015-09-25 17:51:48 +08:00
Yen Chi Hsuan
8ddf48d59f [fktv] Raise an error is no videos found 2015-09-25 17:48:51 +08:00
Yen Chi Hsuan
2e40a12225 [fktv] Correct spellings 2015-09-25 17:24:35 +08:00
Yen Chi Hsuan
dade7245af Merge branch 'fktv' of https://github.com/remitamine/youtube-dl into remitamine-fktv 2015-09-25 17:02:10 +08:00
remitamine
1f9fb20fcd [nextmedia] update AppleDailyIE tests 2015-09-25 07:39:22 +01:00
Sergey M․
0940c5b4c6 [condenast] Do not capture unused group in _VALID_URL 2015-09-25 05:18:45 +06:00
Sergey M․
42ca72dff3 [condenast] Keep acute accent 2015-09-25 05:15:21 +06:00
remitamine
2949a6cda9 [condenast] fix video info regex 2015-09-25 05:11:48 +06:00
remitamine
882fc9052e [condenast] fix extraction and add support for other sites 2015-09-25 05:11:38 +06:00
Sergey M․
9b166fc1f8 [iconosquare] Extract comments 2015-09-25 04:45:31 +06:00
Sergey M․
d4364f30bd [iconosquare] Revert title (Closes #6954) 2015-09-25 04:44:52 +06:00
remitamine
857421024d [iconosquare] fix info extraction 2015-09-25 04:36:15 +06:00
Sergey M.
80faa7a152 Merge pull request #6955 from atomic83/patch-1
More title extraction fixing.
2015-09-25 04:26:07 +06:00
atomic83
545a23f11b More title extraction fixing. 2015-09-24 23:05:32 +02:00
Sergey M.
caedb0721e Merge pull request #6952 from remitamine/hostingbulk
[hostingbulk] remove extractor
2015-09-25 02:08:26 +06:00
remitamine
47024eb564 [hostingbulk] remove extractor 2015-09-24 19:49:10 +01:00
Sergey M․
9c58885c70 [nhl:news] Add support for iframe embeds (Closes #6941) 2015-09-24 23:54:16 +06:00
Sergey M․
9fbd4b35a2 [nhl] Add support for embedded URLs 2015-09-24 23:48:23 +06:00
Sergey M․
05b476a270 [vidme] Prefer non clip (Closes #6924) 2015-09-24 23:38:53 +06:00
Sergey M․
4395ca2e04 [xhamster] Fix title extraction (Closes #6944) 2015-09-24 19:56:54 +06:00
Yen Chi Hsuan
19f93d906e [iqiyi] Use md5_text for all MD5 calls 2015-09-23 22:25:16 +08:00
Yen Chi Hsuan
57565375c8 [iqiyi] Fix extraction (fixes #6878) 2015-09-23 22:22:04 +08:00
Sergey M․
eb11cbe867 [soundcloud] Update client id (Closes #6930) 2015-09-23 19:54:40 +06:00
remitamine
53407e3f38 [brightcove] fix streaming_src extraction 2015-09-23 14:02:13 +01:00
Sergey M․
f102819463 [downloader/hls] Pass http headers to downloader 2015-09-23 02:46:24 +06:00
Philipp Hagemeister
b942db3dc3 release 2015.09.22 2015-09-22 22:41:53 +02:00
Sergey M․
78f9fb902b [9gag] Support embed URLs 2015-09-23 01:58:47 +06:00
Sergey M․
d8fef8faac [9gag] Quotes consistency 2015-09-23 01:56:26 +06:00
Sergey M․
8ea6bd2802 [9gag] Add vimeo test 2015-09-23 01:55:16 +06:00
Sergey M․
c659022b5c [9gag] Modernize 2015-09-23 01:48:13 +06:00
Sergey M․
8ca2e93e1a [9gag] Relax optional fields 2015-09-23 01:46:40 +06:00
Sergey M․
5600e214c3 [9gag] Make post view regex more robust 2015-09-23 01:44:38 +06:00
Sergey M․
6400f8ec0f [9gag] Allow old .tv domain
There are still references to it in webpage's source
2015-09-23 01:43:26 +06:00
Sergey M․
c3a4e2ec40 [9gag] Remove redundant test 2015-09-23 01:41:44 +06:00
Sergey M․
e28c794699 [9gag] Make display_id optional 2015-09-23 01:40:06 +06:00
remitamine
da9f180835 [ninegag] remove unnecessary condition 2015-09-23 01:39:13 +06:00
remitamine
6b8ce312e3 [ninegag] extract source url 2015-09-23 01:39:06 +06:00
remitamine
de3fc356e1 [ninegag] fix _VALID_URL regex and handle the use of other external providers 2015-09-23 01:39:00 +06:00
Sergey M․
d0fed4ac02 [viewster] Use tuple 2015-09-22 22:00:50 +06:00
Sergey M․
7ce50a355c [viewster] Add geo restricted tests 2015-09-22 21:55:04 +06:00
Sergey M․
9612f23399 [viewster] Detect video geo restriction 2015-09-22 21:54:32 +06:00
Sergey M․
cccedc1aa4 [voewster] Detect series geo restriction 2015-09-22 21:52:41 +06:00
Sergey M․
c430802e32 [extractor/common] Add raise_geo_restricted 2015-09-22 21:50:20 +06:00
Sergey M․
cb4e421901 [voewster] Update tests 2015-09-22 21:49:29 +06:00
Sergey M․
8e97596b7b [viewster] Extract height from bitrate and prefer mp4 videos 2015-09-22 21:47:56 +06:00
remitamine
92085e7099 [viewster] accept https links and fix api_token extraction and extract mp4 video link(fixes #6787) 2015-09-22 21:45:45 +06:00
Sergey M․
c6aa838b51 [youtube:history] Enable exractor 2015-09-21 21:28:02 +06:00
Sergey M․
9f5e8d16b3 [youtube:history] Disable exractor 2015-09-21 01:53:28 +06:00
Sergey M․
82c06a40ac Fix typo 2015-09-20 23:54:05 +06:00
Sergey M․
4423eba49b [nfl] Add support for URLs without id (Closes #6907) 2015-09-20 23:45:01 +06:00
Sergey M․
5b4c54631a [nfl] Add team domains (#6907) 2015-09-20 23:12:40 +06:00
Sergey M․
5a1a2e9454 [utils] Fix kwargs on old python 2 (Closes #6905) 2015-09-20 21:08:29 +06:00
Jaime Marquínez Ferrándiz
f005f96ea5 [youtube:history] Explain why it has disabled and skip test 2015-09-20 12:23:13 +02:00
Sergey M․
5e39123b3b [arte:+7] Fix typo 2015-09-20 11:47:03 +06:00
Sergey M․
393ca8c94d [arte:+7] Look for json vp url in iframe (Closes #6895) 2015-09-20 11:45:19 +06:00
Sergey M․
f817adc468 [youtube:history] Disable extractor until #6893 is investigated
Wiped out part of my history as well
2015-09-20 11:31:23 +06:00
Sergey M․
6c91a5a7f5 [extractor/generic] Fix following redirect in Refresh HTTP header on python 2 2015-09-20 11:16:12 +06:00
Sergey M.
749b09616d Merge pull request #6889 from remitamine/shahid
[shahid] fix api request url and change test ext
2015-09-19 03:28:57 +06:00
remitamine
5de5ab89b4 [shahid] change test ext 2015-09-18 22:10:36 +01:00
remitamine
1d67c96640 [shahid] fix api request url 2015-09-18 22:09:33 +01:00
Sergey M․
d1c694ea4a [bbc] Switch extraction to hq iptv hls (Closes #2732, closes #3023, closes #4828, closes #6803, closes #6855) 2015-09-19 00:51:41 +06:00
Sergey M․
06368a232a [playwire] Fix test 2015-09-18 21:01:55 +06:00
Jaime Marquínez Ferrándiz
8a7bbd1606 [postprocessor/ffmpeg] Always use the 'file:' protocol for filenames (fixes #6874)
If the filename contains ':' it is interpreted as a protocol.
It also handles filenames starting with '-'.
2015-09-18 14:38:35 +02:00
Sergey M․
131d05033b [ok] Skip test 2015-09-17 22:59:32 +06:00
Sergey M․
1806a75415 [ok] Capture error message 2015-09-17 22:59:15 +06:00
Sergey M․
659ffe204c [divxstage] Remove extractor
DivxStage is offline for quite some time already
2015-09-17 22:58:49 +06:00
Sergey M․
4647fd8910 [telecinco] Fix typo 2015-09-16 22:49:26 +06:00
Sergey M․
d492dad8f4 [telecinco] Add support for mediaset.es 2015-09-16 22:45:39 +06:00
Sergey M․
3368d70dce [mitele] Clarify IE_DESC 2015-09-16 22:39:48 +06:00
Sergey M․
0e1b2566ff [telecinco] Clarify IE_DESC 2015-09-16 22:39:24 +06:00
Sergey M․
369e60162e [telecinco] Update test 2015-09-16 22:37:47 +06:00
Sergey M․
d5e7657fe2 [telecinco] Add support or cuatro.com 2015-09-16 22:37:01 +06:00
Sergey M․
f84ce1ebaf [mitele] Fix extraction (Closes #6414) 2015-09-16 22:27:03 +06:00
Sergey M․
12bc242944 [telecinco] Incorporate mitele code 2015-09-16 22:25:25 +06:00
Sergey M․
88060cce10 [rai] Add support for videos embedded with drawMediaRaiTV (Closes #6866) 2015-09-15 21:57:30 +06:00
Sergey M․
272e4db5c7 [pornhub] Relax _VALID_URL (Closes #6868) 2015-09-15 21:24:01 +06:00
Sergey M.
6e21cc3b67 Merge pull request #6870 from alexvong1995/add-missing-tag
Add the missing tag `EMBEDDING YOUTUBE-DL` in the content table.
2015-09-15 21:18:11 +06:00
Alex Vong
0391bc8176 Add the missing tag EMBEDDING YOUTUBE-DL in the content table.
* README.md: Add missing tag.
2015-09-15 23:14:48 +08:00
Sergey M.
3b9264a049 [README.md] Fix typo 2015-09-14 23:07:03 +06:00
Philipp Hagemeister
2b3c254678 [youtube:channel] Correct 404 handling
Previously, when we encountered a 404 - such as youtube-dl https://www.youtube.com/ohJeiboh8oorehai - we crashed with a regexp error. Instead, make sure to go on and eventually report a 404.
2015-09-14 00:32:20 +02:00
Philipp Hagemeister
287be8c615 [youtube:truncated_url] Add ?t=
Sometimes found in links - ?t=123&v=ABCD starts the video at position 123.
2015-09-14 00:26:54 +02:00
Philipp Hagemeister
953fed280f [compat] Do not use unicode
If the code ever runs on 3.x, it would fail. Even if it never does, the unicode name confuses Python 3 code analysis tools.
2015-09-14 00:25:08 +02:00
Sergey M․
e2ff3df314 [clubic] Relax _VALID_URL (Closes #6854) 2015-09-14 03:35:15 +06:00
Sergey M.
31208a07c2 [README.md] Unify playlist description 2015-09-13 19:34:09 +06:00
Sergey M.
ac7a1b0dfb [README.md] Clarify playlist_index description 2015-09-13 19:31:20 +06:00
Sergey M․
c246773599 [nowness] Add vimeo test 2015-09-13 19:27:22 +06:00
Sergey M․
25cd56a715 Merge branch 'remitamine-nowness' 2015-09-13 19:19:13 +06:00
Sergey M․
82c18e2a53 [nowness] Simplify 2015-09-13 19:18:57 +06:00
Sergey M․
d5d38d16ae Merge branch 'nowness' of https://github.com/remitamine/youtube-dl into remitamine-nowness 2015-09-13 18:01:55 +06:00
Sergey M․
e1cbf33573 Merge branch 'remitamine-ndr' 2015-09-13 04:16:11 +06:00
Sergey M․
2ffe3bc14b [ndr] Rework and cover with tests 2015-09-13 04:15:49 +06:00
Sergey M․
d5867276a9 Merge branch 'master' of github.com:rg3/youtube-dl into remitamine-ndr 2015-09-13 01:10:39 +06:00
Yen Chi Hsuan
f665ef8fc5 Merge remote-tracking branch 'upstream/master' 2015-09-13 03:04:44 +08:00
Yen Chi Hsuan
b264c21302 [tudou] Use single quotes and compat_str 2015-09-13 02:59:43 +08:00
Yen Chi Hsuan
349b3a2ea0 [tudou] Improve regexs 2015-09-13 02:51:20 +08:00
Yen Chi Hsuan
87813a8570 [tudou] Use _download_xml 2015-09-13 02:36:51 +08:00
Yen Chi Hsuan
aab135516b [tudou] Avoid shadowing builtin names 2015-09-12 22:52:51 +08:00
Yen Chi Hsuan
141ba36996 [tudou] Modernize 2015-09-12 22:51:49 +08:00
Yen Chi Hsuan
d434ca5448 Merge branch 'linhua55-master' 2015-09-12 22:45:32 +08:00
Yen Chi Hsuan
94e507aea7 [tudou] A more comprehensive _VALID_URL 2015-09-12 22:45:09 +08:00
Sergey M․
3ebc121293 Merge branch 'ndr' of https://github.com/remitamine/youtube-dl into remitamine-ndr 2015-09-12 20:44:31 +06:00
Yen Chi Hsuan
41ebd6530b [tudou] Add the test case (#6273) 2015-09-12 22:42:57 +08:00
Yen Chi Hsuan
2ec7b7b79b Merge branch 'master' of https://github.com/linhua55/youtube-dl into linhua55-master 2015-09-12 22:41:51 +08:00
Sergey M․
60ed60353b [openfilm] Remove extractor
OpenFilm has been shut down
2015-09-12 20:34:48 +06:00
Sergey M․
586f1cc532 [extractor/common] Skip html comment tags (Closes #6822) 2015-09-11 21:07:32 +06:00
Sergey M․
73eb13dfc7 [extractor/common] Case insensitive inputs extraction 2015-09-11 20:43:05 +06:00
remitamine
ed1269000f [brightcove] add support for brightcove in page embed(fixes #6824) 2015-09-11 04:46:21 +01:00
remitamine
689fb748ee [utlis] add extract_attributes for extracting html tags attributes 2015-09-11 04:44:17 +01:00
Sergey M․
1721fef28b [yahoo] Fix test 2015-09-10 02:58:40 +06:00
Sergey M․
364ca0582e [yahoo] Process query result embedded in webpage 2015-09-10 02:49:33 +06:00
Sergey M․
133a2b4ac2 [downloader/hls] Properly close stream 2015-09-10 02:41:30 +06:00
Philipp Hagemeister
d85187eb74 release 2015.09.09 2015-09-09 21:20:01 +02:00
Sergey M․
cc1ac11017 [vier:videos] Fix extraction with old approach (Closes #6806) 2015-09-09 23:59:17 +06:00
Sergey M․
73f536439e Credit @reddraggone9 for fixing youtube 2-factor authentication (#6553) 2015-09-09 23:49:36 +06:00
Sergey M․
b17e7d9a9b Credit @anssih for missing metadata extraction on youtube (#5993) 2015-09-09 23:44:49 +06:00
Sergey M․
2f29b758e0 Credit @lalinsky for vidme improvement (#6812) 2015-09-09 23:41:44 +06:00
Sergey M․
482aa3fecc [vidme] Simplify, make more robust, extract more metadata and capture errors (Closes #6812) 2015-09-09 22:04:05 +06:00
Lukáš Lalinský
d9c19db340 Extract all vid.me formats 2015-09-09 22:03:53 +06:00
Sergey M.
6c4d243de5 Merge pull request #6804 from remitamine/xuite-tests
[xuite] fix test md5
2015-09-09 20:48:52 +06:00
Sergey M.
d1561ef777 Merge pull request #6805 from remitamine/washingtonpost-tests
[washingtonpost] fix test info
2015-09-09 20:48:01 +06:00
remitamine
7b4137c351 [fktv] fix info extraction 2015-09-09 10:42:47 +01:00
remitamine
1072336249 [washingtonpost] fix test info 2015-09-08 20:40:23 +01:00
remitamine
75bb5c7028 [xuite] fix test md5 2015-09-08 20:12:30 +01:00
remitamine
376e1ad081 [ndr] use utils.qualites 2015-09-08 15:58:32 +01:00
remitamine
b58a22b963 [ndr] make NDREmbed and NJoyEmbed _VALID_URL regex unique 2015-09-08 15:37:20 +01:00
Sergey M․
47004d9579 [crunchyroll] Fix uploader and upload date extraction 2015-09-08 14:37:53 +06:00
Sergey M․
12810c9cd3 [crunchyroll] Force Accept-Language to any for all requests (Closes #6797) 2015-09-08 14:11:20 +06:00
Sergey M․
7a459170fa [__init__] Remove musicvault 2015-09-08 05:40:00 +06:00
Sergey M.
3cf0df568a Merge pull request #6791 from remitamine/musicvault
[musicvault] remove extractor
2015-09-08 05:37:24 +06:00
remitamine
b88ebd472e [musicvault] remove extractor 2015-09-07 21:25:32 +01:00
remitamine
64997815c4 [ndr] add support for embeds 2015-09-07 20:53:37 +01:00
Sergey M.
3ecc527209 Merge pull request #6786 from remitamine/wimp-tests
[wimp] fix tests info
2015-09-07 20:39:24 +06:00
remitamine
b1b7d1ffba [wimp] fix tests info 2015-09-07 12:49:59 +01:00
Jaime Marquínez Ferrándiz
4003bd82b0 [youtube:shows] Use https 2015-09-07 12:56:16 +02:00
remitamine
8801255d7d [youtube] fix show extraction (fixes #4841)
Closes #6782)
2015-09-07 12:53:52 +02:00
remitamine
3b18f539a7 [ndr] fix info extraction 2015-09-07 11:18:21 +01:00
remitamine
c67a055d16 [test/test_write_annotations] fix test filename
Closes #6781
2015-09-07 11:18:55 +02:00
Yen Chi Hsuan
bc973e06d0 Merge pull request #6783 from remitamine/yahoo-tests
[yahoo] change test md5 checksum
2015-09-07 16:24:08 +08:00
Yen Chi Hsuan
aeb3c8a0e8 Merge pull request #6784 from remitamine/aljazeera
[ajazzera] skip test
2015-09-07 16:14:35 +08:00
remitamine
cf33a47df0 [ajazzera] skip test 2015-09-07 08:22:38 +01:00
remitamine
daeb0f04cd [yahoo] change test md5 checksum 2015-09-07 08:09:34 +01:00
Sergey M․
97243fe395 [ceskatelevize] Extract playlists (Closes #6772) 2015-09-07 04:52:26 +06:00
Sergey M․
9dbdb65abe [techtvmit] Update test 2015-09-06 10:32:42 +06:00
Sergey M․
9af461de35 [techtvmit] Fix extraction 2015-09-06 10:28:40 +06:00
Sergey M.
4d71e200c6 Merge pull request #6774 from remitamine/academicearth
[academicearth] fix test info
2015-09-06 10:22:08 +06:00
Sergey M.
8e0bdabed2 Merge pull request #6773 from remitamine/airmozilla
[airmozilla] fix test info
2015-09-06 10:21:47 +06:00
Sergey M․
bca553caac [eagleplatform] Fix extraction 2015-09-06 10:21:15 +06:00
remitamine
a2f42a3baf [academicearth] fix test info 2015-09-06 04:52:27 +01:00
remitamine
7465222a9c [airmozilla] fix test info 2015-09-06 04:45:13 +01:00
Sergey M․
e28034c5ac [utils] Comment cookie processing until result from travis and some more testing 2015-09-06 08:16:39 +06:00
Sergey M․
12bbd32ad0 [kuwo:singer] Skip test 2015-09-06 08:00:16 +06:00
Sergey M․
266e466ee4 [utils] Simplify cookie processor 2015-09-06 07:53:11 +06:00
Sergey M․
cf83f532ae [niconico] Use encode_dict 2015-09-06 07:43:05 +06:00
Sergey M․
cd019668dc [niconico] Use encode_dict 2015-09-06 07:42:45 +06:00
Sergey M․
515fc8776f [youtube] Use encode_dict 2015-09-06 07:42:15 +06:00
Sergey M․
c7c0996d8c [gorillavid] Add test for filehoot.com (Closes #6771) 2015-09-06 07:32:06 +06:00
Sergey M․
b3e64671cc [gorillavid] Improve title 2015-09-06 07:30:16 +06:00
Sergey M․
4abe214499 [gorillavid] Encode fields 2015-09-06 07:30:02 +06:00
Sergey M․
e94cb5ae7e [gorillavid] Add support for filehoot.com 2015-09-06 07:24:47 +06:00
Sergey M․
e213c98df1 [gorillavid] Build correct URL 2015-09-06 07:23:53 +06:00
Sergey M․
1639282434 [utils] Add encode_dict 2015-09-06 07:22:20 +06:00
Sergey M․
be0e5dbd83 [extractor/common] Extract submit inputs 2015-09-06 07:20:47 +06:00
Sergey M․
ad72917274 [utils] Add issue URL in comment for #6457 2015-09-06 06:23:44 +06:00
Sergey M․
6a3f4c3f82 [YoutubeDL] Use custom cookie processor 2015-09-06 06:21:33 +06:00
Sergey M․
a6420bf50c [utils] Add cookie processor for cookie correction (Closes #6769) 2015-09-06 06:20:48 +06:00
Sergey M․
eb387896e9 [adultswim] Fix extraction on python 2.6 2015-09-06 02:24:14 +06:00
remitamine
f43c163158 [nowness] pass cinematique 2015-09-05 21:18:21 +01:00
remitamine
673bf566fc [nowness] more api abstraction 2015-09-05 21:03:13 +01:00
remitamine
f95c5e1218 [nowness] change id variable name 2015-09-05 20:51:27 +01:00
remitamine
f33f32f159 [nowness] add api abstration function adn extend _VALID_URL regex 2015-09-05 20:32:31 +01:00
Sergey M․
8df5ae15d1 [compat] Fix python version check for compat_shlex_split 2015-09-05 22:21:06 +06:00
remitamine
75b399f455 [nowness] fix _VALID_URL regex 2015-09-05 16:53:17 +01:00
felix
12439dd5ec [generic] Capture <audio> tags in addition to <video> tags 2015-09-05 21:53:10 +06:00
Sergey M․
3513d41436 [test_compat] Fix typo 2015-09-05 21:45:52 +06:00
Sergey M․
cab792abe5 [options] Use compat_shlex_split 2015-09-05 21:42:41 +06:00
Sergey M․
8870358b1b [__init__] Use compat_shlex_split 2015-09-05 21:42:22 +06:00
Sergey M․
ee087c79ad [test_compat] Add test for compat_shlex_split 2015-09-05 21:41:34 +06:00
Sergey M․
51f579b635 [compat] Add compat shlex.split 2015-09-05 21:40:44 +06:00
remitamine
c23c3d7d7d [nowness] fix video extraction and add support serie and playlist extraction (fixes #6720) 2015-09-05 16:38:05 +01:00
Sergey M․
4abf617b9c [YoutubeDL] Ensure bool params always present in downloader 2015-09-05 19:17:30 +06:00
Jaime Marquínez Ferrándiz
3026164b16 [postprocessor/common] Use 'self._downloader.params' instead of 'self.params'
'self.params' is not defined
2015-09-05 12:11:36 +02:00
Yen Chi Hsuan
9dd73ef4a4 [dcn] PEP8 2015-09-05 15:38:05 +08:00
Yen Chi Hsuan
75c72a1e67 Merge branch 'ping-vlive' 2015-09-05 15:35:19 +08:00
Yen Chi Hsuan
08354db47b [vlive] PEP8 2015-09-05 15:25:19 +08:00
Yen Chi Hsuan
027eb5a6b0 Merge branch 'vlive' of https://github.com/ping/youtube-dl into ping-vlive 2015-09-05 15:24:09 +08:00
Sergey M․
f71264490c [test_utils] Add tests for cli option converters 2015-09-05 03:07:19 +06:00
Sergey M․
6270239a6d [postprocessor/common] Use generalized cli option converters 2015-09-05 03:06:53 +06:00
Sergey M․
1195a38f46 [downloader/external] Use generalized cli option converters 2015-09-05 03:06:28 +06:00
Sergey M․
66e289bab4 [utils] Generalize cli option converters 2015-09-05 03:05:11 +06:00
Sergey M.
52c6f26cab Merge pull request #6755 from remitamine/external_downloader_options
[downloader/external] Respect --no-check-certificate for curl and aria2c and --proxy for curl
2015-09-05 02:19:41 +06:00
remitamine
dc534b674f [downloader/external] change _argless_option function to _valueless_option 2015-09-04 21:12:13 +01:00
remitamine
f30c2e8e98 [downloader/external] add _argless_option for option without arguments 2015-09-04 20:57:19 +01:00
Jaime Marquínez Ferrándiz
c482b3c69a [ruutu] Update test url 2015-09-04 21:11:29 +02:00
remitamine
266b0ad676 [downloader/external] add _bool_option to pass value to bool option 2015-09-04 20:07:36 +01:00
Sergey M․
87f70ab39d [test_utils] Add more tests for xpath 2015-09-05 00:36:16 +06:00
Sergey M․
8e636da499 [utils] Improve xpath_text 2015-09-05 00:34:49 +06:00
Sergey M․
22889ab175 [ruutu] Fix extraction (Closes #6759) 2015-09-05 00:01:40 +06:00
Sergey M․
5d2354f177 [utils] Relax attribute key assert 2015-09-04 23:57:27 +06:00
Sergey M․
a41fb80ce1 [utils] Add xpath_element and xpath_attr 2015-09-04 23:56:45 +06:00
Sergey M.
2e2575e213 Merge pull request #6760 from ferama/master
Added ec extension to nowvideo extractor
2015-09-04 21:49:25 +06:00
Marco Ferragina
26c61e0809 Added ec extension to nowvideo extractor 2015-09-04 12:59:13 +02:00
remitamine
e7a8c3032d [downloader/external] Respect --no-check-certificate for curl and aria2c and --proxy for curl 2015-09-03 22:25:33 +01:00
Jaime Marquínez Ferrándiz
725d1c58aa [crunchyroll] Extract width and height (closes #6749) 2015-09-03 13:15:02 +02:00
Philipp Hagemeister
bd6742137f release 2015.09.03 2015-09-03 12:34:20 +02:00
Sergey M․
e8dcfa3d69 [ok] Sort formats 2015-09-02 22:09:33 +06:00
Sergey M․
88720ed09b [ok] Add support for youtube embeds 2015-09-02 22:08:50 +06:00
Sergey M․
1e804244d0 [ok] Improve player regex 2015-09-02 21:38:56 +06:00
Sergey M․
198492bbf0 [ok] Extract correct data-options (Closes #6726) 2015-09-02 21:36:22 +06:00
Sergey M.
8f9d522f62 [README.md] Remove superfluous word 2015-09-02 21:09:31 +06:00
Sergey M.
cbae233aba Merge pull request #6736 from remitamine/readme
[README] link to developer instructions and correct common.py path
2015-09-02 21:08:22 +06:00
Sergey M.
b17ca9c945 Merge pull request #6731 from dstftw/strict-http-response
[utils] Strict HTTP responses (Closes #6727)
2015-09-02 20:22:59 +06:00
remitamine
ebf4ca39ba link to developer instructions and correct common.py path 2015-09-02 01:24:59 +01:00
Sergey M․
e5e78797e6 [utils] Strict HTTP responses (Closes #6727) 2015-09-02 02:16:04 +06:00
Jaime Marquínez Ferrándiz
080997b808 [rtve] Fix extraction (fixes #6723)
Adding the 'Referer' header to the png request is enough.
2015-09-01 20:30:58 +02:00
Sergey M․
77306e8b97 [francetv] Update tests (2) 2015-09-01 22:42:43 +06:00
Sergey M․
6917d2a2f0 [francetv] Update tests 2015-09-01 22:39:26 +06:00
Sergey M․
36c15522c1 [francetv] Use subtitle when present (Closes #6715) 2015-09-01 22:37:42 +06:00
Sergey M․
804c343a4f [options] Clarify extractors supporting videopassword 2015-09-01 22:30:28 +06:00
Sergey M․
cd5d75427e [youku] Fix videopassword test 2015-09-01 22:28:03 +06:00
Sergey M․
5ddc127da6 [youku] Simplify (Closes #6717) 2015-09-01 22:26:17 +06:00
PSJay
f859695b49 Update README
Add Youku in video password description.
2015-09-01 22:25:10 +06:00
PSJay
cb3d2eb9e9 Fix coding styple in YouKuIE. 2015-09-01 22:25:03 +06:00
PSJay
33eae08f04 Support video-password on Youku. 2015-09-01 22:24:57 +06:00
Jaime Marquínez Ferrándiz
aa3f98677d [dumpert] Support 'embed/' urls (fixes #6712) 2015-09-01 18:13:33 +02:00
Sergey M․
fffccaaf41 [globo] Fix extraction and make more robust (Closes #6728) 2015-09-01 20:41:52 +06:00
Yen Chi Hsuan
cdc8d0c373 [odnoklassniki] Fix extraction and extend _VALID_URL (closes #6726) 2015-09-01 16:34:05 +08:00
ping
d14f0c45fc [vlive] Add info about www. to m. redirects for mobile 2015-09-01 11:11:14 +08:00
Sergey M․
39955b0451 [fc2] Fix refer 2015-08-31 21:12:21 +06:00
Sergey M․
52dfb7ffe2 [fc2] Add test for #3171 2015-08-31 21:05:37 +06:00
Sergey M․
93462856e1 [fc2] Relax _VALID_URL (Closes #3171) 2015-08-31 21:05:23 +06:00
ping
615f155a3a [vlive] Fixes for review 2015-08-31 12:46:28 +08:00
Sergey M․
fcd9e423ec [hls] Properly encode fragment filename 2015-08-31 02:28:36 +06:00
Sergey M․
db8f2bfd99 [f4m] Add missing imports 2015-08-31 02:20:29 +06:00
Sergey M.
55801fc76e Merge pull request #5588 from aajanki/encode_frag_filenames
[f4m] Fix encode error by sanitizing fragment filenames
2015-08-31 02:18:15 +06:00
Sergey M․
d3d89c3256 [YoutubeDL] Encode thumbnail filename (Closes #6714) 2015-08-31 02:01:13 +06:00
Sergey M․
8875b3d572 [imgut:album] Add extractor (Closes #6711) 2015-08-30 19:03:21 +06:00
Sergey M․
aabc2be693 [youtube] Simplify and extract more metadata from url_encoded_fmt_stream_map (Closes #5993) 2015-08-30 08:07:07 +06:00
Anssi Hannula
c9afb51cea [youtube] Fix missing format details for 60fps DASH formats
60fps DASH formats do not appear in the DASH manifest, but the non-DASH
video info page does contain additional parameters for DASH formats that
we can parse.

Use those when they exist.

This fixes "bestvideo" not selecting 60fps formats over similar 30fps
formats just because the file size is unknown.
2015-08-30 08:05:16 +06:00
Yen Chi Hsuan
c0a656876c [abc] Use int_or_none 2015-08-30 01:11:56 +08:00
Yen Chi Hsuan
17a647630b [abc] Support YouTube embeds (fixes #6595) 2015-08-30 01:08:55 +08:00
Sergey M․
c88e118b3c [nowtv] Fix display_id (Closes #6702) 2015-08-29 22:41:49 +06:00
Sergey M․
ae6a802106 [kaltura] Fix _VALID_URL formatting 2015-08-29 20:26:51 +06:00
Sergey M․
b184f94413 [kaltura] Clarify html5 player comment 2015-08-29 20:23:06 +06:00
Sergey M․
ee3ec091f4 [kaltura] Strictify _VALID_URL 2015-08-29 20:21:59 +06:00
sourcerect
ef49b59053 [kaltura] add html5 player urls 2015-08-29 19:29:07 +06:00
Sergey M․
1f8125805e [generic] Add test for screenwavemedia embed 2015-08-29 19:12:38 +06:00
Sergey M․
efd712c69b [generic] Use screenwavemedia embed pattern 2015-08-29 19:07:31 +06:00
Sergey M․
109a4156e1 [cinemassacre] Use screenwavemedia embed pattern 2015-08-29 19:06:55 +06:00
Sergey M․
678d33295b [screenwavemedia] Improve _VALID_URL 2015-08-29 19:05:11 +06:00
Sergey M․
5e58956d0a [screenwavemedia] Add EMBED_PATTERN 2015-08-29 19:00:38 +06:00
felix
e276fd2cb3 [screenwavemedia] warn when falling back to a hardcoded list of streams 2015-08-29 18:38:41 +06:00
felix
9b22cb10c4 [screenwavemedia] use the true JS value instead of a comment 2015-08-29 18:38:34 +06:00
felix
8ca31a0e05 [generic] Look for ScreenwaveMedia embeds 2015-08-29 18:38:23 +06:00
Sergey M․
20149a5da1 [mtvservices] Fix title extraction under python 2 2015-08-28 22:10:49 +06:00
Sergey M․
054d43bb11 [mtvservices] Catch more georestriction errors 2015-08-28 21:45:29 +06:00
Sergey M․
65488b820c [mtvde] Add support for news 2015-08-28 21:41:54 +06:00
Sergey M․
c3c9f87954 [mtvde] Add test for mediagen URL without query 2015-08-28 21:31:38 +06:00
Sergey M․
56f447be9f [mtvservices] Fix mediagen URL generation 2015-08-28 21:27:36 +06:00
Sergey M․
79fa9db0da [mtvde] Simplify (Closes #6673) 2015-08-28 21:24:54 +06:00
Paul Hartmann
071c10137b [MTV] move German mtv site to new class 2015-08-28 21:23:00 +06:00
Philipp Hagemeister
a4962b80d6 release 2015.08.28 2015-08-28 05:04:39 +02:00
Sergey M․
5307c33232 [foxnews] Clarify IE_DESC 2015-08-27 21:48:47 +06:00
Sergey M․
1b660cce12 [foxnews] Simplify (Closes #6694) 2015-08-27 21:48:03 +06:00
nmrugg
8df8c278b6 Added matching test for FoxBusiness. 2015-08-27 21:41:16 +06:00
nmrugg
d7e8264517 Make FoxBusiness work. 2015-08-27 21:41:08 +06:00
Sergey M․
f11c316347 [shared] Add filesize to test 2015-08-26 22:06:10 +06:00
Sergey M․
f62e02c24f [shared] Add test for vivo 2015-08-26 22:05:45 +06:00
Sergey M․
70113c38c9 [shared] Clarify IE_DESC 2015-08-26 22:04:39 +06:00
Sergey M․
3d8132f5e2 [shared] Extend _VALID_URL to support vivo.sx (Closes #6681) 2015-08-26 22:03:58 +06:00
Sergey M․
39affb5aa4 [crunchyroll] Fix typo 2015-08-26 21:27:57 +06:00
Sergey M․
a882c5f474 [udemy] Use raise_login_required 2015-08-26 21:27:07 +06:00
Sergey M․
61a7ff1622 [tubitv] Use raise_login_required 2015-08-26 21:26:47 +06:00
Sergey M․
42e7373bd3 [smotri] Use raise_login_required 2015-08-26 21:26:35 +06:00
Sergey M․
e269d3ae7d [safari] Use raise_login_required 2015-08-26 21:26:24 +06:00
Sergey M․
e7ddaef5bd [pluralsight] Use raise_login_required 2015-08-26 21:26:09 +06:00
Sergey M․
62984e4584 [lynda] Use raise_login_required 2015-08-26 21:25:53 +06:00
Sergey M․
3c53455d15 [eroprofile] Use raise_login_required 2015-08-26 21:25:37 +06:00
Sergey M․
bbb43a39fd [crunchyroll] Use raise_login_required 2015-08-26 21:25:04 +06:00
Sergey M․
43e7d3c945 [extractor/common] Add raise_login_required 2015-08-26 21:24:47 +06:00
Sergey M․
2f72e83bbd [crunchyroll] Detect required login (#6677) 2015-08-26 20:47:57 +06:00
Sergey M.
57179b4ca1 Merge pull request #6679 from jaimeMF/youtube-fix-player-version-regex
[youtube] Adapt player version regex to handle urls ending in '/html5player-new.js'
2015-08-26 20:17:52 +06:00
Jaime Marquínez Ferrándiz
4bc8eec4eb [youtube] Adapt player version regex to handle urls ending in '/html5player-new.js'
It was always extracting 'new' as the version, breaking the cache system.
2015-08-26 15:21:55 +02:00
Sergey M․
baf510bf8c [yandexmusic:playlist] Handle playlists with more than 150 tracks (Closes #6666) 2015-08-26 00:11:15 +06:00
Sergey M․
6d53cdd6ce [yandexmusic] Skip removed tracks (#6666) 2015-08-25 23:29:02 +06:00
Sergey M․
ebbf078c7d [krasview] Skip download for test 2015-08-25 21:19:21 +06:00
Sergey M․
95e431e9ec [mailru] Skip tests 2015-08-25 21:08:38 +06:00
ping
eba470f2f2 [vlive] Remove upload_date extraction & cleanup 2015-08-24 16:30:00 +08:00
Philipp Hagemeister
11addc50ff release 2015.08.23 2015-08-23 23:52:47 +02:00
Sergey M․
e4df2f98cc [yandexmusic:track] Eliminate base class 2015-08-24 00:36:54 +06:00
Sergey M․
e7c14660d3 [yandexmusic] Defer link resolve till actual download time to prevent link expiry (Closes #6650) 2015-08-24 00:36:24 +06:00
Sergey M․
90076b6172 [spankwire] Preserve old uploader pattern 2015-08-23 22:33:26 +06:00
Sergey M․
28b83495d8 [spankwire] Simplify 2015-08-23 22:32:44 +06:00
Sergey M․
551c7837ac [spankwire] Simplify and properly format 2015-08-23 22:32:20 +06:00
clauderains
59e6acc757 [spankwire] Support new cdn video url format 2015-08-23 22:21:59 +06:00
clauderains
9990c960f2 [spankwire] Fixed uploader_id field extraction so that test case passes 2015-08-23 22:21:54 +06:00
Sergey M․
2006a06eff [kontrtube] Fix extraction (Closes #6644) 2015-08-23 21:43:28 +06:00
Sergey M․
2b6bda1ed8 [pluralsight] Do not yet rely on userMayViewClip 2015-08-23 11:21:56 +06:00
Sergey M․
468083d2f5 [pluralsight] Remove unused const 2015-08-23 10:44:10 +06:00
Sergey M․
483fc223bb [pluralsight] Add extractor (Closes #6090) 2015-08-23 10:42:34 +06:00
Sergey M․
66ce97024d [soundcloud:user] Update tests 2015-08-22 06:30:00 +06:00
Jaime Marquínez Ferrándiz
8c97f81943 [common] Follow convention of using 'cls' in classmethods 2015-08-21 11:35:51 +02:00
Yen Chi Hsuan
d7c1630570 [rtl2] Remove MD5 checksums 2015-08-21 13:21:21 +08:00
Yen Chi Hsuan
5e1a5ac8de [rtl2] Fix extraction for test_RTL2_1 2015-08-21 13:20:51 +08:00
Yen Chi Hsuan
9eb4ab6ad9 [rtl2] Remove an unused line 2015-08-21 13:04:25 +08:00
Yen Chi Hsuan
4932a817a0 [rtl2] Add skip_download for test 2015-08-21 13:00:08 +08:00
Sergey M․
5d003e29b1 [rtp] Add skip_download for test 2015-08-21 08:56:05 +06:00
Sergey M․
dc95bd503e [folketinget] Add skip_download for test 2015-08-21 08:54:28 +06:00
Yen Chi Hsuan
f738dd7b7c [common] Remove debugging codes 2015-08-21 01:43:22 +08:00
Yen Chi Hsuan
f908b74fa3 [test/subtitles] Add test for ThePlatformFeedIE 2015-08-21 01:38:57 +08:00
Yen Chi Hsuan
c687ac745b [theplatform] Use subtitles from SMIL, too 2015-08-21 01:37:43 +08:00
Yen Chi Hsuan
912e0b7e46 [common] Add _merge_subtitles() 2015-08-21 01:37:07 +08:00
Yen Chi Hsuan
03bc7237ad [common] _parse_smil_subtitles: accept lang as the subtitle language 2015-08-20 23:18:58 +08:00
ping
061f62da54 [vlive] New extractor for vlive.tv 2015-08-20 12:56:11 +08:00
Yen Chi Hsuan
dd565ac1ad [theplatform] Use _download_json 2015-08-20 03:07:04 +08:00
Sergey M․
5cdefc4625 [extractor/common] Add more subtitle mime types for guess when ext is missing 2015-08-20 01:02:50 +06:00
Sergey M․
ce00af8767 [extractor/common] Add default subtitles lang 2015-08-20 00:56:17 +06:00
Yen Chi Hsuan
51047444aa Merge branch 'master' into HEAD 2015-08-20 01:56:08 +08:00
Yen Chi Hsuan
aa6cd05ed8 [theplatform] Fix Python 2: declare coding 2015-08-20 01:47:55 +08:00
Yen Chi Hsuan
dac14bf311 [nbc] Add MSNBCIE 2015-08-20 01:41:18 +08:00
Yen Chi Hsuan
05fe2594e4 [theplatform] Support URLs with 'guid=' 2015-08-20 01:38:39 +08:00
Yen Chi Hsuan
26e1c3514f [theplatform] Add ThePlatformFeedIE 2015-08-20 01:24:32 +08:00
Sergey M․
22c83245c5 [mwave] Improve 2015-08-19 23:07:41 +06:00
ping
7900aede14 [mwave] New extractor for mwave.interest.me 2015-08-19 22:40:40 +06:00
Yen Chi Hsuan
f877c6ae5a [theplatform] Use InfoExtractor._parse_smil_formats() 2015-08-19 23:11:25 +08:00
Sergey M․
ca681f7041 [videobam] Remove extractor
videobam.com redirects to sendvid.com now
2015-08-19 20:52:36 +06:00
Sergey M․
a01da8bbf8 [crunchyroll] Workaround fplive.net rtmp URLs (Closes #5881) 2015-08-18 23:02:57 +06:00
Sergey M․
f3a65d9636 [travis] Move to new infrastructure
We don't use rtmpdump in tests anyway
2015-08-18 21:10:52 +06:00
Sergey M․
559f4c550f [playtvak] PEP 8 2015-08-18 20:27:58 +06:00
Sergey M․
03c635a4b5 [twitch] Fix login (Closes #6599) 2015-08-18 20:26:45 +06:00
Sergey M․
34a4cd0a34 [telecinco] Relax _VALID_URL (Closes #6601) 2015-08-18 20:02:56 +06:00
Philipp Hagemeister
3b9b32f404 [libsyn] Strip options from player URL 2015-08-18 13:02:41 +02:00
Sergey M․
9c724a9802 [twitch:vod] Add test for #6585 2015-08-17 20:23:52 +06:00
Sergey M․
7a6e8a1b17 [twitch] Make more robust 2015-08-17 20:20:04 +06:00
Sergey M․
369c12e038 [twitch] Allow untitled videos (Closes #6585) 2015-08-17 20:16:43 +06:00
Philipp Hagemeister
0fa5795b85 release 2015.08.16.1 2015-08-16 23:40:07 +02:00
Philipp Hagemeister
c00c7c0af0 [sportdeutschland] Fix extraction 2015-08-16 23:39:50 +02:00
Sergey M․
cbaed4bb5e [youtube] Expand _VALID_URL to support vid.plus 2015-08-17 02:04:13 +06:00
Sergey M․
f74a7348f6 [youtube:search_url] Fix extraction (Closes #6578) 2015-08-16 23:33:17 +06:00
Sergey M․
8626b23e4e [screenwavemedia] Make more robust 2015-08-16 20:18:15 +06:00
Sergey M․
0086874277 [playtvak] Use tuples 2015-08-16 19:59:03 +06:00
Sergey M․
7fc18d9309 [screenwavemedia] Fix extraction (Closes #6575) 2015-08-16 19:53:14 +06:00
Sergey M․
974f1a385a [playtvak] Improve description extraction and add test for metro 2015-08-16 17:22:13 +06:00
Philipp Hagemeister
6900b4f6f5 release 2015.08.16 2015-08-16 01:05:04 +02:00
Sergey M․
d90e4bdb74 Merge branch 'oskar456-playtvak' 2015-08-16 03:07:37 +06:00
Sergey M․
276c989772 [playtvak] Improve and simplify 2015-08-16 03:07:05 +06:00
Sergey M․
ea99110d24 Merge branch 'playtvak' of https://github.com/oskar456/youtube-dl into oskar456-playtvak 2015-08-16 01:06:38 +06:00
Ondřej Caletka
221a59fe6f [playtvak] Initial support for videos from Mafra group servers
Support for videos in articles from idnes.cz, lidovky.cz, metro.cz, as
well as video and live streams from playtvak.cz

Signed-off-by: Ondřej Caletka <ondrej@caletka.cz>
2015-08-15 20:59:40 +02:00
Sergey M.
eaa5646483 [README.md] Clarify configuration file usage (Closes #6530) 2015-08-15 22:57:06 +06:00
Sergey M․
041bc3adc5 [youtube] Simplify two-factor authentication 2015-08-15 22:10:38 +06:00
Sergey M․
e64b756943 [extractor/common] Interactive TFA code input 2015-08-15 21:55:07 +06:00
Sergey M․
201ea3ee8e [extractor/common] Improve _hidden_inputs 2015-08-15 21:52:22 +06:00
reddraggone9
9303ce3e69 [youtube] Fix two-factor authentication 2015-08-15 21:48:44 +06:00
Sergey M․
06c085ab6e Merge branch 'remitamine-shahid' 2015-08-15 18:13:53 +06:00
Sergey M․
c576ef1e7c [shahid] Improve and simplify 2015-08-15 18:13:37 +06:00
Sergey M․
11bed5827d Merge branch 'shahid' of https://github.com/remitamine/youtube-dl into remitamine-shahid 2015-08-15 16:32:06 +06:00
Sergey M․
fab83e2456 Credit @scw for video.esri.com (#5459) 2015-08-15 16:10:20 +06:00
Sergey M․
1d25e9d173 [extractor/__init__] Fix order 2015-08-15 16:00:24 +06:00
Sergey M․
9c21f22923 [esri:video] Rename extractor 2015-08-15 15:59:35 +06:00
Sergey M․
3aa697f993 [esri:video] Extract all formats and simplify 2015-08-15 15:58:56 +06:00
Sergey M․
8b9848ac56 [extractor/common] Expand meta regex 2015-08-15 15:58:30 +06:00
Shaun Walbridge
8b8c1093b6 [EsriVideo] Add new extractor
Add extractor for [videos.esri.com](https://videos.esri.com), a collection
of videos relating to GIS.
2015-08-15 15:57:59 +06:00
Yen Chi Hsuan
d0d6c097fc [moniker] Support embed- URLs (#6450) 2015-08-15 15:17:27 +08:00
Sergey M.
6be5e46994 [README.md] Clarify line ranges 2015-08-14 22:22:39 +06:00
Sergey M.
45694b504a Merge pull request #6554 from reddraggone9/readme-line-numbers
Updated line numbers in the fragment portion of README links.
2015-08-14 22:15:13 +06:00
Sergey M․
41dbc50f9c [lynda] Capture and output login error (Closes #6556) 2015-08-14 22:07:02 +06:00
Yen Chi Hsuan
4d2ad866f3 [README.md] Document format_id field in output template section (#6557) 2015-08-14 19:18:03 +08:00
reddraggone9
3cafca04aa Updated line numbers in the fragment portion of README links. 2015-08-14 00:35:35 -05:00
Sergey M․
594f51b859 [indavideo] Remove unused import 2015-08-13 23:47:49 +06:00
Sergey M․
fb56131dd9 Credit @nyuszika7h for indavideo (#6517) 2015-08-13 23:47:12 +06:00
Sergey M․
a34e19629c [indavideo] Relax _VALID_URL to match subdomains and add tests 2015-08-13 23:40:20 +06:00
Sergey M․
3c12a027d4 [indavideo] Split in two extractors, extract all formats and fix timestamp 2015-08-13 23:25:47 +06:00
nyuszika7h
cb28e03386 [indavideo] Add new extractor
Closes #2147.
2015-08-13 22:19:22 +06:00
Sergey M․
7393746da2 [downloader/hls] Add _debug_cmd 2015-08-13 21:10:11 +06:00
Sergey M․
6828c809e4 [downloader/fragment] Respect --retries for fragment based downloaders (Closes #6549) 2015-08-13 21:07:14 +06:00
Yen Chi Hsuan
28479149cc [theplatform] Fallback to hardcoded releaseUrl if not available
Fixes #6546.

Not adding a test case as test_NBC has the same problem.
2015-08-13 12:56:12 +08:00
Sergey M․
237c03c8ea [dhm] Remove unused import 2015-08-13 01:19:23 +06:00
Sergey M․
e73c85cb23 [iqiyi] PEP 8 2015-08-13 01:18:49 +06:00
Sergey M․
b6b2711298 [tweakers] Remove unused imports 2015-08-13 01:17:15 +06:00
Sergey M․
3b7130439a Credit @ngld for RTVNH (#6537) 2015-08-13 01:15:58 +06:00
Sergey M․
2c919adb74 [rtvnh] Check status code 2015-08-13 01:11:55 +06:00
Sergey M․
60231c65b9 [rtvnh] Make SMIL not fatal 2015-08-13 01:02:50 +06:00
Sergey M․
f196047832 [rtvnh] Make thumbnail optional 2015-08-13 01:00:25 +06:00
Sergey M․
240ca32e57 [rtvnh] Carry long lines 2015-08-13 01:00:05 +06:00
Sergey M.
fa37c26c4d Merge pull request #6537 from ngld/rtvnhnl
[rtvnhnl] Added new extractor
2015-08-13 00:57:00 +06:00
ngld
d7dbfc7cc1 Use native HLS implementation by default. 2015-08-12 20:51:28 +02:00
ngld
d9ab5262b1 [rtvnh] Renamed rtvnhnl -> rtvnh 2015-08-12 20:26:13 +02:00
ngld
fb124e3741 [rtvnhnl] Relax the thumbnail check 2015-08-12 20:21:32 +02:00
Sergey M.
479bf783d2 Merge pull request #6539 from ngld/fix-funnyordie
[funnyordie] Handle protocol-relative URLs (fixes #6490)
2015-08-12 23:23:35 +06:00
ngld
f0f3a6c99d [rtvnhnl] Added new extractor 2015-08-12 18:07:27 +02:00
Sergey M․
f57b7835e2 [youtube] Update tests 2015-08-12 21:27:58 +06:00
ngld
1df3186e0e [funnyordie] Handle protocol-relative URLs (fixes #6490) 2015-08-12 16:01:47 +02:00
Yen Chi Hsuan
0b7c27828d Merge pull request #6533 from sceext2/fix-iqiyi-2015-08-10
[iqiyi] update md5 salt (2015-08-10)
2015-08-12 21:52:21 +08:00
sceext
0a19d4ccd6 [iqiyi] update md5 salt (2015-08-10 Zombie) 2015-08-12 14:01:48 +08:00
Sergey M․
9f3da13860 [downloader/external] Use generic _option 2015-08-11 23:05:04 +06:00
Sergey M․
bf812ef714 [downloader/external] Forward --proxy to wget and aria2c 2015-08-11 23:00:45 +06:00
Sergey M․
b1ac38fadc [tvplay] Add age limit to tests 2015-08-11 01:49:23 +06:00
Sergey M․
fb0d12c6cb [pbs] Add age limit to tests 2015-08-11 01:46:25 +06:00
Sergey M․
34952f09e1 [youtube] Add age limit to tests 2015-08-11 01:24:53 +06:00
Sergey M․
34a7de2970 [youtube] Skip download for multiple v= test 2015-08-11 01:22:06 +06:00
Sergey M.
0ff827419e Merge pull request #6518 from puckipedia/master
[youtube] Use the first v= argument in the URL
2015-08-11 01:18:50 +06:00
Sergey M․
b29440aee6 [vimeo:user] Do not match watchlater 2015-08-11 01:17:41 +06:00
Puck Meerburg
11b5605815 [youtube] Use the first v= argument in the URL
This is according to how youtube handles multiple v= values in one URL.
Before this, it was possible to make a single URL show up differently
 on youtube itself, and if you downloaded/viewed it with youtube-dl/mpv
2015-08-10 20:52:38 +02:00
Sergey M․
844587669e [vimeo:watchlater] Fix extraction (Closes #3886) 2015-08-10 23:58:01 +06:00
Sergey M․
f6c3664d71 [vimeo] Fix login (Closes #6488) 2015-08-10 23:35:08 +06:00
Sergey M․
c5864a8ce6 [fc2] Fix python 2.6 (Closes #6512) 2015-08-10 21:38:58 +06:00
Philipp Hagemeister
27c7114af6 release 2015.08.09 2015-08-09 20:13:02 +02:00
Sergey M․
0791ac1b44 [extractor/generic] Clarify comment 2015-08-09 19:47:58 +06:00
Sergey M․
1de5cd3ba5 [extractor/generic] Add test for xspf playlist 2015-08-09 19:47:08 +06:00
Sergey M․
729accb482 [extractor/generic] Add support for xspf playlists 2015-08-09 19:43:42 +06:00
Sergey M․
942acef594 [extractor/common] Extract _parse_xspf 2015-08-09 19:41:55 +06:00
Sergey M․
fb2f339fec [dhm] Use _extract_xspf_playlist 2015-08-09 19:21:25 +06:00
Sergey M․
98044462b1 [extractor/common] Use playlist id as default title 2015-08-09 19:18:50 +06:00
Sergey M․
0dcb318f62 [tweakers] Fix test 2015-08-09 19:15:20 +06:00
Sergey M․
f32143469f [tweakers] Use _extract_xspf_playlist 2015-08-09 19:15:00 +06:00
Sergey M․
3a30508b94 [telegraaf] Add extractor (Closes #6492) 2015-08-09 19:11:23 +06:00
Sergey M․
e0b9d78fab [extractor/common] Clarify playlists can have description field 2015-08-09 19:09:50 +06:00
Sergey M․
8d6765cf48 [extractor/generic] Add generic support for xspf playist extraction 2015-08-09 19:07:18 +06:00
Sergey M․
12bb392a0f [vimeo] Fix password protected videos (Closes #6507) 2015-08-09 17:10:40 +06:00
Sergey M․
08df685fe7 [videolectures] Fix _VALID_URL for test_no_duplicates to pass 2015-08-09 08:51:37 +06:00
Sergey M․
c8d1be772d [rutube] Add age_limit to test 2015-08-08 22:11:06 +06:00
Sergey M․
887e9bc7b5 [ok] Update tests 2015-08-08 22:08:54 +06:00
Sergey M․
9f2e7c2f34 [ok] Add age_limit to tests 2015-08-08 22:04:48 +06:00
Sergey M․
d7bb8884af [break] Add age_limit to test 2015-08-08 21:58:24 +06:00
Sergey M․
464e792496 [vpro] Override npo IE_NAME 2015-08-08 21:51:21 +06:00
Sergey M․
18c3281f9e [24video] Fix test 2015-08-08 21:43:20 +06:00
Sergey M․
8e2b1be127 [test/helper] Make age_limit checkable field 2015-08-08 21:42:50 +06:00
Sergey M․
b61b7787cb [91porn] Extract age limit 2015-08-08 21:30:57 +06:00
Sergey M․
b465083f45 [sexykarma] Fix test 2015-08-08 21:27:10 +06:00
Sergey M․
154655a85a [downloader/external] Respect --no-check-certificate for wget 2015-08-08 19:21:05 +06:00
remitamine
59e89e62d7 [shahid] add default fallbacks for extracting api vars 2015-08-08 12:59:10 +01:00
Sergey M.
d5d7bdaeb5 Merge pull request #6428 from dstftw/improve-generic-smil-support
Improve generic SMIL support
2015-08-08 05:47:33 +06:00
Sergey M․
b2f82948ee [quickscope] Fix typo 2015-08-08 05:40:41 +06:00
Sergey M․
428e4e4a85 [quickscope] Add extractor 2015-08-08 05:37:38 +06:00
Sergey M․
1e83741c9a [periscope] Add support for running streams 2015-08-08 05:33:53 +06:00
Sergey M․
621d6a9516 [periscope] Switch to API for broadcast data 2015-08-08 04:00:52 +06:00
Sergey M․
3550821fb4 [periscope] Add extractor (Closes #5850, closes #6459) 2015-08-08 03:38:55 +06:00
Sergey M․
5b0c40da24 [extractor/common] Expand meta regex 2015-08-08 03:36:29 +06:00
vijayanand nandam
e0ac521438 adding support for axel download manager 2015-08-08 02:39:05 +06:00
remitamine
c29458f3ec [shahid] change the tests 2015-08-07 21:38:50 +01:00
Sergey M․
bf94d763ba [extractor/generic] Add test for #6485 2015-08-08 02:00:49 +06:00
Sergey M․
8a37aa1517 [extractor/generic] Expand ooyala regex (Closes #6485) 2015-08-08 01:55:59 +06:00
Sergey M․
f3d24df6f2 Merge branch 'remitamine-clipfish' 2015-08-08 01:11:06 +06:00
Sergey M․
fd5d8270dc [clipfish] Fix extraction, minimize requests, get rid of drm hds, extract m3u8 and more metadata 2015-08-08 01:10:41 +06:00
Sergey M․
be612d9e0c Merge branch 'clipfish' of https://github.com/remitamine/youtube-dl into remitamine-clipfish 2015-08-08 00:21:42 +06:00
Sergey M․
4a7434d0b0 [dcn] Simplify _VALID_URL 2015-08-08 00:19:40 +06:00
Sergey M․
ad2141be2d Merge branch 'remitamine-dcn' 2015-08-08 00:06:29 +06:00
Sergey M․
f94639fadf [dcn] Improve 2015-08-08 00:06:03 +06:00
Sergey M․
89faae660f Merge branch 'dcn' of https://github.com/remitamine/youtube-dl into remitamine-dcn 2015-08-07 23:29:33 +06:00
Sergey M․
0f422256d6 [nowtv] Add .at test 2015-08-07 19:51:09 +06:00
Sergey M․
acc1adbe7a [nowtv] Add support for .ch TLD 2015-08-07 19:50:54 +06:00
Sergey M․
8002ac9e0a [nowtv] Add support for .at TLD 2015-08-07 19:04:44 +06:00
remitamine
6d30cf04db [dcn] fix type and key errors 2015-08-07 10:01:18 +01:00
remitamine
3be3c622dc [shahid] generic errors handling and check with flake8 2015-08-06 19:37:45 +01:00
remitamine
cd6b555e19 [dcn] add origin to api request and fix the test and check with flake8 2015-08-06 19:17:50 +01:00
Sergey M․
d41d04c0f5 [videolectures] Fix _VALID_URL 2015-08-02 06:35:35 +06:00
Sergey M․
17712eeb19 [extractor/common] Extract namespace parse routine 2015-08-02 01:31:17 +06:00
Sergey M․
41c3a5a7be [extractor/common] Fix python 3 2015-08-02 01:20:49 +06:00
Sergey M․
8765222d22 [extractor/generic] Add generic SMIL tests 2015-08-02 01:16:21 +06:00
Sergey M․
645f814544 [test/helper] Allow dicts for mincount 2015-08-02 01:15:33 +06:00
Sergey M․
308cfe0ab3 [test_downloader] Respect --force-generic-extractor 2015-08-02 01:14:41 +06:00
Sergey M․
e5e8d20a3a [extractor/generic] Improve generic SMIL detection 2015-08-02 01:13:59 +06:00
Sergey M․
a107193e4b [extractor/common] Extract f4m and m3u8 formats, subtitles and info 2015-08-02 01:13:21 +06:00
remitamine
dfaba1ab95 [shahid] fix variable name 2015-07-25 19:14:18 +01:00
remitamine
a62fd1af27 [shahid] improve info extraction 2015-07-25 18:13:44 +01:00
remitamine
1a117a7728 [clipfish] extract mp4 video link 2015-07-24 12:00:20 +01:00
fnord
9e7e0dffd5 Actually add the extractor 2015-07-21 16:56:35 -05:00
fnord
c3dea3f878 Twittercard: support vmapurl method 2015-07-21 16:45:36 -05:00
fnord
f57f84f606 Twitter: get and describe video from status urls 2015-07-21 16:38:40 -05:00
remitamine
3c07a729a6 [shahid] don't fail on missing info 2015-07-18 23:16:57 +01:00
remitamine
84c0ed50a5 [shahid] fix backets closing 2015-07-18 23:10:20 +01:00
remitamine
02c126a7c2 [shahid] raise ExtractorError instead of warning 2015-07-18 22:55:40 +01:00
remitamine
114ed20e64 [shahid] Add new extractor 2015-07-18 22:10:57 +01:00
remitamine
9d681c2bb3 remove unnecessary group name 2015-07-18 20:40:56 +01:00
remitamine
3af1fac7b0 [dcn] Add new extractor 2015-07-18 20:40:56 +01:00
linhua55
bfed4813b2 fix extraction
for http://www.tudou.com/albumplay/cJAHGih4yYg.html
2015-07-18 23:33:42 +08:00
fnord
eb08081330 democracynow: correct syntax 2015-07-17 02:57:08 -05:00
fnord
f870544302 Add support for democracynow.org
Supports downloading clips or entire shows. Subtitle support
2015-07-13 07:41:38 -05:00
Antti Ajanki
233c1c0e76 [downloader/f4m] Fragment filenames must be sanitized
because the fragment was written to a file with a sanitized name by
http_dl.download()
2015-05-03 11:31:42 +03:00
299 changed files with 11197 additions and 4388 deletions

View File

@@ -5,9 +5,8 @@ python:
- "3.2" - "3.2"
- "3.3" - "3.3"
- "3.4" - "3.4"
before_install: - "3.5"
- sudo apt-get update -qq sudo: false
- sudo apt-get install -yqq rtmpdump
script: nosetests test --verbose script: nosetests test --verbose
notifications: notifications:
email: email:

12
AUTHORS
View File

@@ -137,3 +137,15 @@ Zach Bruggeman
Tjark Saul Tjark Saul
slangangular slangangular
Behrouz Abbasi Behrouz Abbasi
ngld
nyuszika7h
Shaun Walbridge
Lee Jenkins
Anssi Hannula
Lukáš Lalinský
Qijiang Fan
Rémy Léone
Marco Ferragina
reiv
Muratcan Simsek
Evan Lu

View File

@@ -1,6 +1,20 @@
**Please include the full output of youtube-dl when run with `-v`**. **Please include the full output of youtube-dl when run with `-v`**, i.e. add `-v` flag to your command line, copy the **whole** output and post it in the issue body wrapped in \`\`\` for better formatting. It should look similar to this:
```
$ youtube-dl -v http://www.youtube.com/watch?v=BaW_jenozKcj
[debug] System config: []
[debug] User config: []
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
[debug] youtube-dl version 2015.12.06
[debug] Git HEAD: 135392e
[debug] Python version 2.6.6 - Windows-2003Server-5.2.3790-SP2
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
[debug] Proxy map: {}
...
```
**Do not post screenshots of verbose log only plain text is acceptable.**
The output (including the first lines) contain important debugging information. Issues without the full output are often not reproducible and therefore do not get solved in short order, if ever. The output (including the first lines) contains important debugging information. Issues without the full output are often not reproducible and therefore do not get solved in short order, if ever.
Please re-read your issue once again to avoid a couple of common mistakes (you can and should use this as a checklist): Please re-read your issue once again to avoid a couple of common mistakes (you can and should use this as a checklist):
@@ -16,19 +30,19 @@ So please elaborate on what feature you are requesting, or what bug you want to
If your report is shorter than two lines, it is almost certainly missing some of these, which makes it hard for us to respond to it. We're often too polite to close the issue outright, but the missing info makes misinterpretation likely. As a commiter myself, I often get frustrated by these issues, since the only possible way for me to move forward on them is to ask for clarification over and over. If your report is shorter than two lines, it is almost certainly missing some of these, which makes it hard for us to respond to it. We're often too polite to close the issue outright, but the missing info makes misinterpretation likely. As a commiter myself, I often get frustrated by these issues, since the only possible way for me to move forward on them is to ask for clarification over and over.
For bug reports, this means that your report should contain the *complete* output of youtube-dl when called with the -v flag. The error message you get for (most) bugs even says so, but you would not believe how many of our bug reports do not contain this information. For bug reports, this means that your report should contain the *complete* output of youtube-dl when called with the `-v` flag. The error message you get for (most) bugs even says so, but you would not believe how many of our bug reports do not contain this information.
If your server has multiple IPs or you suspect censorship, adding --call-home may be a good idea to get more diagnostics. If the error is `ERROR: Unable to extract ...` and you cannot reproduce it from multiple countries, add `--dump-pages` (warning: this will yield a rather large output, redirect it to the file `log.txt` by adding `>log.txt 2>&1` to your command-line) or upload the `.dump` files you get when you add `--write-pages` [somewhere](https://gist.github.com/). If your server has multiple IPs or you suspect censorship, adding `--call-home` may be a good idea to get more diagnostics. If the error is `ERROR: Unable to extract ...` and you cannot reproduce it from multiple countries, add `--dump-pages` (warning: this will yield a rather large output, redirect it to the file `log.txt` by adding `>log.txt 2>&1` to your command-line) or upload the `.dump` files you get when you add `--write-pages` [somewhere](https://gist.github.com/).
**Site support requests must contain an example URL**. An example URL is a URL you might want to download, like http://www.youtube.com/watch?v=BaW_jenozKc . There should be an obvious video present. Except under very special circumstances, the main page of a video service (e.g. http://www.youtube.com/ ) is *not* an example URL. **Site support requests must contain an example URL**. An example URL is a URL you might want to download, like `http://www.youtube.com/watch?v=BaW_jenozKc`. There should be an obvious video present. Except under very special circumstances, the main page of a video service (e.g. `http://www.youtube.com/`) is *not* an example URL.
### Are you using the latest version? ### Are you using the latest version?
Before reporting any issue, type youtube-dl -U. This should report that you're up-to-date. About 20% of the reports we receive are already fixed, but people are using outdated versions. This goes for feature requests as well. Before reporting any issue, type `youtube-dl -U`. This should report that you're up-to-date. About 20% of the reports we receive are already fixed, but people are using outdated versions. This goes for feature requests as well.
### Is the issue already documented? ### Is the issue already documented?
Make sure that someone has not already opened the issue you're trying to open. Search at the top of the window or at https://github.com/rg3/youtube-dl/search?type=Issues . If there is an issue, feel free to write something along the lines of "This affects me as well, with version 2015.01.01. Here is some more information on the issue: ...". While some issues may be old, a new post into them often spurs rapid activity. Make sure that someone has not already opened the issue you're trying to open. Search at the top of the window or browse the [GitHub Issues](https://github.com/rg3/youtube-dl/search?type=Issues) of this repository. If there is an issue, feel free to write something along the lines of "This affects me as well, with version 2015.01.01. Here is some more information on the issue: ...". While some issues may be old, a new post into them often spurs rapid activity.
### Why are existing options not enough? ### Why are existing options not enough?
@@ -114,18 +128,19 @@ If you want to add support for a new site, you can follow this quick list (assum
webpage = self._download_webpage(url, video_id) webpage = self._download_webpage(url, video_id)
# TODO more code goes here, for example ... # TODO more code goes here, for example ...
title = self._html_search_regex(r'<h1>(.*?)</h1>', webpage, 'title') title = self._html_search_regex(r'<h1>(.+?)</h1>', webpage, 'title')
return { return {
'id': video_id, 'id': video_id,
'title': title, 'title': title,
'description': self._og_search_description(webpage), 'description': self._og_search_description(webpage),
'uploader': self._search_regex(r'<div[^>]+id="uploader"[^>]*>([^<]+)<', webpage, 'uploader', fatal=False),
# TODO more properties (see youtube_dl/extractor/common.py) # TODO more properties (see youtube_dl/extractor/common.py)
} }
``` ```
5. Add an import in [`youtube_dl/extractor/__init__.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/__init__.py). 5. Add an import in [`youtube_dl/extractor/__init__.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/__init__.py).
6. Run `python test/test_download.py TestDownload.test_YourExtractor`. This *should fail* at first, but you can continually re-run it until you're done. If you decide to add more than one test, then rename ``_TEST`` to ``_TESTS`` and make it into a list of dictionaries. The tests will be then be named `TestDownload.test_YourExtractor`, `TestDownload.test_YourExtractor_1`, `TestDownload.test_YourExtractor_2`, etc. 6. Run `python test/test_download.py TestDownload.test_YourExtractor`. This *should fail* at first, but you can continually re-run it until you're done. If you decide to add more than one test, then rename ``_TEST`` to ``_TESTS`` and make it into a list of dictionaries. The tests will then be named `TestDownload.test_YourExtractor`, `TestDownload.test_YourExtractor_1`, `TestDownload.test_YourExtractor_2`, etc.
7. Have a look at [`youtube_dl/common/extractor/common.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/common.py) for possible helper methods and a [detailed description of what your extractor should return](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/common.py#L38). Add tests and code for as many as you want. 7. Have a look at [`youtube_dl/extractor/common.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/common.py) for possible helper methods and a [detailed description of what your extractor should and may return](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/common.py#L62-L200). Add tests and code for as many as you want.
8. If you can, check the code with [flake8](https://pypi.python.org/pypi/flake8). 8. If you can, check the code with [flake8](https://pypi.python.org/pypi/flake8).
9. When the tests pass, [add](http://git-scm.com/docs/git-add) the new files and [commit](http://git-scm.com/docs/git-commit) them and [push](http://git-scm.com/docs/git-push) the result, like this: 9. When the tests pass, [add](http://git-scm.com/docs/git-add) the new files and [commit](http://git-scm.com/docs/git-commit) them and [push](http://git-scm.com/docs/git-push) the result, like this:

View File

@@ -61,34 +61,34 @@ youtube-dl: youtube_dl/*.py youtube_dl/*/*.py
chmod a+x youtube-dl chmod a+x youtube-dl
README.md: youtube_dl/*.py youtube_dl/*/*.py README.md: youtube_dl/*.py youtube_dl/*/*.py
COLUMNS=80 python youtube_dl/__main__.py --help | python devscripts/make_readme.py COLUMNS=80 $(PYTHON) youtube_dl/__main__.py --help | $(PYTHON) devscripts/make_readme.py
CONTRIBUTING.md: README.md CONTRIBUTING.md: README.md
python devscripts/make_contributing.py README.md CONTRIBUTING.md $(PYTHON) devscripts/make_contributing.py README.md CONTRIBUTING.md
supportedsites: supportedsites:
python devscripts/make_supportedsites.py docs/supportedsites.md $(PYTHON) devscripts/make_supportedsites.py docs/supportedsites.md
README.txt: README.md README.txt: README.md
pandoc -f markdown -t plain README.md -o README.txt pandoc -f markdown -t plain README.md -o README.txt
youtube-dl.1: README.md youtube-dl.1: README.md
python devscripts/prepare_manpage.py >youtube-dl.1.temp.md $(PYTHON) devscripts/prepare_manpage.py >youtube-dl.1.temp.md
pandoc -s -f markdown -t man youtube-dl.1.temp.md -o youtube-dl.1 pandoc -s -f markdown -t man youtube-dl.1.temp.md -o youtube-dl.1
rm -f youtube-dl.1.temp.md rm -f youtube-dl.1.temp.md
youtube-dl.bash-completion: youtube_dl/*.py youtube_dl/*/*.py devscripts/bash-completion.in youtube-dl.bash-completion: youtube_dl/*.py youtube_dl/*/*.py devscripts/bash-completion.in
python devscripts/bash-completion.py $(PYTHON) devscripts/bash-completion.py
bash-completion: youtube-dl.bash-completion bash-completion: youtube-dl.bash-completion
youtube-dl.zsh: youtube_dl/*.py youtube_dl/*/*.py devscripts/zsh-completion.in youtube-dl.zsh: youtube_dl/*.py youtube_dl/*/*.py devscripts/zsh-completion.in
python devscripts/zsh-completion.py $(PYTHON) devscripts/zsh-completion.py
zsh-completion: youtube-dl.zsh zsh-completion: youtube-dl.zsh
youtube-dl.fish: youtube_dl/*.py youtube_dl/*/*.py devscripts/fish-completion.in youtube-dl.fish: youtube_dl/*.py youtube_dl/*/*.py devscripts/fish-completion.in
python devscripts/fish-completion.py $(PYTHON) devscripts/fish-completion.py
fish-completion: youtube-dl.fish fish-completion: youtube-dl.fish

507
README.md
View File

@@ -9,6 +9,7 @@ youtube-dl - download videos from youtube.com or other video platforms
- [VIDEO SELECTION](#video-selection) - [VIDEO SELECTION](#video-selection)
- [FAQ](#faq) - [FAQ](#faq)
- [DEVELOPER INSTRUCTIONS](#developer-instructions) - [DEVELOPER INSTRUCTIONS](#developer-instructions)
- [EMBEDDING YOUTUBE-DL](#embedding-youtube-dl)
- [BUGS](#bugs) - [BUGS](#bugs)
- [COPYRIGHT](#copyright) - [COPYRIGHT](#copyright)
@@ -34,7 +35,7 @@ You can also use pip:
sudo pip install youtube-dl sudo pip install youtube-dl
Alternatively, refer to the developer instructions below for how to check out and work with the git repository. For further options, including PGP signatures, see https://rg3.github.io/youtube-dl/download.html . Alternatively, refer to the [developer instructions](#developer-instructions) for how to check out and work with the git repository. For further options, including PGP signatures, see the [youtube-dl Download Page](https://rg3.github.io/youtube-dl/download.html).
# DESCRIPTION # DESCRIPTION
**youtube-dl** is a small command-line program to download videos from **youtube-dl** is a small command-line program to download videos from
@@ -48,110 +49,220 @@ which means you can modify it, redistribute it or use it however you like.
# OPTIONS # OPTIONS
-h, --help Print this help text and exit -h, --help Print this help text and exit
--version Print program version and exit --version Print program version and exit
-U, --update Update this program to latest version. Make sure that you have sufficient permissions (run with sudo if needed) -U, --update Update this program to latest version. Make
-i, --ignore-errors Continue on download errors, for example to skip unavailable videos in a playlist sure that you have sufficient permissions
--abort-on-error Abort downloading of further videos (in the playlist or the command line) if an error occurs (run with sudo if needed)
-i, --ignore-errors Continue on download errors, for example to
skip unavailable videos in a playlist
--abort-on-error Abort downloading of further videos (in the
playlist or the command line) if an error
occurs
--dump-user-agent Display the current browser identification --dump-user-agent Display the current browser identification
--list-extractors List all supported extractors --list-extractors List all supported extractors
--extractor-descriptions Output descriptions of all supported extractors --extractor-descriptions Output descriptions of all supported
--force-generic-extractor Force extraction to use the generic extractor extractors
--default-search PREFIX Use this prefix for unqualified URLs. For example "gvsearch2:" downloads two videos from google videos for youtube-dl "large apple". --force-generic-extractor Force extraction to use the generic
Use the value "auto" to let youtube-dl guess ("auto_warning" to emit a warning when guessing). "error" just throws an error. The extractor
default value "fixup_error" repairs broken URLs, but emits an error if this is not possible instead of searching. --default-search PREFIX Use this prefix for unqualified URLs. For
--ignore-config Do not read configuration files. When given in the global configuration file /etc/youtube-dl.conf: Do not read the user configuration example "gvsearch2:" downloads two videos
in ~/.config/youtube-dl/config (%APPDATA%/youtube-dl/config.txt on Windows) from google videos for youtube-dl "large
--flat-playlist Do not extract the videos of a playlist, only list them. apple". Use the value "auto" to let
youtube-dl guess ("auto_warning" to emit a
warning when guessing). "error" just throws
an error. The default value "fixup_error"
repairs broken URLs, but emits an error if
this is not possible instead of searching.
--ignore-config Do not read configuration files. When given
in the global configuration file /etc
/youtube-dl.conf: Do not read the user
configuration in ~/.config/youtube-
dl/config (%APPDATA%/youtube-dl/config.txt
on Windows)
--flat-playlist Do not extract the videos of a playlist,
only list them.
--no-color Do not emit color codes in output --no-color Do not emit color codes in output
## Network Options: ## Network Options:
--proxy URL Use the specified HTTP/HTTPS proxy. Pass in an empty string (--proxy "") for direct connection --proxy URL Use the specified HTTP/HTTPS proxy. Pass in
an empty string (--proxy "") for direct
connection
--socket-timeout SECONDS Time to wait before giving up, in seconds --socket-timeout SECONDS Time to wait before giving up, in seconds
--source-address IP Client-side IP address to bind to (experimental) --source-address IP Client-side IP address to bind to
-4, --force-ipv4 Make all connections via IPv4 (experimental) (experimental)
-6, --force-ipv6 Make all connections via IPv6 (experimental) -4, --force-ipv4 Make all connections via IPv4
--cn-verification-proxy URL Use this proxy to verify the IP address for some Chinese sites. The default proxy specified by --proxy (or none, if the options is (experimental)
not present) is used for the actual downloading. (experimental) -6, --force-ipv6 Make all connections via IPv6
(experimental)
--cn-verification-proxy URL Use this proxy to verify the IP address for
some Chinese sites. The default proxy
specified by --proxy (or none, if the
options is not present) is used for the
actual downloading. (experimental)
## Video Selection: ## Video Selection:
--playlist-start NUMBER Playlist video to start at (default is 1) --playlist-start NUMBER Playlist video to start at (default is 1)
--playlist-end NUMBER Playlist video to end at (default is last) --playlist-end NUMBER Playlist video to end at (default is last)
--playlist-items ITEM_SPEC Playlist video items to download. Specify indices of the videos in the playlist separated by commas like: "--playlist-items 1,2,5,8" --playlist-items ITEM_SPEC Playlist video items to download. Specify
if you want to download videos indexed 1, 2, 5, 8 in the playlist. You can specify range: "--playlist-items 1-3,7,10-13", it will indices of the videos in the playlist
download the videos at index 1, 2, 3, 7, 10, 11, 12 and 13. separated by commas like: "--playlist-items
--match-title REGEX Download only matching titles (regex or caseless sub-string) 1,2,5,8" if you want to download videos
--reject-title REGEX Skip download for matching titles (regex or caseless sub-string) indexed 1, 2, 5, 8 in the playlist. You can
specify range: "--playlist-items
1-3,7,10-13", it will download the videos
at index 1, 2, 3, 7, 10, 11, 12 and 13.
--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 --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) --min-filesize SIZE Do not download any videos smaller than
--max-filesize SIZE Do not download any videos larger than SIZE (e.g. 50k or 44.6m) SIZE (e.g. 50k or 44.6m)
--max-filesize SIZE Do not download any videos larger than SIZE
(e.g. 50k or 44.6m)
--date DATE Download only videos uploaded in this date --date DATE Download only videos uploaded in this date
--datebefore DATE Download only videos uploaded on or before this date (i.e. inclusive) --datebefore DATE Download only videos uploaded on or before
--dateafter DATE Download only videos uploaded on or after this date (i.e. inclusive) this date (i.e. inclusive)
--min-views COUNT Do not download any videos with less than COUNT views --dateafter DATE Download only videos uploaded on or after
--max-views COUNT Do not download any videos with more than COUNT views this date (i.e. inclusive)
--match-filter FILTER Generic video filter (experimental). Specify any key (see help for -o for a list of available keys) to match if the key is present, --min-views COUNT Do not download any videos with less than
!key to check if the key is not present,key > NUMBER (like "comment_count > 12", also works with >=, <, <=, !=, =) to compare against COUNT views
a number, and & to require multiple matches. Values which are not known are excluded unless you put a question mark (?) after the --max-views COUNT Do not download any videos with more than
operator.For example, to only match videos that have been liked more than 100 times and disliked less than 50 times (or the dislike COUNT views
functionality is not available at the given service), but who also have a description, use --match-filter "like_count > 100 & --match-filter FILTER Generic video filter (experimental).
Specify any key (see help for -o for a list
of available keys) to match if the key is
present, !key to check if the key is not
present,key > NUMBER (like "comment_count >
12", also works with >=, <, <=, !=, =) to
compare against a number, and & to require
multiple matches. Values which are not
known are excluded unless you put a
question mark (?) after the operator.For
example, to only match videos that have
been liked more than 100 times and disliked
less than 50 times (or the dislike
functionality is not available at the given
service), but who also have a description,
use --match-filter "like_count > 100 &
dislike_count <? 50 & description" . dislike_count <? 50 & description" .
--no-playlist Download only the video, if the URL refers to a video and a playlist. --no-playlist Download only the video, if the URL refers
--yes-playlist Download the playlist, if the URL refers to a video and a playlist. to a video and a playlist.
--age-limit YEARS Download only videos suitable for the given age --yes-playlist Download the playlist, if the URL refers to
--download-archive FILE Download only videos not listed in the archive file. Record the IDs of all downloaded videos in it. a video and a playlist.
--include-ads Download advertisements as well (experimental) --age-limit YEARS Download only videos suitable for the given
age
--download-archive FILE Download only videos not listed in the
archive file. Record the IDs of all
downloaded videos in it.
--include-ads Download advertisements as well
(experimental)
## Download Options: ## Download Options:
-r, --rate-limit LIMIT Maximum download rate in bytes per second (e.g. 50K or 4.2M) -r, --rate-limit LIMIT Maximum download rate in bytes per second
-R, --retries RETRIES Number of retries (default is 10), or "infinite". (e.g. 50K or 4.2M)
--buffer-size SIZE Size of download buffer (e.g. 1024 or 16K) (default is 1024) -R, --retries RETRIES Number of retries (default is 10), or
--no-resize-buffer Do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE. "infinite".
--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.
--playlist-reverse Download playlist videos in reverse order --playlist-reverse Download playlist videos in reverse order
--xattr-set-filesize Set file xattribute ytdl.filesize with expected filesize (experimental) --xattr-set-filesize Set file xattribute ytdl.filesize with
--hls-prefer-native Use the native HLS downloader instead of ffmpeg (experimental) expected filesize (experimental)
--external-downloader COMMAND Use the specified external downloader. Currently supports aria2c,curl,httpie,wget --hls-prefer-native Use the native HLS downloader instead of
--external-downloader-args ARGS Give these arguments to the external downloader ffmpeg (experimental)
--external-downloader COMMAND Use the specified external downloader.
Currently supports
aria2c,axel,curl,httpie,wget
--external-downloader-args ARGS Give these arguments to the external
downloader
## Filesystem Options: ## Filesystem Options:
-a, --batch-file FILE File containing URLs to download ('-' for stdin) -a, --batch-file FILE File containing URLs to download ('-' for
stdin)
--id Use only video ID in file name --id Use only video ID in file name
-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 -o, --output TEMPLATE Output filename template. Use %(title)s to
nickname if different, %(autonumber)s to get an automatically incremented number, %(ext)s for the filename extension, %(format)s for get the title, %(uploader)s for the
the format description (like "22 - 1280x720" or "HD"), %(format_id)s for the unique id of the format (like YouTube's itags: "137"), uploader name, %(uploader_id)s for the
%(upload_date)s for the upload date (YYYYMMDD), %(extractor)s for the provider (youtube, metacafe, etc), %(id)s for the video id, uploader nickname if different,
%(playlist_title)s, %(playlist_id)s, or %(playlist)s (=title if present, ID otherwise) for the playlist the video is in, %(autonumber)s to get an automatically
%(playlist_index)s for the position in the playlist. %(height)s and %(width)s for the width and height of the video format. incremented number, %(ext)s for the
%(resolution)s for a textual description of the resolution of the video format. %% for a literal percent. Use - to output to stdout. filename extension, %(format)s for the
Can also be used to download to a different directory, for example with -o '/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s' . format description (like "22 - 1280x720" or
--autonumber-size NUMBER Specify the number of digits in %(autonumber)s when it is present in output filename template or --auto-number option is given "HD"), %(format_id)s for the unique id of
--restrict-filenames Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames the format (like YouTube's itags: "137"),
-A, --auto-number [deprecated; use -o "%(autonumber)s-%(title)s.%(ext)s" ] Number downloaded files starting from 00000 %(upload_date)s for the upload date
-t, --title [deprecated] Use title in file name (default) (YYYYMMDD), %(extractor)s for the provider
(youtube, metacafe, etc), %(id)s for the
video id, %(playlist_title)s,
%(playlist_id)s, or %(playlist)s (=title if
present, ID otherwise) for the playlist the
video is in, %(playlist_index)s for the
position in the playlist. %(height)s and
%(width)s for the width and height of the
video format. %(resolution)s for a textual
description of the resolution of the video
format. %% 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' .
--autonumber-size NUMBER Specify the number of digits in
%(autonumber)s when it is present in output
filename template or --auto-number option
is given
--restrict-filenames Restrict filenames to only ASCII
characters, and avoid "&" and spaces in
filenames
-A, --auto-number [deprecated; use -o
"%(autonumber)s-%(title)s.%(ext)s" ] Number
downloaded files starting from 00000
-t, --title [deprecated] Use title in file name
(default)
-l, --literal [deprecated] Alias of --title -l, --literal [deprecated] Alias of --title
-w, --no-overwrites Do not overwrite files -w, --no-overwrites Do not overwrite files
-c, --continue Force resume of partially downloaded files. By default, youtube-dl will resume downloads if possible. -c, --continue Force resume of partially downloaded files.
--no-continue Do not resume partially downloaded files (restart from beginning) By default, youtube-dl will resume
--no-part Do not use .part files - write directly into output file downloads if possible.
--no-mtime Do not use the Last-modified header to set the file modification time --no-continue Do not resume partially downloaded files
--write-description Write video description to a .description file (restart from beginning)
--no-part Do not use .part files - write directly
into output file
--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 --write-info-json Write video metadata to a .info.json file
--write-annotations Write video annotations to a .annotations.xml file --write-annotations Write video annotations to a
--load-info FILE JSON file containing the video information (created with the "--write-info-json" option) .annotations.xml file
--cookies FILE File to read cookies from and dump cookie jar in --load-info FILE JSON file containing the video information
--cache-dir DIR Location in the filesystem where youtube-dl can store some downloaded information permanently. By default $XDG_CACHE_HOME/youtube-dl (created with the "--write-info-json"
or ~/.cache/youtube-dl . At the moment, only YouTube player files (for videos with obfuscated signatures) are cached, but that may option)
change. --cookies FILE File to read cookies from and dump cookie
jar in
--cache-dir DIR Location in the filesystem where youtube-dl
can store some downloaded information
permanently. By default $XDG_CACHE_HOME
/youtube-dl or ~/.cache/youtube-dl . At the
moment, only YouTube player files (for
videos with obfuscated signatures) are
cached, but that may change.
--no-cache-dir Disable filesystem caching --no-cache-dir Disable filesystem caching
--rm-cache-dir Delete all filesystem cache files --rm-cache-dir Delete all filesystem cache files
## Thumbnail images: ## Thumbnail images:
--write-thumbnail Write thumbnail image to disk --write-thumbnail Write thumbnail image to disk
--write-all-thumbnails Write all thumbnail image formats to disk --write-all-thumbnails Write all thumbnail image formats to disk
--list-thumbnails Simulate and list all available thumbnail formats --list-thumbnails Simulate and list all available thumbnail
formats
## Verbosity / Simulation Options: ## Verbosity / Simulation Options:
-q, --quiet Activate quiet mode -q, --quiet Activate quiet mode
--no-warnings Ignore warnings --no-warnings Ignore warnings
-s, --simulate Do not download the video and do not write anything to disk -s, --simulate Do not download the video and do not write
anything to disk
--skip-download Do not download the video --skip-download Do not download the video
-g, --get-url Simulate, quiet but print URL -g, --get-url Simulate, quiet but print URL
-e, --get-title Simulate, quiet but print title -e, --get-title Simulate, quiet but print title
@@ -161,86 +272,151 @@ which means you can modify it, redistribute it or use it however you like.
--get-duration Simulate, quiet but print video length --get-duration Simulate, quiet but print video length
--get-filename Simulate, quiet but print output filename --get-filename Simulate, quiet but print output filename
--get-format Simulate, quiet but print output format --get-format Simulate, quiet but print output format
-j, --dump-json Simulate, quiet but print JSON information. See --output for a description of available keys. -j, --dump-json Simulate, quiet but print JSON information.
-J, --dump-single-json Simulate, quiet but print JSON information for each command-line argument. If the URL refers to a playlist, dump the whole playlist See --output for a description of available
information in a single line. keys.
--print-json Be quiet and print the video information as JSON (video is still being downloaded). -J, --dump-single-json Simulate, quiet but print JSON information
for each command-line argument. If the URL
refers to a playlist, dump the whole
playlist information in a single line.
--print-json Be quiet and print the video information as
JSON (video is still being downloaded).
--newline Output progress bar as new lines --newline Output progress bar as new lines
--no-progress Do not print progress bar --no-progress Do not print progress bar
--console-title Display progress in console titlebar --console-title Display progress in console titlebar
-v, --verbose Print various debugging information -v, --verbose Print various debugging information
--dump-pages Print downloaded pages encoded using base64 to debug problems (very verbose) --dump-pages Print downloaded pages encoded using base64
--write-pages Write downloaded intermediary pages to files in the current directory to debug problems to debug problems (very verbose)
--write-pages Write downloaded intermediary pages to
files in the current directory to debug
problems
--print-traffic Display sent and read HTTP traffic --print-traffic Display sent and read HTTP traffic
-C, --call-home Contact the youtube-dl server for debugging -C, --call-home Contact the youtube-dl server for debugging
--no-call-home Do NOT contact the youtube-dl server for debugging --no-call-home Do NOT contact the youtube-dl server for
debugging
## Workarounds: ## Workarounds:
--encoding ENCODING Force the specified encoding (experimental) --encoding ENCODING Force the specified encoding (experimental)
--no-check-certificate Suppress HTTPS certificate validation --no-check-certificate Suppress HTTPS certificate validation
--prefer-insecure Use an unencrypted connection to retrieve information about the video. (Currently supported only for YouTube) --prefer-insecure Use an unencrypted connection to retrieve
information about the video. (Currently
supported only for YouTube)
--user-agent UA Specify a custom user agent --user-agent UA Specify a custom user agent
--referer URL Specify a custom referer, use if the video access is restricted to one domain --referer URL Specify a custom referer, use if the video
--add-header FIELD:VALUE Specify a custom HTTP header and its value, separated by a colon ':'. You can use this option multiple times access is restricted to one domain
--bidi-workaround Work around terminals that lack bidirectional text support. Requires bidiv or fribidi executable in PATH --add-header FIELD:VALUE Specify a custom HTTP header and its value,
--sleep-interval SECONDS Number of seconds to sleep before each download. separated by a colon ':'. You can use this
option multiple times
--bidi-workaround Work around terminals that lack
bidirectional text support. Requires bidiv
or fribidi executable in PATH
--sleep-interval SECONDS Number of seconds to sleep before each
download.
## Video Format Options: ## Video Format Options:
-f, --format FORMAT Video format code, see the "FORMAT SELECTION" for all the info -f, --format FORMAT Video format code, see the "FORMAT
SELECTION" for all the info
--all-formats Download all available video formats --all-formats Download all available video formats
--prefer-free-formats Prefer free video formats unless a specific one is requested --prefer-free-formats Prefer free video formats unless a specific
-F, --list-formats List all available formats one is requested
--youtube-skip-dash-manifest Do not download the DASH manifests and related data on YouTube videos -F, --list-formats List all available formats of requested
--merge-output-format FORMAT If a merge is required (e.g. bestvideo+bestaudio), output to given container format. One of mkv, mp4, ogg, webm, flv. Ignored if no videos
merge is required --youtube-skip-dash-manifest Do not download the DASH manifests and
related data on YouTube videos
--merge-output-format FORMAT If a merge is required (e.g.
bestvideo+bestaudio), output to given
container format. One of mkv, mp4, ogg,
webm, flv. Ignored if no merge is required
## Subtitle Options: ## Subtitle Options:
--write-sub Write subtitle file --write-sub Write subtitle file
--write-auto-sub Write automatic subtitle file (YouTube only) --write-auto-sub Write automatically generated subtitle file
--all-subs Download all the available subtitles of the video (YouTube only)
--all-subs Download all the available subtitles of the
video
--list-subs List all available subtitles for the video --list-subs List all available subtitles for the video
--sub-format FORMAT Subtitle format, accepts formats preference, for example: "srt" or "ass/srt/best" --sub-format FORMAT Subtitle format, accepts formats
--sub-lang LANGS Languages of the subtitles to download (optional) separated by commas, use IETF language tags like 'en,pt' preference, for example: "srt" or
"ass/srt/best"
--sub-lang LANGS Languages of the subtitles to download
(optional) separated by commas, use IETF
language tags like 'en,pt'
## Authentication Options: ## Authentication Options:
-u, --username USERNAME Login with this account ID -u, --username USERNAME Login with this account ID
-p, --password PASSWORD Account password. If this option is left out, youtube-dl will ask interactively. -p, --password PASSWORD Account password. If this option is left
out, youtube-dl will ask interactively.
-2, --twofactor TWOFACTOR Two-factor auth code -2, --twofactor TWOFACTOR Two-factor auth code
-n, --netrc Use .netrc authentication data -n, --netrc Use .netrc authentication data
--video-password PASSWORD Video password (vimeo, smotri) --video-password PASSWORD Video password (vimeo, smotri, youku)
## Post-processing Options: ## Post-processing Options:
-x, --extract-audio Convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe) -x, --extract-audio Convert video files to audio-only files
--audio-format FORMAT Specify audio format: "best", "aac", "vorbis", "mp3", "m4a", "opus", or "wav"; "best" by default (requires ffmpeg or avconv and ffprobe or
--audio-quality QUALITY Specify ffmpeg/avconv audio quality, insert a value between 0 (better) and 9 (worse) for VBR or a specific bitrate like 128K (default avprobe)
5) --audio-format FORMAT Specify audio format: "best", "aac",
--recode-video FORMAT Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm|mkv|avi) "vorbis", "mp3", "m4a", "opus", or "wav";
"best" by default
--audio-quality QUALITY Specify ffmpeg/avconv audio quality, 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|mkv|avi)
--postprocessor-args ARGS Give these arguments to the postprocessor --postprocessor-args ARGS Give these arguments to the postprocessor
-k, --keep-video Keep the video file on disk after the post-processing; the video is erased by default -k, --keep-video Keep the video file on disk after the post-
--no-post-overwrites Do not overwrite post-processed files; the post-processed files are overwritten by default processing; the video is erased by default
--embed-subs Embed subtitles in the video (only for mkv and mp4 videos) --no-post-overwrites Do not overwrite post-processed files; the
post-processed files are overwritten by
default
--embed-subs Embed subtitles in the video (only for mkv
and mp4 videos)
--embed-thumbnail Embed thumbnail in the audio as cover art --embed-thumbnail Embed thumbnail in the audio as cover art
--add-metadata Write metadata to the video file --add-metadata Write metadata to the video file
--metadata-from-title FORMAT Parse additional metadata like song title / artist from the video title. The format syntax is the same as --output, the parsed --metadata-from-title FORMAT Parse additional metadata like song title /
parameters replace existing values. Additional templates: %(album)s, %(artist)s. Example: --metadata-from-title "%(artist)s - artist from the video title. The format
%(title)s" matches a title like "Coldplay - Paradise" syntax is the same as --output, the parsed
--xattrs Write metadata to the video file's xattrs (using dublin core and xdg standards) parameters replace existing values.
--fixup POLICY Automatically correct known faults of the file. One of never (do nothing), warn (only emit a warning), detect_or_warn (the default; Additional templates: %(album)s,
fix file if we can, warn otherwise) %(artist)s. Example: --metadata-from-title
--prefer-avconv Prefer avconv over ffmpeg for running the postprocessors (default) "%(artist)s - %(title)s" matches a title
--prefer-ffmpeg Prefer ffmpeg over avconv for running the postprocessors like "Coldplay - Paradise"
--ffmpeg-location PATH Location of the ffmpeg/avconv binary; either the path to the binary or its containing directory. --xattrs Write metadata to the video file's xattrs
--exec CMD Execute a command on the file after downloading, similar to find's -exec syntax. Example: --exec 'adb push {} /sdcard/Music/ && rm (using dublin core and xdg standards)
{}' --fixup POLICY Automatically correct known faults of the
--convert-subtitles FORMAT Convert the subtitles to other format (currently supported: srt|ass|vtt) file. One of never (do nothing), warn (only
emit a warning), detect_or_warn (the
default; fix file if we can, warn
otherwise)
--prefer-avconv Prefer avconv over ffmpeg for running the
postprocessors (default)
--prefer-ffmpeg Prefer ffmpeg over avconv for running the
postprocessors
--ffmpeg-location PATH Location of the ffmpeg/avconv binary;
either the path to the binary or its
containing directory.
--exec CMD Execute a command on the file after
downloading, similar to find's -exec
syntax. Example: --exec 'adb push {}
/sdcard/Music/ && rm {}'
--convert-subtitles FORMAT Convert the subtitles to other format
(currently supported: srt|ass|vtt)
# CONFIGURATION # 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/config`. On Windows, the configuration file locations are `%APPDATA%\youtube-dl\config.txt` and `C:\Users\<user name>\youtube-dl.conf`. You can configure youtube-dl by placing any supported command line option to a configuration file. On Linux, the system wide configuration file is located at `/etc/youtube-dl.conf` and the user wide configuration file at `~/.config/youtube-dl/config`. On Windows, the user wide configuration file locations are `%APPDATA%\youtube-dl\config.txt` or `C:\Users\<user name>\youtube-dl.conf`. For example, with the following configuration file youtube-dl will always extract the audio, not copy the mtime and use a proxy:
```
--extract-audio
--no-mtime
--proxy 127.0.0.1:3128
```
### Authentication with `.netrc` file ### You can use `--ignore-config` if you want to disable the configuration file for a particular youtube-dl run.
You may also want to configure automatic credentials storage for extractors that support authentication (by providing login and password with `--username` and `--password`) in order not to pass credentials as command line arguments on every youtube-dl execution and prevent tracking plain text passwords in shell command history. You can achieve this using [`.netrc` file](http://stackoverflow.com/tags/.netrc/info) on per extractor basis. For that you will need to create `.netrc` file in your `$HOME` and restrict permissions to read/write by you only: ### Authentication with `.netrc` file
You may also want to configure automatic credentials storage for extractors that support authentication (by providing login and password with `--username` and `--password`) in order not to pass credentials as command line arguments on every youtube-dl execution and prevent tracking plain text passwords in the shell command history. You can achieve this using a [`.netrc` file](http://stackoverflow.com/tags/.netrc/info) on per extractor basis. For that you will need to create a`.netrc` file in your `$HOME` and restrict permissions to read/write by you only:
``` ```
touch $HOME/.netrc touch $HOME/.netrc
chmod a-rwx,u+rw $HOME/.netrc chmod a-rwx,u+rw $HOME/.netrc
@@ -254,13 +430,13 @@ For example:
machine youtube login myaccount@gmail.com password my_youtube_password machine youtube login myaccount@gmail.com password my_youtube_password
machine twitch login my_twitch_account_name password my_twitch_password machine twitch login my_twitch_account_name password my_twitch_password
``` ```
To activate authentication with `.netrc` file you should pass `--netrc` to youtube-dl or to place it in [configuration file](#configuration). To activate authentication with the `.netrc` file you should pass `--netrc` to youtube-dl or place it in the [configuration file](#configuration).
On Windows you may also need to setup `%HOME%` environment variable manually. On Windows you may also need to setup the `%HOME%` environment variable manually.
# OUTPUT TEMPLATE # 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: 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 parentheses, followed by a lowercase S. Allowed names are:
- `id`: The sequence will be replaced by the video identifier. - `id`: The sequence will be replaced by the video identifier.
- `url`: The sequence will be replaced by the video URL. - `url`: The sequence will be replaced by the video URL.
@@ -270,8 +446,10 @@ The `-o` option allows users to indicate a template for the output file names. T
- `ext`: The sequence will be replaced by the appropriate extension (like flv or mp4). - `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. - `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. - `autonumber`: The sequence will be replaced by a five-digit number that will be increased with each download, starting at zero.
- `playlist`: The name or the id of the playlist that contains the video. - `playlist`: The sequence will be replaced by the name or the id of the playlist that contains the video.
- `playlist_index`: The index of the video in the playlist, a five-digit number. - `playlist_index`: The sequence will be replaced by the index of the video in the playlist padded with leading zeros according to the total length of the playlist.
- `format_id`: The sequence will be replaced by the format code specified by `--format`.
- `duration`: The sequence will be replaced by the length of the video in seconds.
The current default template is `%(title)s-%(id)s.%(ext)s`. The current default template is `%(title)s-%(id)s.%(ext)s`.
@@ -286,18 +464,18 @@ youtube-dl_test_video_.mp4 # A simple file name
# FORMAT SELECTION # FORMAT SELECTION
By default youtube-dl tries to download the best quality, but sometimes you may want to download other format. By default youtube-dl tries to download the best quality, but sometimes you may want to download in a different format.
The simplest case is requesting a specific format, for example `-f 22`. You can get the list of available formats using `--list-formats`, you can also use a file extension (currently it supports aac, m4a, mp3, mp4, ogg, wav, webm) or the special names `best`, `bestvideo`, `bestaudio` and `worst`. The simplest case is requesting a specific format, for example `-f 22`. You can get the list of available formats using `--list-formats`, you can also use a file extension (currently it supports aac, m4a, mp3, mp4, ogg, wav, webm) or the special names `best`, `bestvideo`, `bestaudio` and `worst`.
If you want to download multiple videos and they don't have the same formats available, you can specify the order of preference using slashes, as in `-f 22/17/18`. You can also filter the video results by putting a condition in brackets, as in `-f "best[height=720]"` (or `-f "[filesize>10M]"`). This works for filesize, height, width, tbr, abr, vbr, asr, and fps and the comparisons <, <=, >, >=, =, != and for ext, acodec, vcodec, container, and protocol and the comparisons =, != . Formats for which the value is not known are excluded unless you put a question mark (?) after the operator. You can combine format filters, so `-f "[height <=? 720][tbr>500]"` selects up to 720p videos (or videos where the height is not known) with a bitrate of at least 500 KBit/s. Use commas to download multiple formats, such as `-f 136/137/mp4/bestvideo,140/m4a/bestaudio`. You can merge the video and audio of two formats into a single file using `-f <video-format>+<audio-format>` (requires ffmpeg or avconv), for example `-f bestvideo+bestaudio`. Format selectors can also be grouped using parentheses, for example if you want to download the best mp4 and webm formats with a height lower than 480 you can use `-f '(mp4,webm)[height<480]'`. If you want to download multiple videos and they don't have the same formats available, you can specify the order of preference using slashes, as in `-f 22/17/18`. You can also filter the video results by putting a condition in brackets, as in `-f "best[height=720]"` (or `-f "[filesize>10M]"`). This works for filesize, height, width, tbr, abr, vbr, asr, and fps and the comparisons <, <=, >, >=, =, != and for ext, acodec, vcodec, container, and protocol and the comparisons =, != . Formats for which the value is not known are excluded unless you put a question mark (?) after the operator. You can combine format filters, so `-f "[height <=? 720][tbr>500]"` selects up to 720p videos (or videos where the height is not known) with a bitrate of at least 500 KBit/s. Use commas to download multiple formats, such as `-f 136/137/mp4/bestvideo,140/m4a/bestaudio`. You can merge the video and audio of two formats into a single file using `-f <video-format>+<audio-format>` (requires ffmpeg or avconv), for example `-f bestvideo+bestaudio`. Format selectors can also be grouped using parentheses, for example if you want to download the best mp4 and webm formats with a height lower than 480 you can use `-f '(mp4,webm)[height<480]'`.
Since the end of April 2015 and version 2015.04.26 youtube-dl uses `-f bestvideo+bestaudio/best` as default format selection (see #5447, #5456). If ffmpeg or avconv are installed this results in downloading `bestvideo` and `bestaudio` separately and muxing them together into a single file giving the best overall quality available. Otherwise it falls back to `best` and results in downloading best available quality served as a single file. `best` is also needed for videos that don't come from YouTube because they don't provide the audio and video in two different files. If you want to only download some dash formats (for example if you are not interested in getting videos with a resolution higher than 1080p), you can add `-f bestvideo[height<=?1080]+bestaudio/best` to your configuration file. Note that if you use youtube-dl to stream to `stdout` (and most likely to pipe it to your media player then), i.e. you explicitly specify output template as `-o -`, youtube-dl still uses `-f best` format selection in order to start content delivery immediately to your player and not to wait until `bestvideo` and `bestaudio` are downloaded and muxed. Since the end of April 2015 and version 2015.04.26 youtube-dl uses `-f bestvideo+bestaudio/best` as default format selection (see #5447, #5456). If ffmpeg or avconv are installed this results in downloading `bestvideo` and `bestaudio` separately and muxing them together into a single file giving the best overall quality available. Otherwise it falls back to `best` and results in downloading the best available quality served as a single file. `best` is also needed for videos that don't come from YouTube because they don't provide the audio and video in two different files. If you want to only download some dash formats (for example if you are not interested in getting videos with a resolution higher than 1080p), you can add `-f bestvideo[height<=?1080]+bestaudio/best` to your configuration file. Note that if you use youtube-dl to stream to `stdout` (and most likely to pipe it to your media player then), i.e. you explicitly specify output template as `-o -`, youtube-dl still uses `-f best` format selection in order to start content delivery immediately to your player and not to wait until `bestvideo` and `bestaudio` are downloaded and muxed.
If you want to preserve the old format selection behavior (prior to youtube-dl 2015.04.26), i.e. you want to download best available quality media served as a single file, you should explicitly specify your choice with `-f best`. You may want to add it to the [configuration file](#configuration) in order not to type it every time you run youtube-dl. If you want to preserve the old format selection behavior (prior to youtube-dl 2015.04.26), i.e. you want to download the best available quality media served as a single file, you should explicitly specify your choice with `-f best`. You may want to add it to the [configuration file](#configuration) in order not to type it every time you run youtube-dl.
# VIDEO SELECTION # VIDEO SELECTION
Videos can be filtered by their upload date using the options `--date`, `--datebefore` or `--dateafter`, they accept dates in two formats: Videos can be filtered by their upload date using the options `--date`, `--datebefore` or `--dateafter`. They accept dates in two formats:
- Absolute dates: Dates in the format `YYYYMMDD`. - Absolute dates: Dates in the format `YYYYMMDD`.
- Relative dates: Dates in the format `(now|today)[+-][0-9](day|week|month|year)(s)?` - Relative dates: Dates in the format `(now|today)[+-][0-9](day|week|month|year)(s)?`
@@ -311,7 +489,7 @@ $ youtube-dl --dateafter now-6months
# Download only the videos uploaded on January 1, 1970 # Download only the videos uploaded on January 1, 1970
$ youtube-dl --date 19700101 $ youtube-dl --date 19700101
$ # will only download the videos uploaded in the 200x decade $ # Download only the videos uploaded in the 200x decade
$ youtube-dl --dateafter 20000101 --datebefore 20091231 $ youtube-dl --dateafter 20000101 --datebefore 20091231
``` ```
@@ -323,7 +501,7 @@ If you've followed [our manual installation instructions](http://rg3.github.io/y
If you have used pip, a simple `sudo pip install -U youtube-dl` is sufficient to update. If you have used pip, a simple `sudo pip install -U youtube-dl` is sufficient to update.
If you have installed youtube-dl using a package manager like *apt-get* or *yum*, use the standard system update mechanism to update. Note that distribution packages are often outdated. As a rule of thumb, youtube-dl releases at least once a month, and often weekly or even daily. Simply go to http://yt-dl.org/ to find out the current version. Unfortunately, there is nothing we youtube-dl developers can do if your distributions serves a really outdated version. You can (and should) complain to your distribution in their bugtracker or support forum. If you have installed youtube-dl using a package manager like *apt-get* or *yum*, use the standard system update mechanism to update. Note that distribution packages are often outdated. As a rule of thumb, youtube-dl releases at least once a month, and often weekly or even daily. Simply go to http://yt-dl.org/ to find out the current version. Unfortunately, there is nothing we youtube-dl developers can do if your distribution serves a really outdated version. You can (and should) complain to your distribution in their bugtracker or support forum.
As a last resort, you can also uninstall the version installed by your package manager and follow our manual installation instructions. For that, remove the distribution's package, with a line like As a last resort, you can also uninstall the version installed by your package manager and follow our manual installation instructions. For that, remove the distribution's package, with a line like
@@ -349,7 +527,7 @@ If you have installed youtube-dl with a package manager, pip, setup.py or a tarb
By default, youtube-dl intends to have the best options (incidentally, if you have a convincing case that these should be different, [please file an issue where you explain that](https://yt-dl.org/bug)). Therefore, it is unnecessary and sometimes harmful to copy long option strings from webpages. In particular, the only option out of `-citw` that is regularly useful is `-i`. By default, youtube-dl intends to have the best options (incidentally, if you have a convincing case that these should be different, [please file an issue where you explain that](https://yt-dl.org/bug)). Therefore, it is unnecessary and sometimes harmful to copy long option strings from webpages. In particular, the only option out of `-citw` that is regularly useful is `-i`.
### Can you please put the -b option back? ### 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. 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.
@@ -357,17 +535,23 @@ Most people asking this question are not aware that youtube-dl now defaults to d
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. 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.
### Do I need any other programs?
youtube-dl works fine on its own on most sites. However, if you want to convert video/audio, you'll need [avconv](https://libav.org/) or [ffmpeg](https://www.ffmpeg.org/). On some sites - most notably YouTube - videos can be retrieved in a higher quality format without sound. youtube-dl will detect whether avconv/ffmpeg is present and automatically pick the best option.
Videos or video formats streamed via RTMP protocol can only be downloaded when [rtmpdump](https://rtmpdump.mplayerhq.hu/) is installed. Downloading MMS and RTSP videos requires either [mplayer](http://mplayerhq.hu/) or [mpv](https://mpv.io/) to be installed.
### I have downloaded a video but how can I play it? ### 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/). Once the video is fully downloaded, use any video player, such as [vlc](http://www.videolan.org) or [mplayer](http://www.mplayerhq.hu/).
### I extracted a video URL with -g, but it does not play on another machine / in my webbrowser. ### I extracted a video URL with `-g`, but it does not play on another machine / in my webbrowser.
It depends a lot on the service. In many cases, requests for the video (to download/play it) must come from the same IP address and with the same 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. It depends a lot on the service. In many cases, requests for the video (to download/play it) must come from the same IP address and with the same 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.
It may be beneficial to use IPv6; in some cases, the restrictions are only applied to IPv4. Some services (sometimes only for a subset of videos) do not restrict the video URL by IP address, cookie, or user-agent, but these are the exception rather than the rule. It may be beneficial to use IPv6; in some cases, the restrictions are only applied to IPv4. Some services (sometimes only for a subset of videos) do not restrict the video URL by IP address, cookie, or user-agent, but these are the exception rather than the rule.
Please bear in mind that some URL protocols are **not** supported by browsers out of the box, including RTMP. If you are using -g, your own downloader must support these as well. Please bear in mind that some URL protocols are **not** supported by browsers out of the box, including RTMP. If you are using `-g`, your own downloader must support these as well.
If you want to play the video on a machine that is not running youtube-dl, you can relay the video content from the machine that runs youtube-dl. You can use `-o -` to let youtube-dl stream a video to stdout, or simply allow the player to download the files written by youtube-dl in turn. If you want to play the video on a machine that is not running youtube-dl, you can relay the video content from the machine that runs youtube-dl. You can use `-o -` to let youtube-dl stream a video to stdout, or simply allow the player to download the files written by youtube-dl in turn.
@@ -375,13 +559,13 @@ If you want to play the video on a machine that is not running youtube-dl, you c
YouTube has switched to a new video info format in July 2011 which is not supported by old versions of youtube-dl. See [above](#how-do-i-update-youtube-dl) for how to update youtube-dl. YouTube has switched to a new video info format in July 2011 which is not supported by old versions of youtube-dl. See [above](#how-do-i-update-youtube-dl) for how to update youtube-dl.
### ERROR: unable to download video ### ### ERROR: unable to download video
YouTube requires an additional signature since September 2012 which is not supported by old versions of youtube-dl. See [above](#how-do-i-update-youtube-dl) for how to update youtube-dl. YouTube requires an additional signature since September 2012 which is not supported by old versions of youtube-dl. See [above](#how-do-i-update-youtube-dl) for how to update youtube-dl.
### Video URL contains an ampersand and I'm getting some strange output `[1] 2839` or `'v' is not recognized as an internal or external command` ### ### Video URL contains an ampersand and I'm getting some strange output `[1] 2839` or `'v' is not recognized as an internal or external command`
That's actually the output from your shell. Since ampersand is one of the special shell characters it's interpreted by shell preventing you from passing the whole URL to youtube-dl. To disable your shell from interpreting the ampersands (or any other special characters) you have to either put the whole URL in quotes or escape them with a backslash (which approach will work depends on your shell). That's actually the output from your shell. Since ampersand is one of the special shell characters it's interpreted by the shell preventing you from passing the whole URL to youtube-dl. To disable your shell from interpreting the ampersands (or any other special characters) you have to either put the whole URL in quotes or escape them with a backslash (which approach will work depends on your shell).
For example if your URL is https://www.youtube.com/watch?t=4&v=BaW_jenozKc you should end up with following command: For example if your URL is https://www.youtube.com/watch?t=4&v=BaW_jenozKc you should end up with following command:
@@ -403,7 +587,7 @@ In February 2015, the new YouTube player contained a character sequence in a str
These two error codes indicate that the service is blocking your IP address because of overuse. Contact the service and ask them to unblock your IP address, or - if you have acquired a whitelisted IP address already - use the [`--proxy` or `--source-address` options](#network-options) to select another IP address. These two error codes indicate that the service is blocking your IP address because of overuse. Contact the service and ask them to unblock your IP address, or - if you have acquired a whitelisted IP address already - use the [`--proxy` or `--source-address` options](#network-options) to select another IP address.
### SyntaxError: Non-ASCII character ### ### SyntaxError: Non-ASCII character
The error The error
@@ -432,7 +616,7 @@ From then on, after restarting your shell, you will be able to access both youtu
Use the `-o` to specify an [output template](#output-template), for example `-o "/home/user/videos/%(title)s-%(id)s.%(ext)s"`. If you want this for all of your downloads, put the option into your [configuration file](#configuration). Use the `-o` to specify an [output template](#output-template), for example `-o "/home/user/videos/%(title)s-%(id)s.%(ext)s"`. If you want this for all of your downloads, put the option into your [configuration file](#configuration).
### How do I download a video starting with a `-` ? ### How do I download a video starting with a `-`?
Either prepend `http://www.youtube.com/watch?v=` or separate the ID from the options with `--`: Either prepend `http://www.youtube.com/watch?v=` or separate the ID from the options with `--`:
@@ -441,9 +625,9 @@ Either prepend `http://www.youtube.com/watch?v=` or separate the ID from the opt
### How do I pass cookies to youtube-dl? ### How do I pass cookies to youtube-dl?
Use the `--cookies` option, for example `--cookies /path/to/cookies/file.txt`. Note that cookies file must be in Mozilla/Netscape format and the first line of cookies file must be either `# HTTP Cookie File` or `# Netscape HTTP Cookie File`. Make sure you have correct [newline format](https://en.wikipedia.org/wiki/Newline) in cookies file and convert newlines if necessary to correspond your OS, namely `CRLF` (`\r\n`) for Windows, `LF` (`\n`) for Linux and `CR` (`\r`) for Mac OS. `HTTP Error 400: Bad Request` when using `--cookies` is a good sign of invalid newline format. Use the `--cookies` option, for example `--cookies /path/to/cookies/file.txt`. Note that the cookies file must be in Mozilla/Netscape format and the first line of the cookies file must be either `# HTTP Cookie File` or `# Netscape HTTP Cookie File`. Make sure you have correct [newline format](https://en.wikipedia.org/wiki/Newline) in the cookies file and convert newlines if necessary to correspond with your OS, namely `CRLF` (`\r\n`) for Windows, `LF` (`\n`) for Linux and `CR` (`\r`) for Mac OS. `HTTP Error 400: Bad Request` when using `--cookies` is a good sign of invalid newline format.
Passing cookies to youtube-dl is a good way to workaround login when particular extractor does not implement it explicitly. Passing cookies to youtube-dl is a good way to workaround login when a particular extractor does not implement it explicitly.
### Can you add support for this anime video site, or site which shows current movies for free? ### Can you add support for this anime video site, or site which shows current movies for free?
@@ -533,18 +717,19 @@ If you want to add support for a new site, you can follow this quick list (assum
webpage = self._download_webpage(url, video_id) webpage = self._download_webpage(url, video_id)
# TODO more code goes here, for example ... # TODO more code goes here, for example ...
title = self._html_search_regex(r'<h1>(.*?)</h1>', webpage, 'title') title = self._html_search_regex(r'<h1>(.+?)</h1>', webpage, 'title')
return { return {
'id': video_id, 'id': video_id,
'title': title, 'title': title,
'description': self._og_search_description(webpage), 'description': self._og_search_description(webpage),
'uploader': self._search_regex(r'<div[^>]+id="uploader"[^>]*>([^<]+)<', webpage, 'uploader', fatal=False),
# TODO more properties (see youtube_dl/extractor/common.py) # TODO more properties (see youtube_dl/extractor/common.py)
} }
``` ```
5. Add an import in [`youtube_dl/extractor/__init__.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/__init__.py). 5. Add an import in [`youtube_dl/extractor/__init__.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/__init__.py).
6. Run `python test/test_download.py TestDownload.test_YourExtractor`. This *should fail* at first, but you can continually re-run it until you're done. If you decide to add more than one test, then rename ``_TEST`` to ``_TESTS`` and make it into a list of dictionaries. The tests will be then be named `TestDownload.test_YourExtractor`, `TestDownload.test_YourExtractor_1`, `TestDownload.test_YourExtractor_2`, etc. 6. Run `python test/test_download.py TestDownload.test_YourExtractor`. This *should fail* at first, but you can continually re-run it until you're done. If you decide to add more than one test, then rename ``_TEST`` to ``_TESTS`` and make it into a list of dictionaries. The tests will then be named `TestDownload.test_YourExtractor`, `TestDownload.test_YourExtractor_1`, `TestDownload.test_YourExtractor_2`, etc.
7. Have a look at [`youtube_dl/common/extractor/common.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/common.py) for possible helper methods and a [detailed description of what your extractor should return](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/common.py#L38). Add tests and code for as many as you want. 7. Have a look at [`youtube_dl/extractor/common.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/common.py) for possible helper methods and a [detailed description of what your extractor should and may return](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/common.py#L62-L200). Add tests and code for as many as you want.
8. If you can, check the code with [flake8](https://pypi.python.org/pypi/flake8). 8. If you can, check the code with [flake8](https://pypi.python.org/pypi/flake8).
9. When the tests pass, [add](http://git-scm.com/docs/git-add) the new files and [commit](http://git-scm.com/docs/git-commit) them and [push](http://git-scm.com/docs/git-push) the result, like this: 9. When the tests pass, [add](http://git-scm.com/docs/git-add) the new files and [commit](http://git-scm.com/docs/git-commit) them and [push](http://git-scm.com/docs/git-push) the result, like this:
@@ -572,7 +757,7 @@ with youtube_dl.YoutubeDL(ydl_opts) as ydl:
ydl.download(['http://www.youtube.com/watch?v=BaW_jenozKc']) ydl.download(['http://www.youtube.com/watch?v=BaW_jenozKc'])
``` ```
Most likely, you'll want to use various options. For a list of what can be done, have a look at [youtube_dl/YoutubeDL.py](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/YoutubeDL.py#L69). For a start, if you want to intercept youtube-dl's output, set a `logger` object. Most likely, you'll want to use various options. For a list of what can be done, have a look at [youtube_dl/YoutubeDL.py](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/YoutubeDL.py#L117-L265). For a start, if you want to intercept youtube-dl's output, set a `logger` object.
Here's a more complete example of a program that outputs only errors (and a short message after the download is finished), and downloads/converts the video to an mp3 file: Here's a more complete example of a program that outputs only errors (and a short message after the download is finished), and downloads/converts the video to an mp3 file:
@@ -613,11 +798,25 @@ with youtube_dl.YoutubeDL(ydl_opts) as ydl:
# BUGS # BUGS
Bugs and suggestions should be reported at: <https://github.com/rg3/youtube-dl/issues> . Unless you were prompted so or there is another pertinent reason (e.g. GitHub fails to accept the bug report), please do not send bug reports via personal email. For discussions, join us in the irc channel #youtube-dl on freenode. Bugs and suggestions should be reported at: <https://github.com/rg3/youtube-dl/issues>. Unless you were prompted so or there is another pertinent reason (e.g. GitHub fails to accept the bug report), please do not send bug reports via personal email. For discussions, join us in the IRC channel [#youtube-dl](irc://chat.freenode.net/#youtube-dl) on freenode ([webchat](http://webchat.freenode.net/?randomnick=1&channels=youtube-dl)).
**Please include the full output of youtube-dl when run with `-v`**. **Please include the full output of youtube-dl when run with `-v`**, i.e. add `-v` flag to your command line, copy the **whole** output and post it in the issue body wrapped in \`\`\` for better formatting. It should look similar to this:
```
$ youtube-dl -v http://www.youtube.com/watch?v=BaW_jenozKcj
[debug] System config: []
[debug] User config: []
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
[debug] youtube-dl version 2015.12.06
[debug] Git HEAD: 135392e
[debug] Python version 2.6.6 - Windows-2003Server-5.2.3790-SP2
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
[debug] Proxy map: {}
...
```
**Do not post screenshots of verbose log only plain text is acceptable.**
The output (including the first lines) contain important debugging information. Issues without the full output are often not reproducible and therefore do not get solved in short order, if ever. The output (including the first lines) contains important debugging information. Issues without the full output are often not reproducible and therefore do not get solved in short order, if ever.
Please re-read your issue once again to avoid a couple of common mistakes (you can and should use this as a checklist): Please re-read your issue once again to avoid a couple of common mistakes (you can and should use this as a checklist):
@@ -633,19 +832,19 @@ So please elaborate on what feature you are requesting, or what bug you want to
If your report is shorter than two lines, it is almost certainly missing some of these, which makes it hard for us to respond to it. We're often too polite to close the issue outright, but the missing info makes misinterpretation likely. As a commiter myself, I often get frustrated by these issues, since the only possible way for me to move forward on them is to ask for clarification over and over. If your report is shorter than two lines, it is almost certainly missing some of these, which makes it hard for us to respond to it. We're often too polite to close the issue outright, but the missing info makes misinterpretation likely. As a commiter myself, I often get frustrated by these issues, since the only possible way for me to move forward on them is to ask for clarification over and over.
For bug reports, this means that your report should contain the *complete* output of youtube-dl when called with the -v flag. The error message you get for (most) bugs even says so, but you would not believe how many of our bug reports do not contain this information. For bug reports, this means that your report should contain the *complete* output of youtube-dl when called with the `-v` flag. The error message you get for (most) bugs even says so, but you would not believe how many of our bug reports do not contain this information.
If your server has multiple IPs or you suspect censorship, adding --call-home may be a good idea to get more diagnostics. If the error is `ERROR: Unable to extract ...` and you cannot reproduce it from multiple countries, add `--dump-pages` (warning: this will yield a rather large output, redirect it to the file `log.txt` by adding `>log.txt 2>&1` to your command-line) or upload the `.dump` files you get when you add `--write-pages` [somewhere](https://gist.github.com/). If your server has multiple IPs or you suspect censorship, adding `--call-home` may be a good idea to get more diagnostics. If the error is `ERROR: Unable to extract ...` and you cannot reproduce it from multiple countries, add `--dump-pages` (warning: this will yield a rather large output, redirect it to the file `log.txt` by adding `>log.txt 2>&1` to your command-line) or upload the `.dump` files you get when you add `--write-pages` [somewhere](https://gist.github.com/).
**Site support requests must contain an example URL**. An example URL is a URL you might want to download, like http://www.youtube.com/watch?v=BaW_jenozKc . There should be an obvious video present. Except under very special circumstances, the main page of a video service (e.g. http://www.youtube.com/ ) is *not* an example URL. **Site support requests must contain an example URL**. An example URL is a URL you might want to download, like `http://www.youtube.com/watch?v=BaW_jenozKc`. There should be an obvious video present. Except under very special circumstances, the main page of a video service (e.g. `http://www.youtube.com/`) is *not* an example URL.
### Are you using the latest version? ### Are you using the latest version?
Before reporting any issue, type youtube-dl -U. This should report that you're up-to-date. About 20% of the reports we receive are already fixed, but people are using outdated versions. This goes for feature requests as well. Before reporting any issue, type `youtube-dl -U`. This should report that you're up-to-date. About 20% of the reports we receive are already fixed, but people are using outdated versions. This goes for feature requests as well.
### Is the issue already documented? ### Is the issue already documented?
Make sure that someone has not already opened the issue you're trying to open. Search at the top of the window or at https://github.com/rg3/youtube-dl/search?type=Issues . If there is an issue, feel free to write something along the lines of "This affects me as well, with version 2015.01.01. Here is some more information on the issue: ...". While some issues may be old, a new post into them often spurs rapid activity. Make sure that someone has not already opened the issue you're trying to open. Search at the top of the window or browse the [GitHub Issues](https://github.com/rg3/youtube-dl/search?type=Issues) of this repository. If there is an issue, feel free to write something along the lines of "This affects me as well, with version 2015.01.01. Here is some more information on the issue: ...". While some issues may be old, a new post into them often spurs rapid activity.
### Why are existing options not enough? ### Why are existing options not enough?
@@ -675,4 +874,4 @@ It may sound strange, but some bug reports we receive are completely unrelated t
youtube-dl is released into the public domain by the copyright holders. 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. This README file was originally written by [Daniel Bolton](https://github.com/dbbolton) and is likewise released into the public domain.

View File

@@ -5,7 +5,7 @@ import os
from os.path import dirname as dirn from os.path import dirname as dirn
import sys import sys
sys.path.append(dirn(dirn((os.path.abspath(__file__))))) sys.path.insert(0, dirn(dirn((os.path.abspath(__file__)))))
import youtube_dl import youtube_dl
BASH_COMPLETION_FILE = "youtube-dl.bash-completion" BASH_COMPLETION_FILE = "youtube-dl.bash-completion"

View File

@@ -6,7 +6,7 @@ import os
from os.path import dirname as dirn from os.path import dirname as dirn
import sys import sys
sys.path.append(dirn(dirn((os.path.abspath(__file__))))) sys.path.insert(0, dirn(dirn((os.path.abspath(__file__)))))
import youtube_dl import youtube_dl
from youtube_dl.utils import shell_quote from youtube_dl.utils import shell_quote

View File

@@ -6,7 +6,7 @@ import os
import textwrap import textwrap
# We must be able to import youtube_dl # We must be able to import youtube_dl
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
import youtube_dl import youtube_dl

View File

@@ -9,7 +9,7 @@ import sys
# Import youtube_dl # Import youtube_dl
ROOT_DIR = os.path.join(os.path.dirname(__file__), '..') ROOT_DIR = os.path.join(os.path.dirname(__file__), '..')
sys.path.append(ROOT_DIR) sys.path.insert(0, ROOT_DIR)
import youtube_dl import youtube_dl

View File

@@ -8,6 +8,35 @@ import re
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
README_FILE = os.path.join(ROOT_DIR, 'README.md') README_FILE = os.path.join(ROOT_DIR, 'README.md')
def filter_options(readme):
ret = ''
in_options = False
for line in readme.split('\n'):
if line.startswith('# '):
if line[2:].startswith('OPTIONS'):
in_options = True
else:
in_options = False
if in_options:
if line.lstrip().startswith('-'):
option, description = re.split(r'\s{2,}', line.lstrip())
split_option = option.split(' ')
if not split_option[-1].startswith('-'): # metavar
option = ' '.join(split_option[:-1] + ['*%s*' % split_option[-1]])
# Pandoc's definition_lists. See http://pandoc.org/README.html
# for more information.
ret += '\n%s\n: %s\n' % (option, description)
else:
ret += line.lstrip() + '\n'
else:
ret += line + '\n'
return ret
with io.open(README_FILE, encoding='utf-8') as f: with io.open(README_FILE, encoding='utf-8') as f:
readme = f.read() readme = f.read()
@@ -26,6 +55,8 @@ readme = re.sub(r'(?s)^.*?(?=# DESCRIPTION)', '', readme)
readme = re.sub(r'\s+youtube-dl \[OPTIONS\] URL \[URL\.\.\.\]', '', readme) readme = re.sub(r'\s+youtube-dl \[OPTIONS\] URL \[URL\.\.\.\]', '', readme)
readme = PREFIX + readme readme = PREFIX + readme
readme = filter_options(readme)
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
print(readme.encode('utf-8')) print(readme.encode('utf-8'))
else: else:

View File

@@ -5,7 +5,7 @@ import os
from os.path import dirname as dirn from os.path import dirname as dirn
import sys import sys
sys.path.append(dirn(dirn((os.path.abspath(__file__))))) sys.path.insert(0, dirn(dirn((os.path.abspath(__file__)))))
import youtube_dl import youtube_dl
ZSH_COMPLETION_FILE = "youtube-dl.zsh" ZSH_COMPLETION_FILE = "youtube-dl.zsh"

View File

@@ -15,8 +15,12 @@
- **abc.net.au** - **abc.net.au**
- **Abc7News** - **Abc7News**
- **AcademicEarth:Course** - **AcademicEarth:Course**
- **acast**
- **acast:channel**
- **AddAnime** - **AddAnime**
- **AdobeTV** - **AdobeTV**
- **AdobeTVChannel**
- **AdobeTVShow**
- **AdobeTVVideo** - **AdobeTVVideo**
- **AdultSwim** - **AdultSwim**
- **Aftenposten** - **Aftenposten**
@@ -43,6 +47,7 @@
- **arte.tv:future** - **arte.tv:future**
- **AtresPlayer** - **AtresPlayer**
- **ATTTechChannel** - **ATTTechChannel**
- **AudiMedia**
- **audiomack** - **audiomack**
- **audiomack:album** - **audiomack:album**
- **Azubu** - **Azubu**
@@ -53,6 +58,7 @@
- **Bandcamp:album** - **Bandcamp:album**
- **bbc**: BBC - **bbc**: BBC
- **bbc.co.uk**: BBC iPlayer - **bbc.co.uk**: BBC iPlayer
- **bbc.co.uk:article**: BBC articles
- **BeatportPro** - **BeatportPro**
- **Beeg** - **Beeg**
- **BehindKink** - **BehindKink**
@@ -66,7 +72,8 @@
- **Bpb**: Bundeszentrale für politische Bildung - **Bpb**: Bundeszentrale für politische Bildung
- **BR**: Bayerischer Rundfunk Mediathek - **BR**: Bayerischer Rundfunk Mediathek
- **Break** - **Break**
- **Brightcove** - **brightcove:legacy**
- **brightcove:new**
- **bt:article**: Bergens Tidende Articles - **bt:article**: Bergens Tidende Articles
- **bt:vestlendingen**: Bergens Tidende - Vestlendingen - **bt:vestlendingen**: Bergens Tidende - Vestlendingen
- **BuzzFeed** - **BuzzFeed**
@@ -81,16 +88,19 @@
- **CBSSports** - **CBSSports**
- **CeskaTelevize** - **CeskaTelevize**
- **channel9**: Channel 9 - **channel9**: Channel 9
- **Chaturbate**
- **Chilloutzone** - **Chilloutzone**
- **chirbit** - **chirbit**
- **chirbit:profile** - **chirbit:profile**
- **Cinchcast** - **Cinchcast**
- **Cinemassacre** - **Cinemassacre**
- **clipfish** - **Clipfish**
- **cliphunter** - **cliphunter**
- **Clipsyndicate** - **Clipsyndicate**
- **cloudtime**: CloudTime
- **Cloudy** - **Cloudy**
- **Clubic** - **Clubic**
- **Clyp**
- **cmt.com** - **cmt.com**
- **CNET** - **CNET**
- **CNN** - **CNN**
@@ -101,7 +111,7 @@
- **ComCarCoff** - **ComCarCoff**
- **ComedyCentral** - **ComedyCentral**
- **ComedyCentralShows**: The Daily Show / The Colbert Report - **ComedyCentralShows**: The Daily Show / The Colbert Report
- **CondeNast**: Condé Nast media group: Condé Nast, GQ, Glamour, Vanity Fair, Vogue, W Magazine, WIRED - **CondeNast**: Condé Nast media group: Allure, Architectural Digest, Ars Technica, Bon Appétit, Brides, Condé Nast, Condé Nast Traveler, Details, Epicurious, GQ, Glamour, Golf Digest, SELF, Teen Vogue, The New Yorker, Vanity Fair, Vogue, W Magazine, WIRED
- **Cracked** - **Cracked**
- **Criterion** - **Criterion**
- **CrooksAndLiars** - **CrooksAndLiars**
@@ -116,14 +126,16 @@
- **DailymotionCloud** - **DailymotionCloud**
- **daum.net** - **daum.net**
- **DBTV** - **DBTV**
- **DCN**
- **DctpTv** - **DctpTv**
- **DeezerPlaylist** - **DeezerPlaylist**
- **defense.gouv.fr** - **defense.gouv.fr**
- **democracynow**
- **DHM**: Filmarchiv - Deutsches Historisches Museum - **DHM**: Filmarchiv - Deutsches Historisches Museum
- **Discovery** - **Discovery**
- **divxstage**: DivxStage
- **Dotsub** - **Dotsub**
- **DouyuTV**: 斗鱼 - **DouyuTV**: 斗鱼
- **DPlay**
- **dramafever** - **dramafever**
- **dramafever:series** - **dramafever:series**
- **DRBonanza** - **DRBonanza**
@@ -149,6 +161,8 @@
- **EroProfile** - **EroProfile**
- **Escapist** - **Escapist**
- **ESPN** (Currently broken) - **ESPN** (Currently broken)
- **EsriVideo**
- **Europa**
- **EveryonesMixtape** - **EveryonesMixtape**
- **exfm**: ex.fm - **exfm**: ex.fm
- **ExpoTV** - **ExpoTV**
@@ -156,15 +170,15 @@
- **facebook** - **facebook**
- **faz.net** - **faz.net**
- **fc2** - **fc2**
- **Fczenit**
- **fernsehkritik.tv** - **fernsehkritik.tv**
- **fernsehkritik.tv:postecke**
- **Firstpost** - **Firstpost**
- **FiveTV** - **FiveTV**
- **Flickr** - **Flickr**
- **Folketinget**: Folketinget (ft.dk; Danish parliament) - **Folketinget**: Folketinget (ft.dk; Danish parliament)
- **FootyRoom** - **FootyRoom**
- **Foxgay** - **Foxgay**
- **FoxNews** - **FoxNews**: Fox News and Fox Business Video
- **FoxSports** - **FoxSports**
- **france2.fr:generation-quoi** - **france2.fr:generation-quoi**
- **FranceCulture** - **FranceCulture**
@@ -174,7 +188,9 @@
- **Freesound** - **Freesound**
- **freespeech.org** - **freespeech.org**
- **FreeVideo** - **FreeVideo**
- **Funimation**
- **FunnyOrDie** - **FunnyOrDie**
- **GameInformer**
- **Gamekings** - **Gamekings**
- **GameOne** - **GameOne**
- **gameone:playlist** - **gameone:playlist**
@@ -190,10 +206,10 @@
- **Giga** - **Giga**
- **Glide**: Glide mobile video messages (glide.me) - **Glide**: Glide mobile video messages (glide.me)
- **Globo** - **Globo**
- **GloboArticle**
- **GodTube** - **GodTube**
- **GoldenMoustache** - **GoldenMoustache**
- **Golem** - **Golem**
- **GorillaVid**: GorillaVid.in, daclips.in, movpod.in, fastvideo.in and realvid.net
- **Goshgay** - **Goshgay**
- **Groupon** - **Groupon**
- **Hark** - **Hark**
@@ -207,7 +223,6 @@
- **hitbox** - **hitbox**
- **hitbox:live** - **hitbox:live**
- **HornBunny** - **HornBunny**
- **HostingBulk**
- **HotNewHipHop** - **HotNewHipHop**
- **Howcast** - **Howcast**
- **HowStuffWorks** - **HowStuffWorks**
@@ -218,7 +233,10 @@
- **imdb**: Internet Movie Database trailers - **imdb**: Internet Movie Database trailers
- **imdb:list**: Internet Movie Database lists - **imdb:list**: Internet Movie Database lists
- **Imgur** - **Imgur**
- **ImgurAlbum**
- **Ina** - **Ina**
- **Indavideo**
- **IndavideoEmbed**
- **InfoQ** - **InfoQ**
- **Instagram** - **Instagram**
- **instagram:user**: Instagram user profile - **instagram:user**: Instagram user profile
@@ -261,6 +279,9 @@
- **Libsyn** - **Libsyn**
- **life:embed** - **life:embed**
- **lifenews**: LIFE | NEWS - **lifenews**: LIFE | NEWS
- **limelight**
- **limelight:channel**
- **limelight:channel_list**
- **LiveLeak** - **LiveLeak**
- **livestream** - **livestream**
- **livestream:original** - **livestream:original**
@@ -272,16 +293,15 @@
- **macgamestore**: MacGameStore trailers - **macgamestore**: MacGameStore trailers
- **mailru**: Видео@Mail.Ru - **mailru**: Видео@Mail.Ru
- **Malemotion** - **Malemotion**
- **MDR** - **MDR**: MDR.DE and KiKA
- **media.ccc.de** - **media.ccc.de**
- **MegaVideoz**
- **metacafe** - **metacafe**
- **Metacritic** - **Metacritic**
- **Mgoon** - **Mgoon**
- **Minhateca** - **Minhateca**
- **MinistryGrid** - **MinistryGrid**
- **miomio.tv** - **miomio.tv**
- **mitele.es** - **MiTele**: mitele.es
- **mixcloud** - **mixcloud**
- **MLB** - **MLB**
- **MoeVideo**: LetitBit video services: moevideo.net, playreplay.net and videochart.net - **MoeVideo**: LetitBit video services: moevideo.net, playreplay.net and videochart.net
@@ -295,22 +315,22 @@
- **MovieClips** - **MovieClips**
- **MovieFap** - **MovieFap**
- **Moviezine** - **Moviezine**
- **movshare**: MovShare
- **MPORA** - **MPORA**
- **MSNBC**
- **MTV** - **MTV**
- **mtv.de**
- **mtviggy.com** - **mtviggy.com**
- **mtvservices:embedded** - **mtvservices:embedded**
- **MuenchenTV**: münchen.tv - **MuenchenTV**: münchen.tv
- **MusicPlayOn** - **MusicPlayOn**
- **MusicVault**
- **muzu.tv** - **muzu.tv**
- **Mwave**
- **MySpace** - **MySpace**
- **MySpace:album** - **MySpace:album**
- **MySpass** - **MySpass**
- **Myvi** - **Myvi**
- **myvideo** - **myvideo**
- **MyVidster** - **MyVidster**
- **N-JOY**
- **n-tv.de** - **n-tv.de**
- **NationalGeographic** - **NationalGeographic**
- **Naver** - **Naver**
@@ -319,7 +339,9 @@
- **NBCNews** - **NBCNews**
- **NBCSports** - **NBCSports**
- **NBCSportsVPlayer** - **NBCSportsVPlayer**
- **ndr**: NDR.de - Mediathek - **ndr**: NDR.de - Norddeutscher Rundfunk
- **ndr:embed**
- **ndr:embed:base**
- **NDTV** - **NDTV**
- **NerdCubedFeed** - **NerdCubedFeed**
- **Nerdist** - **Nerdist**
@@ -342,16 +364,20 @@
- **nhl.com:videocenter**: NHL videocenter category - **nhl.com:videocenter**: NHL videocenter category
- **niconico**: ニコニコ動画 - **niconico**: ニコニコ動画
- **NiconicoPlaylist** - **NiconicoPlaylist**
- **njoy**: N-JOY
- **njoy:embed**
- **Noco** - **Noco**
- **Normalboots** - **Normalboots**
- **NosVideo** - **NosVideo**
- **Nova**: TN.cz, Prásk.tv, Nova.cz, Novaplus.cz, FANDA.tv, Krásná.cz and Doma.cz - **Nova**: TN.cz, Prásk.tv, Nova.cz, Novaplus.cz, FANDA.tv, Krásná.cz and Doma.cz
- **novamov**: NovaMov - **novamov**: NovaMov
- **Nowness** - **nowness**
- **nowness:playlist**
- **nowness:series**
- **NowTV** - **NowTV**
- **NowTVList**
- **nowvideo**: NowVideo - **nowvideo**: NowVideo
- **npo**: npo.nl and ntr.nl - **npo**: npo.nl and ntr.nl
- **npo**: npo.nl and ntr.nl
- **npo.nl:live** - **npo.nl:live**
- **npo.nl:radio** - **npo.nl:radio**
- **npo.nl:radio:fragment** - **npo.nl:radio:fragment**
@@ -369,14 +395,14 @@
- **OnionStudios** - **OnionStudios**
- **Ooyala** - **Ooyala**
- **OoyalaExternal** - **OoyalaExternal**
- **OpenFilm**
- **orf:fm4**: radio FM4 - **orf:fm4**: radio FM4
- **orf:iptv**: iptv.ORF.at - **orf:iptv**: iptv.ORF.at
- **orf:oe1**: Radio Österreich 1 - **orf:oe1**: Radio Österreich 1
- **orf:tvthek**: ORF TVthek - **orf:tvthek**: ORF TVthek
- **parliamentlive.tv**: UK parliament videos - **parliamentlive.tv**: UK parliament videos
- **Patreon** - **Patreon**
- **PBS** - **pbs**: Public Broadcasting Service (PBS) and member stations: PBS: Public Broadcasting Service, APT - Alabama Public Television (WBIQ), GPB/Georgia Public Broadcasting (WGTV), Mississippi Public Broadcasting (WMPN), Nashville Public Television (WNPT), WFSU-TV (WFSU), WSRE (WSRE), WTCI (WTCI), WPBA/Channel 30 (WPBA), Alaska Public Media (KAKM), Arizona PBS (KAET), KNME-TV/Channel 5 (KNME), Vegas PBS (KLVX), AETN/ARKANSAS ETV NETWORK (KETS), KET (WKLE), WKNO/Channel 10 (WKNO), LPB/LOUISIANA PUBLIC BROADCASTING (WLPB), OETA (KETA), Ozarks Public Television (KOZK), WSIU Public Broadcasting (WSIU), KEET TV (KEET), KIXE/Channel 9 (KIXE), KPBS San Diego (KPBS), KQED (KQED), KVIE Public Television (KVIE), PBS SoCal/KOCE (KOCE), ValleyPBS (KVPT), CONNECTICUT PUBLIC TELEVISION (WEDH), KNPB Channel 5 (KNPB), SOPTV (KSYS), Rocky Mountain PBS (KRMA), KENW-TV3 (KENW), KUED Channel 7 (KUED), Wyoming PBS (KCWC), Colorado Public Television / KBDI 12 (KBDI), KBYU-TV (KBYU), Thirteen/WNET New York (WNET), WGBH/Channel 2 (WGBH), WGBY (WGBY), NJTV Public Media NJ (WNJT), WLIW21 (WLIW), mpt/Maryland Public Television (WMPB), WETA Television and Radio (WETA), WHYY (WHYY), PBS 39 (WLVT), WVPT - Your Source for PBS and More! (WVPT), Howard University Television (WHUT), WEDU PBS (WEDU), WGCU Public Media (WGCU), WPBT2 (WPBT), WUCF TV (WUCF), WUFT/Channel 5 (WUFT), WXEL/Channel 42 (WXEL), WLRN/Channel 17 (WLRN), WUSF Public Broadcasting (WUSF), ETV (WRLK), UNC-TV (WUNC), PBS Hawaii - Oceanic Cable Channel 10 (KHET), Idaho Public Television (KAID), KSPS (KSPS), OPB (KOPB), KWSU/Channel 10 & KTNW/Channel 31 (KWSU), WILL-TV (WILL), Network Knowledge - WSEC/Springfield (WSEC), WTTW11 (WTTW), Iowa Public Television/IPTV (KDIN), Nine Network (KETC), PBS39 Fort Wayne (WFWA), WFYI Indianapolis (WFYI), Milwaukee Public Television (WMVS), WNIN (WNIN), WNIT Public Television (WNIT), WPT (WPNE), WVUT/Channel 22 (WVUT), WEIU/Channel 51 (WEIU), WQPT-TV (WQPT), WYCC PBS Chicago (WYCC), WIPB-TV (WIPB), WTIU (WTIU), CET (WCET), ThinkTVNetwork (WPTD), WBGU-TV (WBGU), WGVU TV (WGVU), NET1 (KUON), Pioneer Public Television (KWCM), SDPB Television (KUSD), TPT (KTCA), KSMQ (KSMQ), KPTS/Channel 8 (KPTS), KTWU/Channel 11 (KTWU), East Tennessee PBS (WSJK), WCTE-TV (WCTE), WLJT, Channel 11 (WLJT), WOSU TV (WOSU), WOUB/WOUC (WOUB), WVPB (WVPB), WKYU-PBS (WKYU), KERA 13 (KERA), MPBN (WCBB), Mountain Lake PBS (WCFE), NHPTV (WENH), Vermont PBS (WETK), witf (WITF), WQED Multimedia (WQED), WMHT Educational Telecommunications (WMHT), Q-TV (WDCQ), WTVS Detroit Public TV (WTVS), CMU Public Television (WCMU), WKAR-TV (WKAR), WNMU-TV Public TV 13 (WNMU), WDSE - WRPT (WDSE), WGTE TV (WGTE), Lakeland Public Television (KAWE), KMOS-TV - Channels 6.1, 6.2 and 6.3 (KMOS), MontanaPBS (KUSM), KRWG/Channel 22 (KRWG), KACV (KACV), KCOS/Channel 13 (KCOS), WCNY/Channel 24 (WCNY), WNED (WNED), WPBS (WPBS), WSKG Public TV (WSKG), WXXI (WXXI), WPSU (WPSU), WVIA Public Media Studios (WVIA), WTVI (WTVI), Western Reserve PBS (WNEO), WVIZ/PBS ideastream (WVIZ), KCTS 9 (KCTS), Basin PBS (KPBT), KUHT / Channel 8 (KUHT), KLRN (KLRN), KLRU (KLRU), WTJX Channel 12 (WTJX), WCVE PBS (WCVE), KBTC Public Television (KBTC)
- **Periscope**: Periscope
- **PhilharmonieDeParis**: Philharmonie de Paris - **PhilharmonieDeParis**: Philharmonie de Paris
- **Phoenix** - **Phoenix**
- **Photobucket** - **Photobucket**
@@ -385,8 +411,11 @@
- **PlanetaPlay** - **PlanetaPlay**
- **play.fm** - **play.fm**
- **played.to** - **played.to**
- **Playtvak**: Playtvak.cz, iDNES.cz and Lidovky.cz
- **Playvid** - **Playvid**
- **Playwire** - **Playwire**
- **pluralsight**
- **pluralsight:course**
- **plus.google**: Google Plus - **plus.google**: Google Plus
- **pluzz.francetv.fr** - **pluzz.francetv.fr**
- **podomatic** - **podomatic**
@@ -430,6 +459,7 @@
- **rtve.es:alacarta**: RTVE a la carta - **rtve.es:alacarta**: RTVE a la carta
- **rtve.es:infantil**: RTVE infantil - **rtve.es:infantil**: RTVE infantil
- **rtve.es:live**: RTVE.es live streams - **rtve.es:live**: RTVE.es live streams
- **RTVNH**
- **RUHD** - **RUHD**
- **rutube**: Rutube videos - **rutube**: Rutube videos
- **rutube:channel**: Rutube channels - **rutube:channel**: Rutube channels
@@ -453,9 +483,12 @@
- **ServingSys** - **ServingSys**
- **Sexu** - **Sexu**
- **SexyKarma**: Sexy Karma and Watch Indian Porn - **SexyKarma**: Sexy Karma and Watch Indian Porn
- **Shared** - **Shahid**
- **Shared**: shared.sx and vivo.sx
- **ShareSix** - **ShareSix**
- **Sina** - **Sina**
- **skynewsarabia:video**
- **skynewsarabia:video**
- **Slideshare** - **Slideshare**
- **Slutload** - **Slutload**
- **smotri**: Smotri.com - **smotri**: Smotri.com
@@ -470,6 +503,7 @@
- **soompi:show** - **soompi:show**
- **soundcloud** - **soundcloud**
- **soundcloud:playlist** - **soundcloud:playlist**
- **soundcloud:search**: Soundcloud search
- **soundcloud:set** - **soundcloud:set**
- **soundcloud:user** - **soundcloud:user**
- **soundgasm** - **soundgasm**
@@ -496,6 +530,7 @@
- **SSA** - **SSA**
- **stanfordoc**: Stanford Open ClassRoom - **stanfordoc**: Stanford Open ClassRoom
- **Steam** - **Steam**
- **Stitcher**
- **streamcloud.eu** - **streamcloud.eu**
- **StreamCZ** - **StreamCZ**
- **StreetVoice** - **StreetVoice**
@@ -517,7 +552,8 @@
- **techtv.mit.edu** - **techtv.mit.edu**
- **ted** - **ted**
- **TeleBruxelles** - **TeleBruxelles**
- **telecinco.es** - **Telecinco**: telecinco.es, cuatro.com and mediaset.es
- **Telegraaf**
- **TeleMB** - **TeleMB**
- **TeleTask** - **TeleTask**
- **TenPlay** - **TenPlay**
@@ -525,6 +561,7 @@
- **TF1** - **TF1**
- **TheOnion** - **TheOnion**
- **ThePlatform** - **ThePlatform**
- **ThePlatformFeed**
- **TheSixtyOne** - **TheSixtyOne**
- **ThisAmericanLife** - **ThisAmericanLife**
- **ThisAV** - **ThisAV**
@@ -567,7 +604,8 @@
- **twitch:stream** - **twitch:stream**
- **twitch:video** - **twitch:video**
- **twitch:vod** - **twitch:vod**
- **TwitterCard** - **twitter**
- **twitter:card**
- **Ubu** - **Ubu**
- **udemy** - **udemy**
- **udemy:course** - **udemy:course**
@@ -590,10 +628,8 @@
- **Viddler** - **Viddler**
- **video.google:search**: Google Video search - **video.google:search**: Google Video search
- **video.mit.edu** - **video.mit.edu**
- **VideoBam**
- **VideoDetective** - **VideoDetective**
- **videofy.me** - **videofy.me**
- **videolectures.net**
- **VideoMega** - **VideoMega**
- **VideoPremium** - **VideoPremium**
- **VideoTt**: video.tt - Your True Tube - **VideoTt**: video.tt - Your True Tube
@@ -603,6 +639,7 @@
- **vier** - **vier**
- **vier:videos** - **vier:videos**
- **Viewster** - **Viewster**
- **Viidea**
- **viki** - **viki**
- **viki:channel** - **viki:channel**
- **vimeo** - **vimeo**
@@ -618,9 +655,11 @@
- **vine:user** - **vine:user**
- **vk**: VK - **vk**: VK
- **vk:uservideos**: VK - User's Videos - **vk:uservideos**: VK - User's Videos
- **vlive**
- **Vodlocker** - **Vodlocker**
- **VoiceRepublic** - **VoiceRepublic**
- **Vporn** - **Vporn**
- **vpro**: npo.nl and ntr.nl
- **VRT** - **VRT**
- **vube**: Vube.com - **vube**: Vube.com
- **VuClip** - **VuClip**
@@ -635,6 +674,7 @@
- **WebOfStories** - **WebOfStories**
- **WebOfStoriesPlaylist** - **WebOfStoriesPlaylist**
- **Weibo** - **Weibo**
- **wholecloud**: WholeCloud
- **Wimp** - **Wimp**
- **Wistia** - **Wistia**
- **WNL** - **WNL**
@@ -643,6 +683,7 @@
- **WSJ**: Wall Street Journal - **WSJ**: Wall Street Journal
- **XBef** - **XBef**
- **XboxClips** - **XboxClips**
- **XFileShare**: XFileShare based sites: GorillaVid.in, daclips.in, movpod.in, fastvideo.in, realvid.net, filehoot.com and vidto.me
- **XHamster** - **XHamster**
- **XHamsterEmbed** - **XHamsterEmbed**
- **XMinus** - **XMinus**
@@ -677,6 +718,7 @@
- **youtube:show**: YouTube.com (multi-season) shows - **youtube:show**: YouTube.com (multi-season) shows
- **youtube:subscriptions**: YouTube.com subscriptions feed, "ytsubs" keyword (requires authentication) - **youtube:subscriptions**: YouTube.com subscriptions feed, "ytsubs" keyword (requires authentication)
- **youtube:user**: YouTube.com user videos (URL or "ytuser" keyword) - **youtube:user**: YouTube.com user videos (URL or "ytuser" keyword)
- **youtube:user:playlists**: YouTube.com user playlists
- **youtube:watchlater**: Youtube watch later list, ":ytwatchlater" for short (requires authentication) - **youtube:watchlater**: Youtube watch later list, ":ytwatchlater" for short (requires authentication)
- **Zapiks** - **Zapiks**
- **ZDF** - **ZDF**

View File

@@ -28,7 +28,7 @@ py2exe_options = {
"compressed": 1, "compressed": 1,
"optimize": 2, "optimize": 2,
"dist_dir": '.', "dist_dir": '.',
"dll_excludes": ['w9xpopen.exe'], "dll_excludes": ['w9xpopen.exe', 'crypt32.dll'],
} }
py2exe_console = [{ py2exe_console = [{

View File

@@ -89,66 +89,81 @@ def gettestcases(include_onlymatching=False):
md5 = lambda s: hashlib.md5(s.encode('utf-8')).hexdigest() md5 = lambda s: hashlib.md5(s.encode('utf-8')).hexdigest()
def expect_info_dict(self, got_dict, expected_dict): def expect_value(self, got, expected, field):
if isinstance(expected, compat_str) and expected.startswith('re:'):
match_str = expected[len('re:'):]
match_rex = re.compile(match_str)
self.assertTrue(
isinstance(got, compat_str),
'Expected a %s object, but got %s for field %s' % (
compat_str.__name__, type(got).__name__, field))
self.assertTrue(
match_rex.match(got),
'field %s (value: %r) should match %r' % (field, got, match_str))
elif isinstance(expected, compat_str) and expected.startswith('startswith:'):
start_str = expected[len('startswith:'):]
self.assertTrue(
isinstance(got, compat_str),
'Expected a %s object, but got %s for field %s' % (
compat_str.__name__, type(got).__name__, field))
self.assertTrue(
got.startswith(start_str),
'field %s (value: %r) should start with %r' % (field, got, start_str))
elif isinstance(expected, compat_str) and expected.startswith('contains:'):
contains_str = expected[len('contains:'):]
self.assertTrue(
isinstance(got, compat_str),
'Expected a %s object, but got %s for field %s' % (
compat_str.__name__, type(got).__name__, field))
self.assertTrue(
contains_str in got,
'field %s (value: %r) should contain %r' % (field, got, contains_str))
elif isinstance(expected, type):
self.assertTrue(
isinstance(got, expected),
'Expected type %r for field %s, but got value %r of type %r' % (expected, field, got, type(got)))
elif isinstance(expected, dict) and isinstance(got, dict):
expect_dict(self, got, expected)
elif isinstance(expected, list) and isinstance(got, list):
self.assertEqual(
len(expected), len(got),
'Expect a list of length %d, but got a list of length %d for field %s' % (
len(expected), len(got), field))
for index, (item_got, item_expected) in enumerate(zip(got, expected)):
type_got = type(item_got)
type_expected = type(item_expected)
self.assertEqual(
type_expected, type_got,
'Type mismatch for list item at index %d for field %s, expected %r, got %r' % (
index, field, type_expected, type_got))
expect_value(self, item_got, item_expected, field)
else:
if isinstance(expected, compat_str) and expected.startswith('md5:'):
got = 'md5:' + md5(got)
elif isinstance(expected, compat_str) and expected.startswith('mincount:'):
self.assertTrue(
isinstance(got, (list, dict)),
'Expected field %s to be a list or a dict, but it is of type %s' % (
field, type(got).__name__))
expected_num = int(expected.partition(':')[2])
assertGreaterEqual(
self, len(got), expected_num,
'Expected %d items in field %s, but only got %d' % (expected_num, field, len(got)))
return
self.assertEqual(
expected, got,
'Invalid value for field %s, expected %r, got %r' % (field, expected, got))
def expect_dict(self, got_dict, expected_dict):
for info_field, expected in expected_dict.items(): for info_field, expected in expected_dict.items():
if isinstance(expected, compat_str) and expected.startswith('re:'): got = got_dict.get(info_field)
got = got_dict.get(info_field) expect_value(self, got, expected, info_field)
match_str = expected[len('re:'):]
match_rex = re.compile(match_str)
self.assertTrue(
isinstance(got, compat_str),
'Expected a %s object, but got %s for field %s' % (
compat_str.__name__, type(got).__name__, info_field))
self.assertTrue(
match_rex.match(got),
'field %s (value: %r) should match %r' % (info_field, got, match_str))
elif isinstance(expected, compat_str) and expected.startswith('startswith:'):
got = got_dict.get(info_field)
start_str = expected[len('startswith:'):]
self.assertTrue(
isinstance(got, compat_str),
'Expected a %s object, but got %s for field %s' % (
compat_str.__name__, type(got).__name__, info_field))
self.assertTrue(
got.startswith(start_str),
'field %s (value: %r) should start with %r' % (info_field, got, start_str))
elif isinstance(expected, compat_str) and expected.startswith('contains:'):
got = got_dict.get(info_field)
contains_str = expected[len('contains:'):]
self.assertTrue(
isinstance(got, compat_str),
'Expected a %s object, but got %s for field %s' % (
compat_str.__name__, type(got).__name__, info_field))
self.assertTrue(
contains_str in got,
'field %s (value: %r) should contain %r' % (info_field, got, contains_str))
elif isinstance(expected, type):
got = got_dict.get(info_field)
self.assertTrue(isinstance(got, expected),
'Expected type %r for field %s, but got value %r of type %r' % (expected, info_field, got, type(got)))
else:
if isinstance(expected, compat_str) and expected.startswith('md5:'):
got = 'md5:' + md5(got_dict.get(info_field))
elif isinstance(expected, compat_str) and expected.startswith('mincount:'):
got = got_dict.get(info_field)
self.assertTrue(
isinstance(got, list),
'Expected field %s to be a list, but it is of type %s' % (
info_field, type(got).__name__))
expected_num = int(expected.partition(':')[2])
assertGreaterEqual(
self, len(got), expected_num,
'Expected %d items in field %s, but only got %d' % (
expected_num, info_field, len(got)
)
)
continue
else:
got = got_dict.get(info_field)
self.assertEqual(expected, got,
'invalid value for field %s, expected %r, got %r' % (info_field, expected, got))
def expect_info_dict(self, got_dict, expected_dict):
expect_dict(self, got_dict, expected_dict)
# Check for the presence of mandatory fields # Check for the presence of mandatory fields
if got_dict.get('_type') not in ('playlist', 'multi_video'): if got_dict.get('_type') not in ('playlist', 'multi_video'):
for key in ('id', 'url', 'title', 'ext'): for key in ('id', 'url', 'title', 'ext'):
@@ -160,7 +175,7 @@ def expect_info_dict(self, got_dict, expected_dict):
# Are checkable fields missing from the test case definition? # Are checkable fields missing from the test case definition?
test_info_dict = dict((key, value if not isinstance(value, compat_str) or len(value) < 250 else 'md5:' + md5(value)) test_info_dict = dict((key, value if not isinstance(value, compat_str) or len(value) < 250 else 'md5:' + md5(value))
for key, value in got_dict.items() for key, value in got_dict.items()
if value and key in ('id', 'title', 'description', 'uploader', 'upload_date', 'timestamp', 'uploader_id', 'location')) if value and key in ('id', 'title', 'description', 'uploader', 'upload_date', 'timestamp', 'uploader_id', 'location', 'age_limit'))
missing_keys = set(test_info_dict.keys()) - set(expected_dict.keys()) missing_keys = set(test_info_dict.keys()) - set(expected_dict.keys())
if missing_keys: if missing_keys:
def _repr(v): def _repr(v):

View File

@@ -35,10 +35,18 @@ class TestInfoExtractor(unittest.TestCase):
<meta name="og:title" content='Foo'/> <meta name="og:title" content='Foo'/>
<meta content="Some video's description " name="og:description"/> <meta content="Some video's description " name="og:description"/>
<meta property='og:image' content='http://domain.com/pic.jpg?key1=val1&amp;key2=val2'/> <meta property='og:image' content='http://domain.com/pic.jpg?key1=val1&amp;key2=val2'/>
<meta content='application/x-shockwave-flash' property='og:video:type'>
<meta content='Foo' property=og:foobar>
<meta name="og:test1" content='foo > < bar'/>
<meta name="og:test2" content="foo >//< bar"/>
''' '''
self.assertEqual(ie._og_search_title(html), 'Foo') self.assertEqual(ie._og_search_title(html), 'Foo')
self.assertEqual(ie._og_search_description(html), 'Some video\'s description ') self.assertEqual(ie._og_search_description(html), 'Some video\'s description ')
self.assertEqual(ie._og_search_thumbnail(html), 'http://domain.com/pic.jpg?key1=val1&key2=val2') self.assertEqual(ie._og_search_thumbnail(html), 'http://domain.com/pic.jpg?key1=val1&key2=val2')
self.assertEqual(ie._og_search_video_url(html, default=None), None)
self.assertEqual(ie._og_search_property('foobar', html), 'Foo')
self.assertEqual(ie._og_search_property('test1', html), 'foo > < bar')
self.assertEqual(ie._og_search_property('test2', html), 'foo >//< bar')
def test_html_search_meta(self): def test_html_search_meta(self):
ie = self.ie ie = self.ie

View File

@@ -121,8 +121,8 @@ class TestAllURLsMatching(unittest.TestCase):
def test_pbs(self): def test_pbs(self):
# https://github.com/rg3/youtube-dl/issues/2350 # https://github.com/rg3/youtube-dl/issues/2350
self.assertMatch('http://video.pbs.org/viralplayer/2365173446/', ['PBS']) self.assertMatch('http://video.pbs.org/viralplayer/2365173446/', ['pbs'])
self.assertMatch('http://video.pbs.org/widget/partnerplayer/980042464/', ['PBS']) self.assertMatch('http://video.pbs.org/widget/partnerplayer/980042464/', ['pbs'])
def test_yahoo_https(self): def test_yahoo_https(self):
# https://github.com/rg3/youtube-dl/issues/2701 # https://github.com/rg3/youtube-dl/issues/2701

View File

@@ -13,7 +13,10 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from youtube_dl.utils import get_filesystem_encoding from youtube_dl.utils import get_filesystem_encoding
from youtube_dl.compat import ( from youtube_dl.compat import (
compat_getenv, compat_getenv,
compat_etree_fromstring,
compat_expanduser, compat_expanduser,
compat_shlex_split,
compat_str,
compat_urllib_parse_unquote, compat_urllib_parse_unquote,
compat_urllib_parse_unquote_plus, compat_urllib_parse_unquote_plus,
) )
@@ -67,5 +70,23 @@ class TestCompat(unittest.TestCase):
self.assertEqual(compat_urllib_parse_unquote_plus('abc%20def'), 'abc def') self.assertEqual(compat_urllib_parse_unquote_plus('abc%20def'), 'abc def')
self.assertEqual(compat_urllib_parse_unquote_plus('%7e/abc+def'), '~/abc def') self.assertEqual(compat_urllib_parse_unquote_plus('%7e/abc+def'), '~/abc def')
def test_compat_shlex_split(self):
self.assertEqual(compat_shlex_split('-option "one two"'), ['-option', 'one two'])
def test_compat_etree_fromstring(self):
xml = '''
<root foo="bar" spam="中文">
<normal>foo</normal>
<chinese>中文</chinese>
<foo><bar>spam</bar></foo>
</root>
'''
doc = compat_etree_fromstring(xml.encode('utf-8'))
self.assertTrue(isinstance(doc.attrib['foo'], compat_str))
self.assertTrue(isinstance(doc.attrib['spam'], compat_str))
self.assertTrue(isinstance(doc.find('normal').text, compat_str))
self.assertTrue(isinstance(doc.find('chinese').text, compat_str))
self.assertTrue(isinstance(doc.find('foo/bar').text, compat_str))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@@ -102,7 +102,7 @@ def generator(test_case):
params = get_params(test_case.get('params', {})) params = get_params(test_case.get('params', {}))
if is_playlist and 'playlist' not in test_case: if is_playlist and 'playlist' not in test_case:
params.setdefault('extract_flat', True) params.setdefault('extract_flat', 'in_playlist')
params.setdefault('skip_download', True) params.setdefault('skip_download', True)
ydl = YoutubeDL(params, auto_init=False) ydl = YoutubeDL(params, auto_init=False)
@@ -136,7 +136,9 @@ def generator(test_case):
# We're not using .download here sine that is just a shim # We're not using .download here sine that is just a shim
# for outside error handling, and returns the exit code # for outside error handling, and returns the exit code
# instead of the result dict. # instead of the result dict.
res_dict = ydl.extract_info(test_case['url']) res_dict = ydl.extract_info(
test_case['url'],
force_generic_extractor=params.get('force_generic_extractor', False))
except (DownloadError, ExtractorError) as err: except (DownloadError, ExtractorError) as err:
# Check if the exception is not a network related one # Check if the exception is not a network related one
if not err.exc_info[0] in (compat_urllib_error.URLError, socket.timeout, UnavailableVideoError, compat_http_client.BadStatusLine) or (err.exc_info[0] == compat_HTTPError and err.exc_info[1].code == 503): if not err.exc_info[0] in (compat_urllib_error.URLError, socket.timeout, UnavailableVideoError, compat_http_client.BadStatusLine) or (err.exc_info[0] == compat_HTTPError and err.exc_info[1].code == 503):

View File

@@ -19,6 +19,9 @@ class TestJSInterpreter(unittest.TestCase):
jsi = JSInterpreter('function x3(){return 42;}') jsi = JSInterpreter('function x3(){return 42;}')
self.assertEqual(jsi.call_function('x3'), 42) self.assertEqual(jsi.call_function('x3'), 42)
jsi = JSInterpreter('var x5 = function(){return 42;}')
self.assertEqual(jsi.call_function('x5'), 42)
def test_calc(self): def test_calc(self):
jsi = JSInterpreter('function x4(a){return 2*a+1;}') jsi = JSInterpreter('function x4(a){return 2*a+1;}')
self.assertEqual(jsi.call_function('x4', 3), 7) self.assertEqual(jsi.call_function('x4', 3), 7)

View File

@@ -25,8 +25,10 @@ from youtube_dl.extractor import (
RaiIE, RaiIE,
VikiIE, VikiIE,
ThePlatformIE, ThePlatformIE,
ThePlatformFeedIE,
RTVEALaCartaIE, RTVEALaCartaIE,
FunnyOrDieIE, FunnyOrDieIE,
DemocracynowIE,
) )
@@ -307,6 +309,18 @@ class TestThePlatformSubtitles(BaseTestSubtitles):
self.assertEqual(md5(subtitles['en']), '97e7670cbae3c4d26ae8bcc7fdd78d4b') self.assertEqual(md5(subtitles['en']), '97e7670cbae3c4d26ae8bcc7fdd78d4b')
class TestThePlatformFeedSubtitles(BaseTestSubtitles):
url = 'http://feed.theplatform.com/f/7wvmTC/msnbc_video-p-test?form=json&pretty=true&range=-40&byGuid=n_hardball_5biden_140207'
IE = ThePlatformFeedIE
def test_allsubtitles(self):
self.DL.params['writesubtitles'] = True
self.DL.params['allsubtitles'] = True
subtitles = self.getSubtitles()
self.assertEqual(set(subtitles.keys()), set(['en']))
self.assertEqual(md5(subtitles['en']), '48649a22e82b2da21c9a67a395eedade')
class TestRtveSubtitles(BaseTestSubtitles): class TestRtveSubtitles(BaseTestSubtitles):
url = 'http://www.rtve.es/alacarta/videos/los-misterios-de-laura/misterios-laura-capitulo-32-misterio-del-numero-17-2-parte/2428621/' url = 'http://www.rtve.es/alacarta/videos/los-misterios-de-laura/misterios-laura-capitulo-32-misterio-del-numero-17-2-parte/2428621/'
IE = RTVEALaCartaIE IE = RTVEALaCartaIE
@@ -333,5 +347,25 @@ class TestFunnyOrDieSubtitles(BaseTestSubtitles):
self.assertEqual(md5(subtitles['en']), 'c5593c193eacd353596c11c2d4f9ecc4') self.assertEqual(md5(subtitles['en']), 'c5593c193eacd353596c11c2d4f9ecc4')
class TestDemocracynowSubtitles(BaseTestSubtitles):
url = 'http://www.democracynow.org/shows/2015/7/3'
IE = DemocracynowIE
def test_allsubtitles(self):
self.DL.params['writesubtitles'] = True
self.DL.params['allsubtitles'] = True
subtitles = self.getSubtitles()
self.assertEqual(set(subtitles.keys()), set(['en']))
self.assertEqual(md5(subtitles['en']), 'acaca989e24a9e45a6719c9b3d60815c')
def test_subtitles_in_page(self):
self.url = 'http://www.democracynow.org/2015/7/3/this_flag_comes_down_today_bree'
self.DL.params['writesubtitles'] = True
self.DL.params['allsubtitles'] = True
subtitles = self.getSubtitles()
self.assertEqual(set(subtitles.keys()), set(['en']))
self.assertEqual(md5(subtitles['en']), 'acaca989e24a9e45a6719c9b3d60815c')
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@@ -21,6 +21,7 @@ from youtube_dl.utils import (
clean_html, clean_html,
DateRange, DateRange,
detect_exe_version, detect_exe_version,
determine_ext,
encodeFilename, encodeFilename,
escape_rfc3986, escape_rfc3986,
escape_url, escape_url,
@@ -57,11 +58,19 @@ from youtube_dl.utils import (
urlencode_postdata, urlencode_postdata,
version_tuple, version_tuple,
xpath_with_ns, xpath_with_ns,
xpath_element,
xpath_text, xpath_text,
xpath_attr,
render_table, render_table,
match_str, match_str,
parse_dfxp_time_expr, parse_dfxp_time_expr,
dfxp2srt, dfxp2srt,
cli_option,
cli_valueless_option,
cli_bool_option,
)
from youtube_dl.compat import (
compat_etree_fromstring,
) )
@@ -202,8 +211,8 @@ class TestUtil(unittest.TestCase):
self.assertEqual(unescapeHTML('%20;'), '%20;') self.assertEqual(unescapeHTML('%20;'), '%20;')
self.assertEqual(unescapeHTML('&#x2F;'), '/') self.assertEqual(unescapeHTML('&#x2F;'), '/')
self.assertEqual(unescapeHTML('&#47;'), '/') self.assertEqual(unescapeHTML('&#47;'), '/')
self.assertEqual( self.assertEqual(unescapeHTML('&eacute;'), 'é')
unescapeHTML('&eacute;'), 'é') self.assertEqual(unescapeHTML('&#2013266066;'), '&#2013266066;')
def test_daterange(self): def test_daterange(self):
_20century = DateRange("19000101", "20000101") _20century = DateRange("19000101", "20000101")
@@ -228,6 +237,14 @@ class TestUtil(unittest.TestCase):
unified_strdate('2/2/2015 6:47:40 PM', day_first=False), unified_strdate('2/2/2015 6:47:40 PM', day_first=False),
'20150202') '20150202')
self.assertEqual(unified_strdate('25-09-2014'), '20140925') self.assertEqual(unified_strdate('25-09-2014'), '20140925')
self.assertEqual(unified_strdate('UNKNOWN DATE FORMAT'), None)
def test_determine_ext(self):
self.assertEqual(determine_ext('http://example.com/foo/bar.mp4/?download'), 'mp4')
self.assertEqual(determine_ext('http://example.com/foo/bar/?download', None), None)
self.assertEqual(determine_ext('http://example.com/foo/bar.nonext/?download', None), None)
self.assertEqual(determine_ext('http://example.com/foo/bar/mp4?download', None), None)
self.assertEqual(determine_ext('http://example.com/foo/bar.m3u8//?download'), 'm3u8')
def test_find_xpath_attr(self): def test_find_xpath_attr(self):
testxml = '''<root> testxml = '''<root>
@@ -237,7 +254,7 @@ class TestUtil(unittest.TestCase):
<node x="b" y="d" /> <node x="b" y="d" />
<node x="" /> <node x="" />
</root>''' </root>'''
doc = xml.etree.ElementTree.fromstring(testxml) doc = compat_etree_fromstring(testxml)
self.assertEqual(find_xpath_attr(doc, './/fourohfour', 'n'), None) self.assertEqual(find_xpath_attr(doc, './/fourohfour', 'n'), None)
self.assertEqual(find_xpath_attr(doc, './/fourohfour', 'n', 'v'), None) self.assertEqual(find_xpath_attr(doc, './/fourohfour', 'n', 'v'), None)
@@ -258,23 +275,56 @@ class TestUtil(unittest.TestCase):
<url>http://server.com/download.mp3</url> <url>http://server.com/download.mp3</url>
</media:song> </media:song>
</root>''' </root>'''
doc = xml.etree.ElementTree.fromstring(testxml) doc = compat_etree_fromstring(testxml)
find = lambda p: doc.find(xpath_with_ns(p, {'media': 'http://example.com/'})) find = lambda p: doc.find(xpath_with_ns(p, {'media': 'http://example.com/'}))
self.assertTrue(find('media:song') is not None) self.assertTrue(find('media:song') is not None)
self.assertEqual(find('media:song/media:author').text, 'The Author') self.assertEqual(find('media:song/media:author').text, 'The Author')
self.assertEqual(find('media:song/url').text, 'http://server.com/download.mp3') self.assertEqual(find('media:song/url').text, 'http://server.com/download.mp3')
def test_xpath_element(self):
doc = xml.etree.ElementTree.Element('root')
div = xml.etree.ElementTree.SubElement(doc, 'div')
p = xml.etree.ElementTree.SubElement(div, 'p')
p.text = 'Foo'
self.assertEqual(xpath_element(doc, 'div/p'), p)
self.assertEqual(xpath_element(doc, ['div/p']), p)
self.assertEqual(xpath_element(doc, ['div/bar', 'div/p']), p)
self.assertEqual(xpath_element(doc, 'div/bar', default='default'), 'default')
self.assertEqual(xpath_element(doc, ['div/bar'], default='default'), 'default')
self.assertTrue(xpath_element(doc, 'div/bar') is None)
self.assertTrue(xpath_element(doc, ['div/bar']) is None)
self.assertTrue(xpath_element(doc, ['div/bar'], 'div/baz') is None)
self.assertRaises(ExtractorError, xpath_element, doc, 'div/bar', fatal=True)
self.assertRaises(ExtractorError, xpath_element, doc, ['div/bar'], fatal=True)
self.assertRaises(ExtractorError, xpath_element, doc, ['div/bar', 'div/baz'], fatal=True)
def test_xpath_text(self): def test_xpath_text(self):
testxml = '''<root> testxml = '''<root>
<div> <div>
<p>Foo</p> <p>Foo</p>
</div> </div>
</root>''' </root>'''
doc = xml.etree.ElementTree.fromstring(testxml) doc = compat_etree_fromstring(testxml)
self.assertEqual(xpath_text(doc, 'div/p'), 'Foo') self.assertEqual(xpath_text(doc, 'div/p'), 'Foo')
self.assertEqual(xpath_text(doc, 'div/bar', default='default'), 'default')
self.assertTrue(xpath_text(doc, 'div/bar') is None) self.assertTrue(xpath_text(doc, 'div/bar') is None)
self.assertRaises(ExtractorError, xpath_text, doc, 'div/bar', fatal=True) self.assertRaises(ExtractorError, xpath_text, doc, 'div/bar', fatal=True)
def test_xpath_attr(self):
testxml = '''<root>
<div>
<p x="a">Foo</p>
</div>
</root>'''
doc = compat_etree_fromstring(testxml)
self.assertEqual(xpath_attr(doc, 'div/p', 'x'), 'a')
self.assertEqual(xpath_attr(doc, 'div/bar', 'x'), None)
self.assertEqual(xpath_attr(doc, 'div/p', 'y'), None)
self.assertEqual(xpath_attr(doc, 'div/bar', 'x', default='default'), 'default')
self.assertEqual(xpath_attr(doc, 'div/p', 'y', default='default'), 'default')
self.assertRaises(ExtractorError, xpath_attr, doc, 'div/bar', 'x', fatal=True)
self.assertRaises(ExtractorError, xpath_attr, doc, 'div/p', 'y', fatal=True)
def test_smuggle_url(self): def test_smuggle_url(self):
data = {"ö": "ö", "abc": [3]} data = {"ö": "ö", "abc": [3]}
url = 'https://foo.bar/baz?x=y#a' url = 'https://foo.bar/baz?x=y#a'
@@ -394,6 +444,8 @@ class TestUtil(unittest.TestCase):
self.assertEqual(parse_iso8601('2014-03-23T22:04:26+0000'), 1395612266) self.assertEqual(parse_iso8601('2014-03-23T22:04:26+0000'), 1395612266)
self.assertEqual(parse_iso8601('2014-03-23T22:04:26Z'), 1395612266) self.assertEqual(parse_iso8601('2014-03-23T22:04:26Z'), 1395612266)
self.assertEqual(parse_iso8601('2014-03-23T22:04:26.1234Z'), 1395612266) self.assertEqual(parse_iso8601('2014-03-23T22:04:26.1234Z'), 1395612266)
self.assertEqual(parse_iso8601('2015-09-29T08:27:31.727'), 1443515251)
self.assertEqual(parse_iso8601('2015-09-29T08-27-31.727'), None)
def test_strip_jsonp(self): def test_strip_jsonp(self):
stripped = strip_jsonp('cb ([ {"id":"532cb",\n\n\n"x":\n3}\n]\n);') stripped = strip_jsonp('cb ([ {"id":"532cb",\n\n\n"x":\n3}\n]\n);')
@@ -464,6 +516,9 @@ class TestUtil(unittest.TestCase):
"playlist":[{"controls":{"all":null}}] "playlist":[{"controls":{"all":null}}]
}''') }''')
inp = '''"The CW\\'s \\'Crazy Ex-Girlfriend\\'"'''
self.assertEqual(js_to_json(inp), '''"The CW's 'Crazy Ex-Girlfriend'"''')
inp = '"SAND Number: SAND 2013-7800P\\nPresenter: Tom Russo\\nHabanero Software Training - Xyce Software\\nXyce, Sandia\\u0027s"' inp = '"SAND Number: SAND 2013-7800P\\nPresenter: Tom Russo\\nHabanero Software Training - Xyce Software\\nXyce, Sandia\\u0027s"'
json_code = js_to_json(inp) json_code = js_to_json(inp)
self.assertEqual(json.loads(json_code), json.loads(inp)) self.assertEqual(json.loads(json_code), json.loads(inp))
@@ -646,6 +701,51 @@ The first line
''' '''
self.assertEqual(dfxp2srt(dfxp_data_no_default_namespace), srt_data) self.assertEqual(dfxp2srt(dfxp_data_no_default_namespace), srt_data)
def test_cli_option(self):
self.assertEqual(cli_option({'proxy': '127.0.0.1:3128'}, '--proxy', 'proxy'), ['--proxy', '127.0.0.1:3128'])
self.assertEqual(cli_option({'proxy': None}, '--proxy', 'proxy'), [])
self.assertEqual(cli_option({}, '--proxy', 'proxy'), [])
def test_cli_valueless_option(self):
self.assertEqual(cli_valueless_option(
{'downloader': 'external'}, '--external-downloader', 'downloader', 'external'), ['--external-downloader'])
self.assertEqual(cli_valueless_option(
{'downloader': 'internal'}, '--external-downloader', 'downloader', 'external'), [])
self.assertEqual(cli_valueless_option(
{'nocheckcertificate': True}, '--no-check-certificate', 'nocheckcertificate'), ['--no-check-certificate'])
self.assertEqual(cli_valueless_option(
{'nocheckcertificate': False}, '--no-check-certificate', 'nocheckcertificate'), [])
self.assertEqual(cli_valueless_option(
{'checkcertificate': True}, '--no-check-certificate', 'checkcertificate', False), [])
self.assertEqual(cli_valueless_option(
{'checkcertificate': False}, '--no-check-certificate', 'checkcertificate', False), ['--no-check-certificate'])
def test_cli_bool_option(self):
self.assertEqual(
cli_bool_option(
{'nocheckcertificate': True}, '--no-check-certificate', 'nocheckcertificate'),
['--no-check-certificate', 'true'])
self.assertEqual(
cli_bool_option(
{'nocheckcertificate': True}, '--no-check-certificate', 'nocheckcertificate', separator='='),
['--no-check-certificate=true'])
self.assertEqual(
cli_bool_option(
{'nocheckcertificate': True}, '--check-certificate', 'nocheckcertificate', 'false', 'true'),
['--check-certificate', 'false'])
self.assertEqual(
cli_bool_option(
{'nocheckcertificate': True}, '--check-certificate', 'nocheckcertificate', 'false', 'true', '='),
['--check-certificate=false'])
self.assertEqual(
cli_bool_option(
{'nocheckcertificate': False}, '--check-certificate', 'nocheckcertificate', 'false', 'true'),
['--check-certificate', 'true'])
self.assertEqual(
cli_bool_option(
{'nocheckcertificate': False}, '--check-certificate', 'nocheckcertificate', 'false', 'true', '='),
['--check-certificate=true'])
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@@ -33,7 +33,7 @@ params = get_params({
TEST_ID = 'gr51aVj-mLg' TEST_ID = 'gr51aVj-mLg'
ANNOTATIONS_FILE = TEST_ID + '.flv.annotations.xml' ANNOTATIONS_FILE = TEST_ID + '.annotations.xml'
EXPECTED_ANNOTATIONS = ['Speech bubble', 'Note', 'Title', 'Spotlight', 'Label'] EXPECTED_ANNOTATIONS = ['Speech bubble', 'Note', 'Title', 'Spotlight', 'Label']

View File

@@ -57,5 +57,14 @@ class TestYoutubeLists(unittest.TestCase):
entries = result['entries'] entries = result['entries']
self.assertEqual(len(entries), 100) self.assertEqual(len(entries), 100)
def test_youtube_flat_playlist_titles(self):
dl = FakeYDL()
dl.params['extract_flat'] = True
ie = YoutubePlaylistIE(dl)
result = ie.extract('https://www.youtube.com/playlist?list=PLwiyx1dc3P2JR9N8gQaQN_BCvlSlap7re')
self.assertIsPlaylist(result)
for entry in result['entries']:
self.assertTrue(entry.get('title'))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@@ -1,5 +1,5 @@
[tox] [tox]
envlist = py26,py27,py33,py34 envlist = py26,py27,py33,py34,py35
[testenv] [testenv]
deps = deps =
nose nose

View File

@@ -28,6 +28,7 @@ if os.name == 'nt':
import ctypes import ctypes
from .compat import ( from .compat import (
compat_basestring,
compat_cookiejar, compat_cookiejar,
compat_expanduser, compat_expanduser,
compat_get_terminal_size, compat_get_terminal_size,
@@ -37,6 +38,7 @@ from .compat import (
compat_tokenize_tokenize, compat_tokenize_tokenize,
compat_urllib_error, compat_urllib_error,
compat_urllib_request, compat_urllib_request,
compat_urllib_request_DataHandler,
) )
from .utils import ( from .utils import (
ContentTooShortError, ContentTooShortError,
@@ -62,6 +64,7 @@ from .utils import (
SameFileError, SameFileError,
sanitize_filename, sanitize_filename,
sanitize_path, sanitize_path,
sanitized_Request,
std_headers, std_headers,
subtitles_filename, subtitles_filename,
UnavailableVideoError, UnavailableVideoError,
@@ -69,6 +72,7 @@ from .utils import (
version_tuple, version_tuple,
write_json_file, write_json_file,
write_string, write_string,
YoutubeDLCookieProcessor,
YoutubeDLHandler, YoutubeDLHandler,
prepend_extension, prepend_extension,
replace_extension, replace_extension,
@@ -154,7 +158,7 @@ class YoutubeDL(object):
writethumbnail: Write the thumbnail image to a file writethumbnail: Write the thumbnail image to a file
write_all_thumbnails: Write all thumbnail formats to files write_all_thumbnails: Write all thumbnail formats to files
writesubtitles: Write the video subtitles to a file writesubtitles: Write the video subtitles to a file
writeautomaticsub: Write the automatic subtitles to a file writeautomaticsub: Write the automatically generated subtitles to a file
allsubtitles: Downloads all the subtitles of the video allsubtitles: Downloads all the subtitles of the video
(requires writesubtitles or writeautomaticsub) (requires writesubtitles or writeautomaticsub)
listsubtitles: Lists all available subtitles for the video listsubtitles: Lists all available subtitles for the video
@@ -284,7 +288,11 @@ class YoutubeDL(object):
self._num_downloads = 0 self._num_downloads = 0
self._screen_file = [sys.stdout, sys.stderr][params.get('logtostderr', False)] self._screen_file = [sys.stdout, sys.stderr][params.get('logtostderr', False)]
self._err_file = sys.stderr self._err_file = sys.stderr
self.params = params self.params = {
# Default parameters
'nocheckcertificate': False,
}
self.params.update(params)
self.cache = Cache(self) self.cache = Cache(self)
if params.get('bidi_workaround', False): if params.get('bidi_workaround', False):
@@ -566,7 +574,7 @@ class YoutubeDL(object):
if v is not None) if v is not None)
template_dict = collections.defaultdict(lambda: 'NA', template_dict) template_dict = collections.defaultdict(lambda: 'NA', template_dict)
outtmpl = sanitize_path(self.params.get('outtmpl', DEFAULT_OUTTMPL)) outtmpl = self.params.get('outtmpl', DEFAULT_OUTTMPL)
tmpl = compat_expanduser(outtmpl) tmpl = compat_expanduser(outtmpl)
filename = tmpl % template_dict filename = tmpl % template_dict
# Temporary fix for #4787 # Temporary fix for #4787
@@ -574,7 +582,7 @@ class YoutubeDL(object):
# to workaround encoding issues with subprocess on python2 @ Windows # to workaround encoding issues with subprocess on python2 @ Windows
if sys.version_info < (3, 0) and sys.platform == 'win32': if sys.version_info < (3, 0) and sys.platform == 'win32':
filename = encodeFilename(filename, True).decode(preferredencoding()) filename = encodeFilename(filename, True).decode(preferredencoding())
return filename return sanitize_path(filename)
except ValueError as err: except ValueError as err:
self.report_error('Error in output template: ' + str(err) + ' (encoding: ' + repr(preferredencoding()) + ')') self.report_error('Error in output template: ' + str(err) + ' (encoding: ' + repr(preferredencoding()) + ')')
return None return None
@@ -827,6 +835,7 @@ class YoutubeDL(object):
extra_info=extra) extra_info=extra)
playlist_results.append(entry_result) playlist_results.append(entry_result)
ie_result['entries'] = playlist_results ie_result['entries'] = playlist_results
self.to_screen('[download] Finished downloading playlist: %s' % playlist)
return ie_result return ie_result
elif result_type == 'compat_list': elif result_type == 'compat_list':
self.report_warning( self.report_warning(
@@ -931,7 +940,7 @@ class YoutubeDL(object):
filter_parts.append(string) filter_parts.append(string)
def _remove_unused_ops(tokens): def _remove_unused_ops(tokens):
# Remove operators that we don't use and join them with the sourrounding strings # Remove operators that we don't use and join them with the surrounding strings
# for example: 'mp4' '-' 'baseline' '-' '16x9' is converted to 'mp4-baseline-16x9' # for example: 'mp4' '-' 'baseline' '-' '16x9' is converted to 'mp4-baseline-16x9'
ALLOWED_OPS = ('/', '+', ',', '(', ')') ALLOWED_OPS = ('/', '+', ',', '(', ')')
last_string, last_start, last_end, last_line = None, None, None, None last_string, last_start, last_end, last_line = None, None, None, None
@@ -1101,6 +1110,12 @@ class YoutubeDL(object):
'contain the video, try using ' 'contain the video, try using '
'"-f %s+%s"' % (format_2, format_1)) '"-f %s+%s"' % (format_2, format_1))
return return
# Formats must be opposite (video+audio)
if formats_info[0].get('acodec') == 'none' and formats_info[1].get('acodec') == 'none':
self.report_error(
'Both formats %s and %s are video-only, you must specify "-f video+audio"'
% (format_1, format_2))
return
output_ext = ( output_ext = (
formats_info[0]['ext'] formats_info[0]['ext']
if self.params.get('merge_output_format') is None if self.params.get('merge_output_format') is None
@@ -1180,7 +1195,7 @@ class YoutubeDL(object):
return res return res
def _calc_cookies(self, info_dict): def _calc_cookies(self, info_dict):
pr = compat_urllib_request.Request(info_dict['url']) pr = sanitized_Request(info_dict['url'])
self.cookiejar.add_cookie_header(pr) self.cookiejar.add_cookie_header(pr)
return pr.get_header('Cookie') return pr.get_header('Cookie')
@@ -1227,13 +1242,20 @@ class YoutubeDL(object):
except (ValueError, OverflowError, OSError): except (ValueError, OverflowError, OSError):
pass pass
subtitles = info_dict.get('subtitles')
if subtitles:
for _, subtitle in subtitles.items():
for subtitle_format in subtitle:
if 'ext' not in subtitle_format:
subtitle_format['ext'] = determine_ext(subtitle_format['url']).lower()
if self.params.get('listsubtitles', False): if self.params.get('listsubtitles', False):
if 'automatic_captions' in info_dict: if 'automatic_captions' in info_dict:
self.list_subtitles(info_dict['id'], info_dict.get('automatic_captions'), 'automatic captions') self.list_subtitles(info_dict['id'], info_dict.get('automatic_captions'), 'automatic captions')
self.list_subtitles(info_dict['id'], info_dict.get('subtitles'), 'subtitles') self.list_subtitles(info_dict['id'], subtitles, 'subtitles')
return return
info_dict['requested_subtitles'] = self.process_subtitles( info_dict['requested_subtitles'] = self.process_subtitles(
info_dict['id'], info_dict.get('subtitles'), info_dict['id'], subtitles,
info_dict.get('automatic_captions')) info_dict.get('automatic_captions'))
# We now pick which formats have to be downloaded # We now pick which formats have to be downloaded
@@ -1857,6 +1879,8 @@ class YoutubeDL(object):
def urlopen(self, req): def urlopen(self, req):
""" Start an HTTP download """ """ Start an HTTP download """
if isinstance(req, compat_basestring):
req = sanitized_Request(req)
return self._opener.open(req, timeout=self._socket_timeout) return self._opener.open(req, timeout=self._socket_timeout)
def print_debug_header(self): def print_debug_header(self):
@@ -1939,8 +1963,7 @@ class YoutubeDL(object):
if os.access(opts_cookiefile, os.R_OK): if os.access(opts_cookiefile, os.R_OK):
self.cookiejar.load() self.cookiejar.load()
cookie_processor = compat_urllib_request.HTTPCookieProcessor( cookie_processor = YoutubeDLCookieProcessor(self.cookiejar)
self.cookiejar)
if opts_proxy is not None: if opts_proxy is not None:
if opts_proxy == '': if opts_proxy == '':
proxies = {} proxies = {}
@@ -1956,8 +1979,9 @@ class YoutubeDL(object):
debuglevel = 1 if self.params.get('debug_printtraffic') else 0 debuglevel = 1 if self.params.get('debug_printtraffic') else 0
https_handler = make_HTTPS_handler(self.params, debuglevel=debuglevel) https_handler = make_HTTPS_handler(self.params, debuglevel=debuglevel)
ydlh = YoutubeDLHandler(self.params, debuglevel=debuglevel) ydlh = YoutubeDLHandler(self.params, debuglevel=debuglevel)
data_handler = compat_urllib_request_DataHandler()
opener = compat_urllib_request.build_opener( opener = compat_urllib_request.build_opener(
proxy_handler, https_handler, cookie_processor, ydlh) proxy_handler, https_handler, cookie_processor, ydlh, data_handler)
# Delete the default user-agent header, which would otherwise apply in # Delete the default user-agent header, which would otherwise apply in
# cases where our custom HTTP handler doesn't come into play # cases where our custom HTTP handler doesn't come into play
@@ -2009,7 +2033,7 @@ class YoutubeDL(object):
(info_dict['extractor'], info_dict['id'], thumb_display_id)) (info_dict['extractor'], info_dict['id'], thumb_display_id))
try: try:
uf = self.urlopen(t['url']) uf = self.urlopen(t['url'])
with open(thumb_filename, 'wb') as thumbf: with open(encodeFilename(thumb_filename), 'wb') as thumbf:
shutil.copyfileobj(uf, thumbf) shutil.copyfileobj(uf, thumbf)
self.to_screen('[%s] %s: Writing thumbnail %sto: %s' % self.to_screen('[%s] %s: Writing thumbnail %sto: %s' %
(info_dict['extractor'], info_dict['id'], thumb_display_id, thumb_filename)) (info_dict['extractor'], info_dict['id'], thumb_display_id, thumb_filename))

View File

@@ -9,7 +9,6 @@ import codecs
import io import io
import os import os
import random import random
import shlex
import sys import sys
@@ -20,6 +19,7 @@ from .compat import (
compat_expanduser, compat_expanduser,
compat_getpass, compat_getpass,
compat_print, compat_print,
compat_shlex_split,
workaround_optparse_bug9161, workaround_optparse_bug9161,
) )
from .utils import ( from .utils import (
@@ -262,10 +262,10 @@ def _real_main(argv=None):
parser.error('setting filesize xattr requested but python-xattr is not available') parser.error('setting filesize xattr requested but python-xattr is not available')
external_downloader_args = None external_downloader_args = None
if opts.external_downloader_args: if opts.external_downloader_args:
external_downloader_args = shlex.split(opts.external_downloader_args) external_downloader_args = compat_shlex_split(opts.external_downloader_args)
postprocessor_args = None postprocessor_args = None
if opts.postprocessor_args: if opts.postprocessor_args:
postprocessor_args = shlex.split(opts.postprocessor_args) postprocessor_args = compat_shlex_split(opts.postprocessor_args)
match_filter = ( match_filter = (
None if opts.match_filter is None None if opts.match_filter is None
else match_filter_func(opts.match_filter)) else match_filter_func(opts.match_filter))
@@ -377,7 +377,7 @@ def _real_main(argv=None):
with YoutubeDL(ydl_opts) as ydl: with YoutubeDL(ydl_opts) as ydl:
# Update version # Update version
if opts.update_self: if opts.update_self:
update_self(ydl.to_screen, opts.verbose) update_self(ydl.to_screen, opts.verbose, ydl._opener)
# Remove cache dir # Remove cache dir
if opts.rm_cachedir: if opts.rm_cachedir:

View File

@@ -11,7 +11,7 @@ if __package__ is None and not hasattr(sys, "frozen"):
# direct call of __main__.py # direct call of __main__.py
import os.path import os.path
path = os.path.realpath(os.path.abspath(__file__)) path = os.path.realpath(os.path.abspath(__file__))
sys.path.append(os.path.dirname(os.path.dirname(path))) sys.path.insert(0, os.path.dirname(os.path.dirname(path)))
import youtube_dl import youtube_dl

View File

@@ -1,15 +1,20 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import binascii
import collections import collections
import email
import getpass import getpass
import io
import optparse import optparse
import os import os
import re import re
import shlex
import shutil import shutil
import socket import socket
import subprocess import subprocess
import sys import sys
import itertools import itertools
import xml.etree.ElementTree
try: try:
@@ -37,6 +42,11 @@ try:
except ImportError: # Python 2 except ImportError: # Python 2
import urlparse as compat_urlparse import urlparse as compat_urlparse
try:
import urllib.response as compat_urllib_response
except ImportError: # Python 2
import urllib as compat_urllib_response
try: try:
import http.cookiejar as compat_cookiejar import http.cookiejar as compat_cookiejar
except ImportError: # Python 2 except ImportError: # Python 2
@@ -79,6 +89,11 @@ try:
except ImportError: except ImportError:
import BaseHTTPServer as compat_http_server import BaseHTTPServer as compat_http_server
try:
compat_str = unicode # Python 2
except NameError:
compat_str = str
try: try:
from urllib.parse import unquote_to_bytes as compat_urllib_parse_unquote_to_bytes from urllib.parse import unquote_to_bytes as compat_urllib_parse_unquote_to_bytes
from urllib.parse import unquote as compat_urllib_parse_unquote from urllib.parse import unquote as compat_urllib_parse_unquote
@@ -99,7 +114,7 @@ except ImportError: # Python 2
# Is it a string-like object? # Is it a string-like object?
string.split string.split
return b'' return b''
if isinstance(string, unicode): if isinstance(string, compat_str):
string = string.encode('utf-8') string = string.encode('utf-8')
bits = string.split(b'%') bits = string.split(b'%')
if len(bits) == 1: if len(bits) == 1:
@@ -150,9 +165,38 @@ except ImportError: # Python 2
return compat_urllib_parse_unquote(string, encoding, errors) return compat_urllib_parse_unquote(string, encoding, errors)
try: try:
compat_str = unicode # Python 2 from urllib.request import DataHandler as compat_urllib_request_DataHandler
except NameError: except ImportError: # Python < 3.4
compat_str = str # Ported from CPython 98774:1733b3bd46db, Lib/urllib/request.py
class compat_urllib_request_DataHandler(compat_urllib_request.BaseHandler):
def data_open(self, req):
# data URLs as specified in RFC 2397.
#
# ignores POSTed data
#
# syntax:
# dataurl := "data:" [ mediatype ] [ ";base64" ] "," data
# mediatype := [ type "/" subtype ] *( ";" parameter )
# data := *urlchar
# parameter := attribute "=" value
url = req.get_full_url()
scheme, data = url.split(":", 1)
mediatype, data = data.split(",", 1)
# even base64 encoded data URLs might be quoted so unquote in any case:
data = compat_urllib_parse_unquote_to_bytes(data)
if mediatype.endswith(";base64"):
data = binascii.a2b_base64(data)
mediatype = mediatype[:-7]
if not mediatype:
mediatype = "text/plain;charset=US-ASCII"
headers = email.message_from_string(
"Content-type: %s\nContent-length: %d\n" % (mediatype, len(data)))
return compat_urllib_response.addinfourl(io.BytesIO(data), headers, url)
try: try:
compat_basestring = basestring # Python 2 compat_basestring = basestring # Python 2
@@ -169,6 +213,43 @@ try:
except ImportError: # Python 2.6 except ImportError: # Python 2.6
from xml.parsers.expat import ExpatError as compat_xml_parse_error from xml.parsers.expat import ExpatError as compat_xml_parse_error
if sys.version_info[0] >= 3:
compat_etree_fromstring = xml.etree.ElementTree.fromstring
else:
# python 2.x tries to encode unicode strings with ascii (see the
# XMLParser._fixtext method)
etree = xml.etree.ElementTree
try:
_etree_iter = etree.Element.iter
except AttributeError: # Python <=2.6
def _etree_iter(root):
for el in root.findall('*'):
yield el
for sub in _etree_iter(el):
yield sub
# on 2.6 XML doesn't have a parser argument, function copied from CPython
# 2.7 source
def _XML(text, parser=None):
if not parser:
parser = etree.XMLParser(target=etree.TreeBuilder())
parser.feed(text)
return parser.close()
def _element_factory(*args, **kwargs):
el = etree.Element(*args, **kwargs)
for k, v in el.items():
if isinstance(v, bytes):
el.set(k, v.decode('utf-8'))
return el
def compat_etree_fromstring(text):
doc = _XML(text, parser=etree.XMLParser(target=etree.TreeBuilder(element_factory=_element_factory)))
for el in _etree_iter(doc):
if el.text is not None and isinstance(el.text, bytes):
el.text = el.text.decode('utf-8')
return doc
try: try:
from urllib.parse import parse_qs as compat_parse_qs from urllib.parse import parse_qs as compat_parse_qs
@@ -227,6 +308,17 @@ except ImportError: # Python < 3.3
return "'" + s.replace("'", "'\"'\"'") + "'" return "'" + s.replace("'", "'\"'\"'") + "'"
if sys.version_info >= (2, 7, 3):
compat_shlex_split = shlex.split
else:
# Working around shlex issue with unicode strings on some python 2
# versions (see http://bugs.python.org/issue1548891)
def compat_shlex_split(s, comments=False, posix=True):
if isinstance(s, compat_str):
s = s.encode('utf-8')
return shlex.split(s, comments, posix)
def compat_ord(c): def compat_ord(c):
if type(c) is int: if type(c) is int:
return c return c
@@ -404,26 +496,32 @@ if hasattr(shutil, 'get_terminal_size'): # Python >= 3.3
else: else:
_terminal_size = collections.namedtuple('terminal_size', ['columns', 'lines']) _terminal_size = collections.namedtuple('terminal_size', ['columns', 'lines'])
def compat_get_terminal_size(): def compat_get_terminal_size(fallback=(80, 24)):
columns = compat_getenv('COLUMNS', None) columns = compat_getenv('COLUMNS')
if columns: if columns:
columns = int(columns) columns = int(columns)
else: else:
columns = None columns = None
lines = compat_getenv('LINES', None) lines = compat_getenv('LINES')
if lines: if lines:
lines = int(lines) lines = int(lines)
else: else:
lines = None lines = None
try: if columns is None or lines is None or columns <= 0 or lines <= 0:
sp = subprocess.Popen( try:
['stty', 'size'], sp = subprocess.Popen(
stdout=subprocess.PIPE, stderr=subprocess.PIPE) ['stty', 'size'],
out, err = sp.communicate() stdout=subprocess.PIPE, stderr=subprocess.PIPE)
lines, columns = map(int, out.split()) out, err = sp.communicate()
except Exception: _lines, _columns = map(int, out.split())
pass except Exception:
_columns, _lines = _terminal_size(*fallback)
if columns is None or columns <= 0:
columns = _columns
if lines is None or lines <= 0:
lines = _lines
return _terminal_size(columns, lines) return _terminal_size(columns, lines)
try: try:
@@ -447,6 +545,7 @@ __all__ = [
'compat_chr', 'compat_chr',
'compat_cookiejar', 'compat_cookiejar',
'compat_cookies', 'compat_cookies',
'compat_etree_fromstring',
'compat_expanduser', 'compat_expanduser',
'compat_get_terminal_size', 'compat_get_terminal_size',
'compat_getenv', 'compat_getenv',
@@ -459,6 +558,7 @@ __all__ = [
'compat_ord', 'compat_ord',
'compat_parse_qs', 'compat_parse_qs',
'compat_print', 'compat_print',
'compat_shlex_split',
'compat_socket_create_connection', 'compat_socket_create_connection',
'compat_str', 'compat_str',
'compat_subprocess_get_DEVNULL', 'compat_subprocess_get_DEVNULL',
@@ -470,6 +570,8 @@ __all__ = [
'compat_urllib_parse_unquote_to_bytes', 'compat_urllib_parse_unquote_to_bytes',
'compat_urllib_parse_urlparse', 'compat_urllib_parse_urlparse',
'compat_urllib_request', 'compat_urllib_request',
'compat_urllib_request_DataHandler',
'compat_urllib_response',
'compat_urlparse', 'compat_urlparse',
'compat_urlretrieve', 'compat_urlretrieve',
'compat_xml_parse_error', 'compat_xml_parse_error',

View File

@@ -42,7 +42,7 @@ class FileDownloader(object):
min_filesize: Skip files smaller than this size min_filesize: Skip files smaller than this size
max_filesize: Skip files larger than this size max_filesize: Skip files larger than this size
xattr_set_filesize: Set ytdl.filesize user xattribute with expected size. xattr_set_filesize: Set ytdl.filesize user xattribute with expected size.
(experimenatal) (experimental)
external_downloader_args: A list of additional command-line arguments for the external_downloader_args: A list of additional command-line arguments for the
external downloader. external downloader.
@@ -325,7 +325,7 @@ class FileDownloader(object):
) )
# Check file already present # Check file already present
if filename != '-' and nooverwrites_and_exists or continuedl_and_exists: if filename != '-' and (nooverwrites_and_exists or continuedl_and_exists):
self.report_file_already_downloaded(filename) self.report_file_already_downloaded(filename)
self._hook_progress({ self._hook_progress({
'filename': filename, 'filename': filename,

View File

@@ -3,7 +3,7 @@ from __future__ import unicode_literals
import re import re
from .common import FileDownloader from .common import FileDownloader
from ..compat import compat_urllib_request from ..utils import sanitized_Request
class DashSegmentsFD(FileDownloader): class DashSegmentsFD(FileDownloader):
@@ -22,7 +22,7 @@ class DashSegmentsFD(FileDownloader):
def append_url_to_file(outf, target_url, target_name, remaining_bytes=None): def append_url_to_file(outf, target_url, target_name, remaining_bytes=None):
self.to_screen('[DashSegments] %s: Downloading %s' % (info_dict['id'], target_name)) self.to_screen('[DashSegments] %s: Downloading %s' % (info_dict['id'], target_name))
req = compat_urllib_request.Request(target_url) req = sanitized_Request(target_url)
if remaining_bytes is not None: if remaining_bytes is not None:
req.add_header('Range', 'bytes=0-%d' % (remaining_bytes - 1)) req.add_header('Range', 'bytes=0-%d' % (remaining_bytes - 1))

View File

@@ -5,6 +5,10 @@ import subprocess
from .common import FileDownloader from .common import FileDownloader
from ..utils import ( from ..utils import (
cli_option,
cli_valueless_option,
cli_bool_option,
cli_configuration_args,
encodeFilename, encodeFilename,
encodeArgument, encodeArgument,
) )
@@ -45,18 +49,17 @@ class ExternalFD(FileDownloader):
def supports(cls, info_dict): def supports(cls, info_dict):
return info_dict['protocol'] in ('http', 'https', 'ftp', 'ftps') return info_dict['protocol'] in ('http', 'https', 'ftp', 'ftps')
def _source_address(self, command_option): def _option(self, command_option, param):
source_address = self.params.get('source_address') return cli_option(self.params, command_option, param)
if source_address is None:
return [] def _bool_option(self, command_option, param, true_value='true', false_value='false', separator=None):
return [command_option, source_address] return cli_bool_option(self.params, command_option, param, true_value, false_value, separator)
def _valueless_option(self, command_option, param, expected_value=True):
return cli_valueless_option(self.params, command_option, param, expected_value)
def _configuration_args(self, default=[]): def _configuration_args(self, default=[]):
ex_args = self.params.get('external_downloader_args') return cli_configuration_args(self.params, 'external_downloader_args', default)
if ex_args is None:
return default
assert isinstance(ex_args, list)
return ex_args
def _call_downloader(self, tmpfilename, info_dict): def _call_downloader(self, tmpfilename, info_dict):
""" Either overwrite this or implement _make_cmd """ """ Either overwrite this or implement _make_cmd """
@@ -77,7 +80,19 @@ class CurlFD(ExternalFD):
cmd = [self.exe, '--location', '-o', tmpfilename] cmd = [self.exe, '--location', '-o', tmpfilename]
for key, val in info_dict['http_headers'].items(): for key, val in info_dict['http_headers'].items():
cmd += ['--header', '%s: %s' % (key, val)] cmd += ['--header', '%s: %s' % (key, val)]
cmd += self._source_address('--interface') cmd += self._option('--interface', 'source_address')
cmd += self._option('--proxy', 'proxy')
cmd += self._valueless_option('--insecure', 'nocheckcertificate')
cmd += self._configuration_args()
cmd += ['--', info_dict['url']]
return cmd
class AxelFD(ExternalFD):
def _make_cmd(self, tmpfilename, info_dict):
cmd = [self.exe, '-o', tmpfilename]
for key, val in info_dict['http_headers'].items():
cmd += ['-H', '%s: %s' % (key, val)]
cmd += self._configuration_args() cmd += self._configuration_args()
cmd += ['--', info_dict['url']] cmd += ['--', info_dict['url']]
return cmd return cmd
@@ -88,7 +103,9 @@ class WgetFD(ExternalFD):
cmd = [self.exe, '-O', tmpfilename, '-nv', '--no-cookies'] cmd = [self.exe, '-O', tmpfilename, '-nv', '--no-cookies']
for key, val in info_dict['http_headers'].items(): for key, val in info_dict['http_headers'].items():
cmd += ['--header', '%s: %s' % (key, val)] cmd += ['--header', '%s: %s' % (key, val)]
cmd += self._source_address('--bind-address') cmd += self._option('--bind-address', 'source_address')
cmd += self._option('--proxy', 'proxy')
cmd += self._valueless_option('--no-check-certificate', 'nocheckcertificate')
cmd += self._configuration_args() cmd += self._configuration_args()
cmd += ['--', info_dict['url']] cmd += ['--', info_dict['url']]
return cmd return cmd
@@ -105,7 +122,9 @@ class Aria2cFD(ExternalFD):
cmd += ['--out', os.path.basename(tmpfilename)] cmd += ['--out', os.path.basename(tmpfilename)]
for key, val in info_dict['http_headers'].items(): for key, val in info_dict['http_headers'].items():
cmd += ['--header', '%s: %s' % (key, val)] cmd += ['--header', '%s: %s' % (key, val)]
cmd += self._source_address('--interface') cmd += self._option('--interface', 'source_address')
cmd += self._option('--all-proxy', 'proxy')
cmd += self._bool_option('--check-certificate', 'nocheckcertificate', 'false', 'true', '=')
cmd += ['--', info_dict['url']] cmd += ['--', info_dict['url']]
return cmd return cmd

View File

@@ -5,14 +5,18 @@ import io
import itertools import itertools
import os import os
import time import time
import xml.etree.ElementTree as etree
from .fragment import FragmentFD from .fragment import FragmentFD
from ..compat import ( from ..compat import (
compat_etree_fromstring,
compat_urlparse, compat_urlparse,
compat_urllib_error, compat_urllib_error,
compat_urllib_parse_urlparse,
) )
from ..utils import ( from ..utils import (
encodeFilename,
fix_xml_ampersands,
sanitize_open,
struct_pack, struct_pack,
struct_unpack, struct_unpack,
xpath_text, xpath_text,
@@ -283,9 +287,14 @@ class F4mFD(FragmentFD):
man_url = info_dict['url'] man_url = info_dict['url']
requested_bitrate = info_dict.get('tbr') requested_bitrate = info_dict.get('tbr')
self.to_screen('[%s] Downloading f4m manifest' % self.FD_NAME) self.to_screen('[%s] Downloading f4m manifest' % self.FD_NAME)
manifest = self.ydl.urlopen(man_url).read() urlh = self.ydl.urlopen(man_url)
man_url = urlh.geturl()
# Some manifests may be malformed, e.g. prosiebensat1 generated manifests
# (see https://github.com/rg3/youtube-dl/issues/6215#issuecomment-121704244
# and https://github.com/rg3/youtube-dl/issues/7823)
manifest = fix_xml_ampersands(urlh.read().decode('utf-8', 'ignore')).strip()
doc = etree.fromstring(manifest) doc = compat_etree_fromstring(manifest)
formats = [(int(f.attrib.get('bitrate', -1)), f) formats = [(int(f.attrib.get('bitrate', -1)), f)
for f in self._get_unencrypted_media(doc)] for f in self._get_unencrypted_media(doc)]
if requested_bitrate is None: if requested_bitrate is None:
@@ -327,34 +336,40 @@ class F4mFD(FragmentFD):
if not live: if not live:
write_metadata_tag(dest_stream, metadata) write_metadata_tag(dest_stream, metadata)
base_url_parsed = compat_urllib_parse_urlparse(base_url)
self._start_frag_download(ctx) self._start_frag_download(ctx)
frags_filenames = [] frags_filenames = []
while fragments_list: while fragments_list:
seg_i, frag_i = fragments_list.pop(0) seg_i, frag_i = fragments_list.pop(0)
name = 'Seg%d-Frag%d' % (seg_i, frag_i) name = 'Seg%d-Frag%d' % (seg_i, frag_i)
url = base_url + name query = []
if base_url_parsed.query:
query.append(base_url_parsed.query)
if akamai_pv: if akamai_pv:
url += '?' + akamai_pv.strip(';') query.append(akamai_pv.strip(';'))
if info_dict.get('extra_param_to_segment_url'): if info_dict.get('extra_param_to_segment_url'):
url += info_dict.get('extra_param_to_segment_url') query.append(info_dict['extra_param_to_segment_url'])
url_parsed = base_url_parsed._replace(path=base_url_parsed.path + name, query='&'.join(query))
frag_filename = '%s-%s' % (ctx['tmpfilename'], name) frag_filename = '%s-%s' % (ctx['tmpfilename'], name)
try: try:
success = ctx['dl'].download(frag_filename, {'url': url}) success = ctx['dl'].download(frag_filename, {'url': url_parsed.geturl()})
if not success: if not success:
return False return False
with open(frag_filename, 'rb') as down: (down, frag_sanitized) = sanitize_open(frag_filename, 'rb')
down_data = down.read() down_data = down.read()
reader = FlvReader(down_data) down.close()
while True: reader = FlvReader(down_data)
_, box_type, box_data = reader.read_box_info() while True:
if box_type == b'mdat': _, box_type, box_data = reader.read_box_info()
dest_stream.write(box_data) if box_type == b'mdat':
break dest_stream.write(box_data)
break
if live: if live:
os.remove(frag_filename) os.remove(encodeFilename(frag_sanitized))
else: else:
frags_filenames.append(frag_filename) frags_filenames.append(frag_sanitized)
except (compat_urllib_error.HTTPError, ) as err: except (compat_urllib_error.HTTPError, ) as err:
if live and (err.code == 404 or err.code == 410): if live and (err.code == 404 or err.code == 410):
# We didn't keep up with the live window. Continue # We didn't keep up with the live window. Continue
@@ -375,6 +390,6 @@ class F4mFD(FragmentFD):
self._finish_frag_download(ctx) self._finish_frag_download(ctx)
for frag_file in frags_filenames: for frag_file in frags_filenames:
os.remove(frag_file) os.remove(encodeFilename(frag_file))
return True return True

View File

@@ -35,6 +35,7 @@ class FragmentFD(FileDownloader):
'quiet': True, 'quiet': True,
'noprogress': True, 'noprogress': True,
'ratelimit': self.params.get('ratelimit', None), 'ratelimit': self.params.get('ratelimit', None),
'retries': self.params.get('retries', 0),
'test': self.params.get('test', False), 'test': self.params.get('test', False),
} }
) )

View File

@@ -12,6 +12,8 @@ from ..postprocessor.ffmpeg import FFmpegPostProcessor
from ..utils import ( from ..utils import (
encodeArgument, encodeArgument,
encodeFilename, encodeFilename,
sanitize_open,
handle_youtubedl_headers,
) )
@@ -27,10 +29,22 @@ class HlsFD(FileDownloader):
return False return False
ffpp.check_version() ffpp.check_version()
args = [ args = [ffpp.executable, '-y']
encodeArgument(opt)
for opt in (ffpp.executable, '-y', '-i', url, '-f', 'mp4', '-c', 'copy', '-bsf:a', 'aac_adtstoasc')] if info_dict['http_headers'] and re.match(r'^https?://', url):
args.append(encodeFilename(tmpfilename, True)) # Trailing \r\n after each HTTP header is important to prevent warning from ffmpeg/avconv:
# [http @ 00000000003d2fa0] No trailing CRLF found in HTTP header.
headers = handle_youtubedl_headers(info_dict['http_headers'])
args += [
'-headers',
''.join('%s: %s\r\n' % (key, val) for key, val in headers.items())]
args += ['-i', url, '-f', 'mp4', '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
args = [encodeArgument(opt) for opt in args]
args.append(encodeFilename(ffpp._ffmpeg_filename_argument(tmpfilename), True))
self._debug_cmd(args)
retval = subprocess.call(args) retval = subprocess.call(args)
if retval == 0: if retval == 0:
@@ -87,13 +101,14 @@ class NativeHlsFD(FragmentFD):
success = ctx['dl'].download(frag_filename, {'url': frag_url}) success = ctx['dl'].download(frag_filename, {'url': frag_url})
if not success: if not success:
return False return False
with open(frag_filename, 'rb') as down: down, frag_sanitized = sanitize_open(frag_filename, 'rb')
ctx['dest_stream'].write(down.read()) ctx['dest_stream'].write(down.read())
frags_filenames.append(frag_filename) down.close()
frags_filenames.append(frag_sanitized)
self._finish_frag_download(ctx) self._finish_frag_download(ctx)
for frag_file in frags_filenames: for frag_file in frags_filenames:
os.remove(frag_file) os.remove(encodeFilename(frag_file))
return True return True

View File

@@ -7,14 +7,12 @@ import time
import re import re
from .common import FileDownloader from .common import FileDownloader
from ..compat import ( from ..compat import compat_urllib_error
compat_urllib_request,
compat_urllib_error,
)
from ..utils import ( from ..utils import (
ContentTooShortError, ContentTooShortError,
encodeFilename, encodeFilename,
sanitize_open, sanitize_open,
sanitized_Request,
) )
@@ -29,8 +27,8 @@ class HttpFD(FileDownloader):
add_headers = info_dict.get('http_headers') add_headers = info_dict.get('http_headers')
if add_headers: if add_headers:
headers.update(add_headers) headers.update(add_headers)
basic_request = compat_urllib_request.Request(url, None, headers) basic_request = sanitized_Request(url, None, headers)
request = compat_urllib_request.Request(url, None, headers) request = sanitized_Request(url, None, headers)
is_test = self.params.get('test', False) is_test = self.params.get('test', False)

View File

@@ -105,7 +105,7 @@ class RtmpFD(FileDownloader):
protocol = info_dict.get('rtmp_protocol', None) protocol = info_dict.get('rtmp_protocol', None)
real_time = info_dict.get('rtmp_real_time', False) real_time = info_dict.get('rtmp_real_time', False)
no_resume = info_dict.get('no_resume', False) no_resume = info_dict.get('no_resume', False)
continue_dl = info_dict.get('continuedl', True) continue_dl = self.params.get('continuedl', True)
self.report_destination(filename) self.report_destination(filename)
tmpfilename = self.temp_name(filename) tmpfilename = self.temp_name(filename)
@@ -117,7 +117,7 @@ class RtmpFD(FileDownloader):
return False return False
# Download using rtmpdump. rtmpdump returns exit code 2 when # Download using rtmpdump. rtmpdump returns exit code 2 when
# the connection was interrumpted and resuming appears to be # the connection was interrupted and resuming appears to be
# possible. This is part of rtmpdump's normal usage, AFAIK. # possible. This is part of rtmpdump's normal usage, AFAIK.
basic_args = [ basic_args = [
'rtmpdump', '--verbose', '-r', url, 'rtmpdump', '--verbose', '-r', url,

View File

@@ -3,9 +3,15 @@ from __future__ import unicode_literals
from .abc import ABCIE from .abc import ABCIE
from .abc7news import Abc7NewsIE from .abc7news import Abc7NewsIE
from .academicearth import AcademicEarthCourseIE from .academicearth import AcademicEarthCourseIE
from .acast import (
ACastIE,
ACastChannelIE,
)
from .addanime import AddAnimeIE from .addanime import AddAnimeIE
from .adobetv import ( from .adobetv import (
AdobeTVIE, AdobeTVIE,
AdobeTVShowIE,
AdobeTVChannelIE,
AdobeTVVideoIE, AdobeTVVideoIE,
) )
from .adultswim import AdultSwimIE from .adultswim import AdultSwimIE
@@ -38,6 +44,7 @@ from .arte import (
) )
from .atresplayer import AtresPlayerIE from .atresplayer import AtresPlayerIE
from .atttechchannel import ATTTechChannelIE from .atttechchannel import ATTTechChannelIE
from .audimedia import AudiMediaIE
from .audiomack import AudiomackIE, AudiomackAlbumIE from .audiomack import AudiomackIE, AudiomackAlbumIE
from .azubu import AzubuIE from .azubu import AzubuIE
from .baidu import BaiduVideoIE from .baidu import BaiduVideoIE
@@ -45,6 +52,7 @@ from .bambuser import BambuserIE, BambuserChannelIE
from .bandcamp import BandcampIE, BandcampAlbumIE from .bandcamp import BandcampIE, BandcampAlbumIE
from .bbc import ( from .bbc import (
BBCCoUkIE, BBCCoUkIE,
BBCCoUkArticleIE,
BBCIE, BBCIE,
) )
from .beeg import BeegIE from .beeg import BeegIE
@@ -59,7 +67,10 @@ from .bloomberg import BloombergIE
from .bpb import BpbIE from .bpb import BpbIE
from .br import BRIE from .br import BRIE
from .breakcom import BreakIE from .breakcom import BreakIE
from .brightcove import BrightcoveIE from .brightcove import (
BrightcoveLegacyIE,
BrightcoveNewIE,
)
from .buzzfeed import BuzzFeedIE from .buzzfeed import BuzzFeedIE
from .byutv import BYUtvIE from .byutv import BYUtvIE
from .c56 import C56IE from .c56 import C56IE
@@ -76,6 +87,7 @@ from .cbssports import CBSSportsIE
from .ccc import CCCIE from .ccc import CCCIE
from .ceskatelevize import CeskaTelevizeIE from .ceskatelevize import CeskaTelevizeIE
from .channel9 import Channel9IE from .channel9 import Channel9IE
from .chaturbate import ChaturbateIE
from .chilloutzone import ChilloutzoneIE from .chilloutzone import ChilloutzoneIE
from .chirbit import ( from .chirbit import (
ChirbitIE, ChirbitIE,
@@ -88,6 +100,7 @@ from .cliphunter import CliphunterIE
from .clipsyndicate import ClipsyndicateIE from .clipsyndicate import ClipsyndicateIE
from .cloudy import CloudyIE from .cloudy import CloudyIE
from .clubic import ClubicIE from .clubic import ClubicIE
from .clyp import ClypIE
from .cmt import CMTIE from .cmt import CMTIE
from .cnet import CNETIE from .cnet import CNETIE
from .cnn import ( from .cnn import (
@@ -118,12 +131,15 @@ from .dailymotion import (
) )
from .daum import DaumIE from .daum import DaumIE
from .dbtv import DBTVIE from .dbtv import DBTVIE
from .dcn import DCNIE
from .dctp import DctpTvIE from .dctp import DctpTvIE
from .deezer import DeezerPlaylistIE from .deezer import DeezerPlaylistIE
from .democracynow import DemocracynowIE
from .dfb import DFBIE from .dfb import DFBIE
from .dhm import DHMIE from .dhm import DHMIE
from .dotsub import DotsubIE from .dotsub import DotsubIE
from .douyutv import DouyuTVIE from .douyutv import DouyuTVIE
from .dplay import DPlayIE
from .dramafever import ( from .dramafever import (
DramaFeverIE, DramaFeverIE,
DramaFeverSeriesIE, DramaFeverSeriesIE,
@@ -137,7 +153,6 @@ from .dump import DumpIE
from .dumpert import DumpertIE from .dumpert import DumpertIE
from .defense import DefenseGouvFrIE from .defense import DefenseGouvFrIE
from .discovery import DiscoveryIE from .discovery import DiscoveryIE
from .divxstage import DivxStageIE
from .dropbox import DropboxIE from .dropbox import DropboxIE
from .eagleplatform import EaglePlatformIE from .eagleplatform import EaglePlatformIE
from .ebaumsworld import EbaumsWorldIE from .ebaumsworld import EbaumsWorldIE
@@ -157,6 +172,8 @@ from .eporner import EpornerIE
from .eroprofile import EroProfileIE from .eroprofile import EroProfileIE
from .escapist import EscapistIE from .escapist import EscapistIE
from .espn import ESPNIE from .espn import ESPNIE
from .esri import EsriVideoIE
from .europa import EuropaIE
from .everyonesmixtape import EveryonesMixtapeIE from .everyonesmixtape import EveryonesMixtapeIE
from .exfm import ExfmIE from .exfm import ExfmIE
from .expotv import ExpoTVIE from .expotv import ExpoTVIE
@@ -164,14 +181,12 @@ from .extremetube import ExtremeTubeIE
from .facebook import FacebookIE from .facebook import FacebookIE
from .faz import FazIE from .faz import FazIE
from .fc2 import FC2IE from .fc2 import FC2IE
from .fczenit import FczenitIE
from .firstpost import FirstpostIE from .firstpost import FirstpostIE
from .firsttv import FirstTVIE from .firsttv import FirstTVIE
from .fivemin import FiveMinIE from .fivemin import FiveMinIE
from .fivetv import FiveTVIE from .fivetv import FiveTVIE
from .fktv import ( from .fktv import FKTVIE
FKTVIE,
FKTVPosteckeIE,
)
from .flickr import FlickrIE from .flickr import FlickrIE
from .folketinget import FolketingetIE from .folketinget import FolketingetIE
from .footyroom import FootyRoomIE from .footyroom import FootyRoomIE
@@ -191,7 +206,9 @@ from .francetv import (
from .freesound import FreesoundIE from .freesound import FreesoundIE
from .freespeech import FreespeechIE from .freespeech import FreespeechIE
from .freevideo import FreeVideoIE from .freevideo import FreeVideoIE
from .funimation import FunimationIE
from .funnyordie import FunnyOrDieIE from .funnyordie import FunnyOrDieIE
from .gameinformer import GameInformerIE
from .gamekings import GamekingsIE from .gamekings import GamekingsIE
from .gameone import ( from .gameone import (
GameOneIE, GameOneIE,
@@ -208,13 +225,15 @@ from .gfycat import GfycatIE
from .giantbomb import GiantBombIE from .giantbomb import GiantBombIE
from .giga import GigaIE from .giga import GigaIE
from .glide import GlideIE from .glide import GlideIE
from .globo import GloboIE from .globo import (
GloboIE,
GloboArticleIE,
)
from .godtube import GodTubeIE from .godtube import GodTubeIE
from .goldenmoustache import GoldenMoustacheIE from .goldenmoustache import GoldenMoustacheIE
from .golem import GolemIE from .golem import GolemIE
from .googleplus import GooglePlusIE from .googleplus import GooglePlusIE
from .googlesearch import GoogleSearchIE from .googlesearch import GoogleSearchIE
from .gorillavid import GorillaVidIE
from .goshgay import GoshgayIE from .goshgay import GoshgayIE
from .groupon import GrouponIE from .groupon import GrouponIE
from .hark import HarkIE from .hark import HarkIE
@@ -227,7 +246,6 @@ from .historicfilms import HistoricFilmsIE
from .history import HistoryIE from .history import HistoryIE
from .hitbox import HitboxIE, HitboxLiveIE from .hitbox import HitboxIE, HitboxLiveIE
from .hornbunny import HornBunnyIE from .hornbunny import HornBunnyIE
from .hostingbulk import HostingBulkIE
from .hotnewhiphop import HotNewHipHopIE from .hotnewhiphop import HotNewHipHopIE
from .howcast import HowcastIE from .howcast import HowcastIE
from .howstuffworks import HowStuffWorksIE from .howstuffworks import HowStuffWorksIE
@@ -239,8 +257,15 @@ from .imdb import (
ImdbIE, ImdbIE,
ImdbListIE ImdbListIE
) )
from .imgur import ImgurIE from .imgur import (
ImgurIE,
ImgurAlbumIE,
)
from .ina import InaIE from .ina import InaIE
from .indavideo import (
IndavideoIE,
IndavideoEmbedIE,
)
from .infoq import InfoQIE from .infoq import InfoQIE
from .instagram import InstagramIE, InstagramUserIE from .instagram import InstagramIE, InstagramUserIE
from .internetvideoarchive import InternetVideoArchiveIE from .internetvideoarchive import InternetVideoArchiveIE
@@ -290,6 +315,11 @@ from .lifenews import (
LifeNewsIE, LifeNewsIE,
LifeEmbedIE, LifeEmbedIE,
) )
from .limelight import (
LimelightMediaIE,
LimelightChannelIE,
LimelightChannelListIE,
)
from .liveleak import LiveLeakIE from .liveleak import LiveLeakIE
from .livestream import ( from .livestream import (
LivestreamIE, LivestreamIE,
@@ -307,7 +337,6 @@ from .macgamestore import MacGameStoreIE
from .mailru import MailRuIE from .mailru import MailRuIE
from .malemotion import MalemotionIE from .malemotion import MalemotionIE
from .mdr import MDRIE from .mdr import MDRIE
from .megavideoz import MegaVideozIE
from .metacafe import MetacafeIE from .metacafe import MetacafeIE
from .metacritic import MetacriticIE from .metacritic import MetacriticIE
from .mgoon import MgoonIE from .mgoon import MgoonIE
@@ -329,16 +358,16 @@ from .motherless import MotherlessIE
from .motorsport import MotorsportIE from .motorsport import MotorsportIE
from .movieclips import MovieClipsIE from .movieclips import MovieClipsIE
from .moviezine import MoviezineIE from .moviezine import MoviezineIE
from .movshare import MovShareIE
from .mtv import ( from .mtv import (
MTVIE, MTVIE,
MTVServicesEmbeddedIE, MTVServicesEmbeddedIE,
MTVIggyIE, MTVIggyIE,
MTVDEIE,
) )
from .muenchentv import MuenchenTVIE from .muenchentv import MuenchenTVIE
from .musicplayon import MusicPlayOnIE from .musicplayon import MusicPlayOnIE
from .musicvault import MusicVaultIE
from .muzu import MuzuTVIE from .muzu import MuzuTVIE
from .mwave import MwaveIE
from .myspace import MySpaceIE, MySpaceAlbumIE from .myspace import MySpaceIE, MySpaceAlbumIE
from .myspass import MySpassIE from .myspass import MySpassIE
from .myvi import MyviIE from .myvi import MyviIE
@@ -352,10 +381,14 @@ from .nbc import (
NBCNewsIE, NBCNewsIE,
NBCSportsIE, NBCSportsIE,
NBCSportsVPlayerIE, NBCSportsVPlayerIE,
MSNBCIE,
) )
from .ndr import ( from .ndr import (
NDRIE, NDRIE,
NJoyIE, NJoyIE,
NDREmbedBaseIE,
NDREmbedIE,
NJoyEmbedIE,
) )
from .ndtv import NDTVIE from .ndtv import NDTVIE
from .netzkino import NetzkinoIE from .netzkino import NetzkinoIE
@@ -390,10 +423,22 @@ from .noco import NocoIE
from .normalboots import NormalbootsIE from .normalboots import NormalbootsIE
from .nosvideo import NosVideoIE from .nosvideo import NosVideoIE
from .nova import NovaIE from .nova import NovaIE
from .novamov import NovaMovIE from .novamov import (
from .nowness import NownessIE NovaMovIE,
from .nowtv import NowTVIE WholeCloudIE,
from .nowvideo import NowVideoIE NowVideoIE,
VideoWeedIE,
CloudTimeIE,
)
from .nowness import (
NownessIE,
NownessPlaylistIE,
NownessSeriesIE,
)
from .nowtv import (
NowTVIE,
NowTVListIE,
)
from .npo import ( from .npo import (
NPOIE, NPOIE,
NPOLiveIE, NPOLiveIE,
@@ -421,7 +466,6 @@ from .ooyala import (
OoyalaIE, OoyalaIE,
OoyalaExternalIE, OoyalaExternalIE,
) )
from .openfilm import OpenFilmIE
from .orf import ( from .orf import (
ORFTVthekIE, ORFTVthekIE,
ORFOE1IE, ORFOE1IE,
@@ -431,6 +475,7 @@ from .orf import (
from .parliamentliveuk import ParliamentLiveUKIE from .parliamentliveuk import ParliamentLiveUKIE
from .patreon import PatreonIE from .patreon import PatreonIE
from .pbs import PBSIE from .pbs import PBSIE
from .periscope import PeriscopeIE
from .philharmoniedeparis import PhilharmonieDeParisIE from .philharmoniedeparis import PhilharmonieDeParisIE
from .phoenix import PhoenixIE from .phoenix import PhoenixIE
from .photobucket import PhotobucketIE from .photobucket import PhotobucketIE
@@ -439,8 +484,13 @@ from .planetaplay import PlanetaPlayIE
from .pladform import PladformIE from .pladform import PladformIE
from .played import PlayedIE from .played import PlayedIE
from .playfm import PlayFMIE from .playfm import PlayFMIE
from .playtvak import PlaytvakIE
from .playvid import PlayvidIE from .playvid import PlayvidIE
from .playwire import PlaywireIE from .playwire import PlaywireIE
from .pluralsight import (
PluralsightIE,
PluralsightCourseIE,
)
from .podomatic import PodomaticIE from .podomatic import PodomaticIE
from .porn91 import Porn91IE from .porn91 import Porn91IE
from .pornhd import PornHdIE from .pornhd import PornHdIE
@@ -486,6 +536,7 @@ from .rtl2 import RTL2IE
from .rtp import RTPIE from .rtp import RTPIE
from .rts import RTSIE from .rts import RTSIE
from .rtve import RTVEALaCartaIE, RTVELiveIE, RTVEInfantilIE from .rtve import RTVEALaCartaIE, RTVELiveIE, RTVEInfantilIE
from .rtvnh import RTVNHIE
from .ruhd import RUHDIE from .ruhd import RUHDIE
from .rutube import ( from .rutube import (
RutubeIE, RutubeIE,
@@ -512,9 +563,14 @@ from .senateisvp import SenateISVPIE
from .servingsys import ServingSysIE from .servingsys import ServingSysIE
from .sexu import SexuIE from .sexu import SexuIE
from .sexykarma import SexyKarmaIE from .sexykarma import SexyKarmaIE
from .shahid import ShahidIE
from .shared import SharedIE from .shared import SharedIE
from .sharesix import ShareSixIE from .sharesix import ShareSixIE
from .sina import SinaIE from .sina import SinaIE
from .skynewsarabia import (
SkyNewsArabiaIE,
SkyNewsArabiaArticleIE,
)
from .slideshare import SlideshareIE from .slideshare import SlideshareIE
from .slutload import SlutloadIE from .slutload import SlutloadIE
from .smotri import ( from .smotri import (
@@ -537,7 +593,8 @@ from .soundcloud import (
SoundcloudIE, SoundcloudIE,
SoundcloudSetIE, SoundcloudSetIE,
SoundcloudUserIE, SoundcloudUserIE,
SoundcloudPlaylistIE SoundcloudPlaylistIE,
SoundcloudSearchIE
) )
from .soundgasm import ( from .soundgasm import (
SoundgasmIE, SoundgasmIE,
@@ -556,6 +613,7 @@ from .spankwire import SpankwireIE
from .spiegel import SpiegelIE, SpiegelArticleIE from .spiegel import SpiegelIE, SpiegelArticleIE
from .spiegeltv import SpiegeltvIE from .spiegeltv import SpiegeltvIE
from .spike import SpikeIE from .spike import SpikeIE
from .stitcher import StitcherIE
from .sport5 import Sport5IE from .sport5 import Sport5IE
from .sportbox import ( from .sportbox import (
SportBoxIE, SportBoxIE,
@@ -591,6 +649,7 @@ from .techtalks import TechTalksIE
from .ted import TEDIE from .ted import TEDIE
from .telebruxelles import TeleBruxellesIE from .telebruxelles import TeleBruxellesIE
from .telecinco import TelecincoIE from .telecinco import TelecincoIE
from .telegraaf import TelegraafIE
from .telemb import TeleMBIE from .telemb import TeleMBIE
from .teletask import TeleTaskIE from .teletask import TeleTaskIE
from .tenplay import TenPlayIE from .tenplay import TenPlayIE
@@ -598,7 +657,10 @@ from .testurl import TestURLIE
from .testtube import TestTubeIE from .testtube import TestTubeIE
from .tf1 import TF1IE from .tf1 import TF1IE
from .theonion import TheOnionIE from .theonion import TheOnionIE
from .theplatform import ThePlatformIE from .theplatform import (
ThePlatformIE,
ThePlatformFeedIE,
)
from .thesixtyone import TheSixtyOneIE from .thesixtyone import TheSixtyOneIE
from .thisamericanlife import ThisAmericanLifeIE from .thisamericanlife import ThisAmericanLifeIE
from .thisav import ThisAVIE from .thisav import ThisAVIE
@@ -656,7 +718,7 @@ from .twitch import (
TwitchBookmarksIE, TwitchBookmarksIE,
TwitchStreamIE, TwitchStreamIE,
) )
from .twitter import TwitterCardIE from .twitter import TwitterCardIE, TwitterIE
from .ubu import UbuIE from .ubu import UbuIE
from .udemy import ( from .udemy import (
UdemyIE, UdemyIE,
@@ -682,18 +744,16 @@ from .vgtv import (
from .vh1 import VH1IE from .vh1 import VH1IE
from .vice import ViceIE from .vice import ViceIE
from .viddler import ViddlerIE from .viddler import ViddlerIE
from .videobam import VideoBamIE
from .videodetective import VideoDetectiveIE from .videodetective import VideoDetectiveIE
from .videolecturesnet import VideoLecturesNetIE
from .videofyme import VideofyMeIE from .videofyme import VideofyMeIE
from .videomega import VideoMegaIE from .videomega import VideoMegaIE
from .videopremium import VideoPremiumIE from .videopremium import VideoPremiumIE
from .videott import VideoTtIE from .videott import VideoTtIE
from .videoweed import VideoWeedIE
from .vidme import VidmeIE from .vidme import VidmeIE
from .vidzi import VidziIE from .vidzi import VidziIE
from .vier import VierIE, VierVideosIE from .vier import VierIE, VierVideosIE
from .viewster import ViewsterIE from .viewster import ViewsterIE
from .viidea import ViideaIE
from .vimeo import ( from .vimeo import (
VimeoIE, VimeoIE,
VimeoAlbumIE, VimeoAlbumIE,
@@ -717,6 +777,7 @@ from .vk import (
VKIE, VKIE,
VKUserVideosIE, VKUserVideosIE,
) )
from .vlive import VLiveIE
from .vodlocker import VodlockerIE from .vodlocker import VodlockerIE
from .voicerepublic import VoiceRepublicIE from .voicerepublic import VoiceRepublicIE
from .vporn import VpornIE from .vporn import VpornIE
@@ -745,6 +806,7 @@ from .wrzuta import WrzutaIE
from .wsj import WSJIE from .wsj import WSJIE
from .xbef import XBefIE from .xbef import XBefIE
from .xboxclips import XboxClipsIE from .xboxclips import XboxClipsIE
from .xfileshare import XFileShareIE
from .xhamster import ( from .xhamster import (
XHamsterIE, XHamsterIE,
XHamsterEmbedIE, XHamsterEmbedIE,
@@ -788,6 +850,7 @@ from .youtube import (
YoutubeTruncatedIDIE, YoutubeTruncatedIDIE,
YoutubeTruncatedURLIE, YoutubeTruncatedURLIE,
YoutubeUserIE, YoutubeUserIE,
YoutubeUserPlaylistsIE,
YoutubeWatchLaterIE, YoutubeWatchLaterIE,
) )
from .zapiks import ZapiksIE from .zapiks import ZapiksIE

View File

@@ -1,16 +1,20 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import re import re
import json
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import (
ExtractorError,
js_to_json,
int_or_none,
)
class ABCIE(InfoExtractor): class ABCIE(InfoExtractor):
IE_NAME = 'abc.net.au' IE_NAME = 'abc.net.au'
_VALID_URL = r'http://www\.abc\.net\.au/news/[^/]+/[^/]+/(?P<id>\d+)' _VALID_URL = r'http://www\.abc\.net\.au/news/(?:[^/]+/){1,2}(?P<id>\d+)'
_TEST = { _TESTS = [{
'url': 'http://www.abc.net.au/news/2014-11-05/australia-to-staff-ebola-treatment-centre-in-sierra-leone/5868334', 'url': 'http://www.abc.net.au/news/2014-11-05/australia-to-staff-ebola-treatment-centre-in-sierra-leone/5868334',
'md5': 'cb3dd03b18455a661071ee1e28344d9f', 'md5': 'cb3dd03b18455a661071ee1e28344d9f',
'info_dict': { 'info_dict': {
@@ -19,23 +23,62 @@ class ABCIE(InfoExtractor):
'title': 'Australia to help staff Ebola treatment centre in Sierra Leone', 'title': 'Australia to help staff Ebola treatment centre in Sierra Leone',
'description': 'md5:809ad29c67a05f54eb41f2a105693a67', 'description': 'md5:809ad29c67a05f54eb41f2a105693a67',
}, },
} }, {
'url': 'http://www.abc.net.au/news/2015-08-17/warren-entsch-introduces-same-sex-marriage-bill/6702326',
'md5': 'db2a5369238b51f9811ad815b69dc086',
'info_dict': {
'id': 'NvqvPeNZsHU',
'ext': 'mp4',
'upload_date': '20150816',
'uploader': 'ABC News (Australia)',
'description': 'Government backbencher Warren Entsch introduces a cross-party sponsored bill to legalise same-sex marriage, saying the bill is designed to promote "an inclusive Australia, not a divided one.". Read more here: http://ab.co/1Mwc6ef',
'uploader_id': 'NewsOnABC',
'title': 'Marriage Equality: Warren Entsch introduces same sex marriage bill',
},
'add_ie': ['Youtube'],
}, {
'url': 'http://www.abc.net.au/news/2015-10-23/nab-lifts-interest-rates-following-westpac-and-cba/6880080',
'md5': 'b96eee7c9edf4fc5a358a0252881cc1f',
'info_dict': {
'id': '6880080',
'ext': 'mp3',
'title': 'NAB lifts interest rates, following Westpac and CBA',
'description': 'md5:f13d8edc81e462fce4a0437c7dc04728',
},
}, {
'url': 'http://www.abc.net.au/news/2015-10-19/6866214',
'only_matching': True,
}]
def _real_extract(self, url): def _real_extract(self, url):
video_id = self._match_id(url) video_id = self._match_id(url)
webpage = self._download_webpage(url, video_id) webpage = self._download_webpage(url, video_id)
urls_info_json = self._search_regex( mobj = re.search(
r'inlineVideoData\.push\((.*?)\);', webpage, 'video urls', r'inline(?P<type>Video|Audio|YouTube)Data\.push\((?P<json_data>[^)]+)\);',
flags=re.DOTALL) webpage)
urls_info = json.loads(urls_info_json.replace('\'', '"')) if mobj is None:
raise ExtractorError('Unable to extract video urls')
urls_info = self._parse_json(
mobj.group('json_data'), video_id, transform_source=js_to_json)
if not isinstance(urls_info, list):
urls_info = [urls_info]
if mobj.group('type') == 'YouTube':
return self.playlist_result([
self.url_result(url_info['url']) for url_info in urls_info])
formats = [{ formats = [{
'url': url_info['url'], 'url': url_info['url'],
'width': int(url_info['width']), 'vcodec': url_info.get('codec') if mobj.group('type') == 'Video' else 'none',
'height': int(url_info['height']), 'width': int_or_none(url_info.get('width')),
'tbr': int(url_info['bitrate']), 'height': int_or_none(url_info.get('height')),
'filesize': int(url_info['filesize']), 'tbr': int_or_none(url_info.get('bitrate')),
'filesize': int_or_none(url_info.get('filesize')),
} for url_info in urls_info] } for url_info in urls_info]
self._sort_formats(formats) self._sort_formats(formats)
return { return {

View File

@@ -15,7 +15,7 @@ class AcademicEarthCourseIE(InfoExtractor):
'title': 'Laws of Nature', 'title': 'Laws of Nature',
'description': 'Introduce yourself to the laws of nature with these free online college lectures from Yale, Harvard, and MIT.', 'description': 'Introduce yourself to the laws of nature with these free online college lectures from Yale, Harvard, and MIT.',
}, },
'playlist_count': 4, 'playlist_count': 3,
} }
def _real_extract(self, url): def _real_extract(self, url):

View File

@@ -0,0 +1,70 @@
# coding: utf-8
from __future__ import unicode_literals
import re
from .common import InfoExtractor
from ..compat import compat_str
from ..utils import int_or_none
class ACastBaseIE(InfoExtractor):
_API_BASE_URL = 'https://www.acast.com/api/'
class ACastIE(ACastBaseIE):
IE_NAME = 'acast'
_VALID_URL = r'https?://(?:www\.)?acast\.com/(?P<channel>[^/]+)/(?P<id>[^/#?]+)'
_TEST = {
'url': 'https://www.acast.com/condenasttraveler/-where-are-you-taipei-101-taiwan',
'md5': 'ada3de5a1e3a2a381327d749854788bb',
'info_dict': {
'id': '57de3baa-4bb0-487e-9418-2692c1277a34',
'ext': 'mp3',
'title': '"Where Are You?": Taipei 101, Taiwan',
'timestamp': 1196172000000,
'description': 'md5:0c5d8201dfea2b93218ea986c91eee6e',
'duration': 211,
}
}
def _real_extract(self, url):
channel, display_id = re.match(self._VALID_URL, url).groups()
cast_data = self._download_json(self._API_BASE_URL + 'channels/%s/acasts/%s/playback' % (channel, display_id), display_id)
return {
'id': compat_str(cast_data['id']),
'display_id': display_id,
'url': cast_data['blings'][0]['audio'],
'title': cast_data['name'],
'description': cast_data.get('description'),
'thumbnail': cast_data.get('image'),
'timestamp': int_or_none(cast_data.get('publishingDate')),
'duration': int_or_none(cast_data.get('duration')),
}
class ACastChannelIE(ACastBaseIE):
IE_NAME = 'acast:channel'
_VALID_URL = r'https?://(?:www\.)?acast\.com/(?P<id>[^/#?]+)'
_TEST = {
'url': 'https://www.acast.com/condenasttraveler',
'info_dict': {
'id': '50544219-29bb-499e-a083-6087f4cb7797',
'title': 'Condé Nast Traveler Podcast',
'description': 'md5:98646dee22a5b386626ae31866638fbd',
},
'playlist_mincount': 20,
}
@classmethod
def suitable(cls, url):
return False if ACastIE.suitable(url) else super(ACastChannelIE, cls).suitable(url)
def _real_extract(self, url):
display_id = self._match_id(url)
channel_data = self._download_json(self._API_BASE_URL + 'channels/%s' % display_id, display_id)
casts = self._download_json(self._API_BASE_URL + 'channels/%s/acasts' % display_id, display_id)
entries = [self.url_result('https://www.acast.com/%s/%s' % (display_id, cast['url']), 'ACast') for cast in casts]
return self.playlist_result(entries, compat_str(channel_data['id']), channel_data['name'], channel_data.get('description'))

View File

@@ -1,23 +1,32 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import re
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import compat_str
from ..utils import ( from ..utils import (
parse_duration, parse_duration,
unified_strdate, unified_strdate,
str_to_int, str_to_int,
int_or_none,
float_or_none, float_or_none,
ISO639Utils, ISO639Utils,
determine_ext,
) )
class AdobeTVIE(InfoExtractor): class AdobeTVBaseIE(InfoExtractor):
_VALID_URL = r'https?://tv\.adobe\.com/watch/[^/]+/(?P<id>[^/]+)' _API_BASE_URL = 'http://tv.adobe.com/api/v4/'
class AdobeTVIE(AdobeTVBaseIE):
_VALID_URL = r'https?://tv\.adobe\.com/(?:(?P<language>fr|de|es|jp)/)?watch/(?P<show_urlname>[^/]+)/(?P<id>[^/]+)'
_TEST = { _TEST = {
'url': 'http://tv.adobe.com/watch/the-complete-picture-with-julieanne-kost/quick-tip-how-to-draw-a-circle-around-an-object-in-photoshop/', 'url': 'http://tv.adobe.com/watch/the-complete-picture-with-julieanne-kost/quick-tip-how-to-draw-a-circle-around-an-object-in-photoshop/',
'md5': '9bc5727bcdd55251f35ad311ca74fa1e', 'md5': '9bc5727bcdd55251f35ad311ca74fa1e',
'info_dict': { 'info_dict': {
'id': 'quick-tip-how-to-draw-a-circle-around-an-object-in-photoshop', 'id': '10981',
'ext': 'mp4', 'ext': 'mp4',
'title': 'Quick Tip - How to Draw a Circle Around an Object in Photoshop', 'title': 'Quick Tip - How to Draw a Circle Around an Object in Photoshop',
'description': 'md5:99ec318dc909d7ba2a1f2b038f7d2311', 'description': 'md5:99ec318dc909d7ba2a1f2b038f7d2311',
@@ -29,50 +38,106 @@ class AdobeTVIE(InfoExtractor):
} }
def _real_extract(self, url): def _real_extract(self, url):
video_id = self._match_id(url) language, show_urlname, urlname = re.match(self._VALID_URL, url).groups()
webpage = self._download_webpage(url, video_id) if not language:
language = 'en'
player = self._parse_json( video_data = self._download_json(
self._search_regex(r'html5player:\s*({.+?})\s*\n', webpage, 'player'), self._API_BASE_URL + 'episode/get/?language=%s&show_urlname=%s&urlname=%s&disclosure=standard' % (language, show_urlname, urlname),
video_id) urlname)['data'][0]
title = player.get('title') or self._search_regex(
r'data-title="([^"]+)"', webpage, 'title')
description = self._og_search_description(webpage)
thumbnail = self._og_search_thumbnail(webpage)
upload_date = unified_strdate(
self._html_search_meta('datepublished', webpage, 'upload date'))
duration = parse_duration(
self._html_search_meta('duration', webpage, 'duration') or
self._search_regex(
r'Runtime:\s*(\d{2}:\d{2}:\d{2})',
webpage, 'duration', fatal=False))
view_count = str_to_int(self._search_regex(
r'<div class="views">\s*Views?:\s*([\d,.]+)\s*</div>',
webpage, 'view count'))
formats = [{ formats = [{
'url': source['src'], 'url': source['url'],
'format_id': source.get('quality') or source['src'].split('-')[-1].split('.')[0] or None, 'format_id': source.get('quality_level') or source['url'].split('-')[-1].split('.')[0] or None,
'tbr': source.get('bitrate'), 'width': int_or_none(source.get('width')),
} for source in player['sources']] 'height': int_or_none(source.get('height')),
'tbr': int_or_none(source.get('video_data_rate')),
} for source in video_data['videos']]
self._sort_formats(formats) self._sort_formats(formats)
return { return {
'id': video_id, 'id': compat_str(video_data['id']),
'title': title, 'title': video_data['title'],
'description': description, 'description': video_data.get('description'),
'thumbnail': thumbnail, 'thumbnail': video_data.get('thumbnail'),
'upload_date': upload_date, 'upload_date': unified_strdate(video_data.get('start_date')),
'duration': duration, 'duration': parse_duration(video_data.get('duration')),
'view_count': view_count, 'view_count': str_to_int(video_data.get('playcount')),
'formats': formats, 'formats': formats,
} }
class AdobeTVPlaylistBaseIE(AdobeTVBaseIE):
def _parse_page_data(self, page_data):
return [self.url_result(self._get_element_url(element_data)) for element_data in page_data]
def _extract_playlist_entries(self, url, display_id):
page = self._download_json(url, display_id)
entries = self._parse_page_data(page['data'])
for page_num in range(2, page['paging']['pages'] + 1):
entries.extend(self._parse_page_data(
self._download_json(url + '&page=%d' % page_num, display_id)['data']))
return entries
class AdobeTVShowIE(AdobeTVPlaylistBaseIE):
_VALID_URL = r'https?://tv\.adobe\.com/(?:(?P<language>fr|de|es|jp)/)?show/(?P<id>[^/]+)'
_TEST = {
'url': 'http://tv.adobe.com/show/the-complete-picture-with-julieanne-kost',
'info_dict': {
'id': '36',
'title': 'The Complete Picture with Julieanne Kost',
'description': 'md5:fa50867102dcd1aa0ddf2ab039311b27',
},
'playlist_mincount': 136,
}
def _get_element_url(self, element_data):
return element_data['urls'][0]
def _real_extract(self, url):
language, show_urlname = re.match(self._VALID_URL, url).groups()
if not language:
language = 'en'
query = 'language=%s&show_urlname=%s' % (language, show_urlname)
show_data = self._download_json(self._API_BASE_URL + 'show/get/?%s' % query, show_urlname)['data'][0]
return self.playlist_result(
self._extract_playlist_entries(self._API_BASE_URL + 'episode/?%s' % query, show_urlname),
compat_str(show_data['id']),
show_data['show_name'],
show_data['show_description'])
class AdobeTVChannelIE(AdobeTVPlaylistBaseIE):
_VALID_URL = r'https?://tv\.adobe\.com/(?:(?P<language>fr|de|es|jp)/)?channel/(?P<id>[^/]+)(?:/(?P<category_urlname>[^/]+))?'
_TEST = {
'url': 'http://tv.adobe.com/channel/development',
'info_dict': {
'id': 'development',
},
'playlist_mincount': 96,
}
def _get_element_url(self, element_data):
return element_data['url']
def _real_extract(self, url):
language, channel_urlname, category_urlname = re.match(self._VALID_URL, url).groups()
if not language:
language = 'en'
query = 'language=%s&channel_urlname=%s' % (language, channel_urlname)
if category_urlname:
query += '&category_urlname=%s' % category_urlname
return self.playlist_result(
self._extract_playlist_entries(self._API_BASE_URL + 'show/?%s' % query, channel_urlname),
channel_urlname)
class AdobeTVVideoIE(InfoExtractor): class AdobeTVVideoIE(InfoExtractor):
_VALID_URL = r'https?://video\.tv\.adobe\.com/v/(?P<id>\d+)' _VALID_URL = r'https?://video\.tv\.adobe\.com/v/(?P<id>\d+)'
@@ -91,28 +156,25 @@ class AdobeTVVideoIE(InfoExtractor):
def _real_extract(self, url): def _real_extract(self, url):
video_id = self._match_id(url) video_id = self._match_id(url)
video_data = self._download_json(url + '?format=json', video_id)
webpage = self._download_webpage(url, video_id)
player_params = self._parse_json(self._search_regex(
r'var\s+bridge\s*=\s*([^;]+);', webpage, 'player parameters'),
video_id)
formats = [{ formats = [{
'format_id': '%s-%s' % (determine_ext(source['src']), source.get('height')),
'url': source['src'], 'url': source['src'],
'width': source.get('width'), 'width': int_or_none(source.get('width')),
'height': source.get('height'), 'height': int_or_none(source.get('height')),
'tbr': source.get('bitrate'), 'tbr': int_or_none(source.get('bitrate')),
} for source in player_params['sources']] } for source in video_data['sources']]
self._sort_formats(formats)
# For both metadata and downloaded files the duration varies among # For both metadata and downloaded files the duration varies among
# formats. I just pick the max one # formats. I just pick the max one
duration = max(filter(None, [ duration = max(filter(None, [
float_or_none(source.get('duration'), scale=1000) float_or_none(source.get('duration'), scale=1000)
for source in player_params['sources']])) for source in video_data['sources']]))
subtitles = {} subtitles = {}
for translation in player_params.get('translations', []): for translation in video_data.get('translations', []):
lang_id = translation.get('language_w3c') or ISO639Utils.long2short(translation['language_medium']) lang_id = translation.get('language_w3c') or ISO639Utils.long2short(translation['language_medium'])
if lang_id not in subtitles: if lang_id not in subtitles:
subtitles[lang_id] = [] subtitles[lang_id] = []
@@ -124,8 +186,9 @@ class AdobeTVVideoIE(InfoExtractor):
return { return {
'id': video_id, 'id': video_id,
'formats': formats, 'formats': formats,
'title': player_params['title'], 'title': video_data['title'],
'description': self._og_search_description(webpage), 'description': video_data.get('description'),
'thumbnail': video_data['video'].get('poster'),
'duration': duration, 'duration': duration,
'subtitles': subtitles, 'subtitles': subtitles,
} }

View File

@@ -5,6 +5,7 @@ import re
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import ( from ..utils import (
determine_ext,
ExtractorError, ExtractorError,
float_or_none, float_or_none,
xpath_text, xpath_text,
@@ -40,7 +41,8 @@ class AdultSwimIE(InfoExtractor):
'id': 'rQxZvXQ4ROaSOqq-or2Mow', 'id': 'rQxZvXQ4ROaSOqq-or2Mow',
'title': 'Rick and Morty - Pilot', 'title': 'Rick and Morty - Pilot',
'description': "Rick moves in with his daughter's family and establishes himself as a bad influence on his grandson, Morty. " 'description': "Rick moves in with his daughter's family and establishes himself as a bad influence on his grandson, Morty. "
} },
'skip': 'This video is only available for registered users',
}, { }, {
'url': 'http://www.adultswim.com/videos/playlists/american-parenting/putting-francine-out-of-business/', 'url': 'http://www.adultswim.com/videos/playlists/american-parenting/putting-francine-out-of-business/',
'playlist': [ 'playlist': [
@@ -123,7 +125,6 @@ class AdultSwimIE(InfoExtractor):
else: else:
collections = bootstrapped_data['show']['collections'] collections = bootstrapped_data['show']['collections']
collection, video_info = self.find_collection_containing_video(collections, episode_path) collection, video_info = self.find_collection_containing_video(collections, episode_path)
# Video wasn't found in the collections, let's try `slugged_video`. # Video wasn't found in the collections, let's try `slugged_video`.
if video_info is None: if video_info is None:
if bootstrapped_data.get('slugged_video', {}).get('slug') == episode_path: if bootstrapped_data.get('slugged_video', {}).get('slug') == episode_path:
@@ -133,7 +134,15 @@ class AdultSwimIE(InfoExtractor):
show = bootstrapped_data['show'] show = bootstrapped_data['show']
show_title = show['title'] show_title = show['title']
segment_ids = [clip['videoPlaybackID'] for clip in video_info['clips']] stream = video_info.get('stream')
clips = [stream] if stream else video_info.get('clips')
if not clips:
raise ExtractorError(
'This video is only available via cable service provider subscription that'
' is not currently supported. You may want to use --cookies.'
if video_info.get('auth') is True else 'Unable to find stream or clips',
expected=True)
segment_ids = [clip['videoPlaybackID'] for clip in clips]
episode_id = video_info['id'] episode_id = video_info['id']
episode_title = video_info['title'] episode_title = video_info['title']
@@ -142,7 +151,7 @@ class AdultSwimIE(InfoExtractor):
entries = [] entries = []
for part_num, segment_id in enumerate(segment_ids): for part_num, segment_id in enumerate(segment_ids):
segment_url = 'http://www.adultswim.com/videos/api/v0/assets?id=%s&platform=mobile' % segment_id segment_url = 'http://www.adultswim.com/videos/api/v0/assets?id=%s&platform=desktop' % segment_id
segment_title = '%s - %s' % (show_title, episode_title) segment_title = '%s - %s' % (show_title, episode_title)
if len(segment_ids) > 1: if len(segment_ids) > 1:
@@ -156,19 +165,32 @@ class AdultSwimIE(InfoExtractor):
xpath_text(idoc, './/trt', 'segment duration').strip()) xpath_text(idoc, './/trt', 'segment duration').strip())
formats = [] formats = []
file_els = idoc.findall('.//files/file') file_els = idoc.findall('.//files/file') or idoc.findall('./files/file')
unique_urls = []
unique_file_els = []
for file_el in file_els: for file_el in file_els:
media_url = file_el.text
if not media_url or determine_ext(media_url) == 'f4m':
continue
if file_el.text not in unique_urls:
unique_urls.append(file_el.text)
unique_file_els.append(file_el)
for file_el in unique_file_els:
bitrate = file_el.attrib.get('bitrate') bitrate = file_el.attrib.get('bitrate')
ftype = file_el.attrib.get('type') ftype = file_el.attrib.get('type')
media_url = file_el.text
formats.append({ if determine_ext(media_url) == 'm3u8':
'format_id': '%s_%s' % (bitrate, ftype), formats.extend(self._extract_m3u8_formats(
'url': file_el.text.strip(), media_url, segment_title, 'mp4', preference=0, m3u8_id='hls'))
# The bitrate may not be a number (for example: 'iphone') else:
'tbr': int(bitrate) if bitrate.isdigit() else None, formats.append({
'quality': 1 if ftype == 'hd' else -1 'format_id': '%s_%s' % (bitrate, ftype),
}) 'url': file_el.text.strip(),
# The bitrate may not be a number (for example: 'iphone')
'tbr': int(bitrate) if bitrate.isdigit() else None,
})
self._sort_formats(formats) self._sort_formats(formats)

View File

@@ -20,14 +20,14 @@ class AirMozillaIE(InfoExtractor):
'id': '6x4q2w', 'id': '6x4q2w',
'ext': 'mp4', 'ext': 'mp4',
'title': 'Privacy Lab - a meetup for privacy minded people in San Francisco', 'title': 'Privacy Lab - a meetup for privacy minded people in San Francisco',
'thumbnail': 're:https://\w+\.cloudfront\.net/6x4q2w/poster\.jpg\?t=\d+', 'thumbnail': 're:https?://vid\.ly/(?P<id>[0-9a-z-]+)/poster',
'description': 'Brings together privacy professionals and others interested in privacy at for-profits, non-profits, and NGOs in an effort to contribute to the state of the ecosystem...', 'description': 'Brings together privacy professionals and others interested in privacy at for-profits, non-profits, and NGOs in an effort to contribute to the state of the ecosystem...',
'timestamp': 1422487800, 'timestamp': 1422487800,
'upload_date': '20150128', 'upload_date': '20150128',
'location': 'SFO Commons', 'location': 'SFO Commons',
'duration': 3780, 'duration': 3780,
'view_count': int, 'view_count': int,
'categories': ['Main'], 'categories': ['Main', 'Privacy'],
} }
} }

View File

@@ -15,7 +15,8 @@ class AlJazeeraIE(InfoExtractor):
'description': 'As a birth attendant advocating for family planning, Remy is on the frontline of Tondo\'s battle with overcrowding.', 'description': 'As a birth attendant advocating for family planning, Remy is on the frontline of Tondo\'s battle with overcrowding.',
'uploader': 'Al Jazeera English', 'uploader': 'Al Jazeera English',
}, },
'add_ie': ['Brightcove'], 'add_ie': ['BrightcoveLegacy'],
'skip': 'Not accessible from Travis CI server',
} }
def _real_extract(self, url): def _real_extract(self, url):
@@ -31,5 +32,5 @@ class AlJazeeraIE(InfoExtractor):
'playerKey=AQ~~%2CAAAAmtVJIFk~%2CTVGOQ5ZTwJbeMWnq5d_H4MOM57xfzApc' 'playerKey=AQ~~%2CAAAAmtVJIFk~%2CTVGOQ5ZTwJbeMWnq5d_H4MOM57xfzApc'
'&%40videoPlayer={0}'.format(brightcove_id) '&%40videoPlayer={0}'.format(brightcove_id)
), ),
'ie_key': 'Brightcove', 'ie_key': 'BrightcoveLegacy',
} }

View File

@@ -26,8 +26,8 @@ class AnitubeIE(InfoExtractor):
video_id = mobj.group('id') video_id = mobj.group('id')
webpage = self._download_webpage(url, video_id) webpage = self._download_webpage(url, video_id)
key = self._html_search_regex( key = self._search_regex(
r'http://www\.anitube\.se/embed/([A-Za-z0-9_-]*)', webpage, 'key') r'src=["\']https?://[^/]+/embed/([A-Za-z0-9_-]+)', webpage, 'key')
config_xml = self._download_xml( config_xml = self._download_xml(
'http://www.anitube.se/nuevo/econfig.php?key=%s' % key, key) 'http://www.anitube.se/nuevo/econfig.php?key=%s' % key, key)

View File

@@ -13,53 +13,53 @@ from ..utils import (
class AppleTrailersIE(InfoExtractor): class AppleTrailersIE(InfoExtractor):
_VALID_URL = r'https?://(?:www\.)?trailers\.apple\.com/(?:trailers|ca)/(?P<company>[^/]+)/(?P<movie>[^/]+)' _VALID_URL = r'https?://(?:www\.)?trailers\.apple\.com/(?:trailers|ca)/(?P<company>[^/]+)/(?P<movie>[^/]+)'
_TESTS = [{ _TESTS = [{
"url": "http://trailers.apple.com/trailers/wb/manofsteel/", 'url': 'http://trailers.apple.com/trailers/wb/manofsteel/',
'info_dict': { 'info_dict': {
'id': 'manofsteel', 'id': 'manofsteel',
}, },
"playlist": [ 'playlist': [
{ {
"md5": "d97a8e575432dbcb81b7c3acb741f8a8", 'md5': 'd97a8e575432dbcb81b7c3acb741f8a8',
"info_dict": { 'info_dict': {
"id": "manofsteel-trailer4", 'id': 'manofsteel-trailer4',
"ext": "mov", 'ext': 'mov',
"duration": 111, 'duration': 111,
"title": "Trailer 4", 'title': 'Trailer 4',
"upload_date": "20130523", 'upload_date': '20130523',
"uploader_id": "wb", 'uploader_id': 'wb',
}, },
}, },
{ {
"md5": "b8017b7131b721fb4e8d6f49e1df908c", 'md5': 'b8017b7131b721fb4e8d6f49e1df908c',
"info_dict": { 'info_dict': {
"id": "manofsteel-trailer3", 'id': 'manofsteel-trailer3',
"ext": "mov", 'ext': 'mov',
"duration": 182, 'duration': 182,
"title": "Trailer 3", 'title': 'Trailer 3',
"upload_date": "20130417", 'upload_date': '20130417',
"uploader_id": "wb", 'uploader_id': 'wb',
}, },
}, },
{ {
"md5": "d0f1e1150989b9924679b441f3404d48", 'md5': 'd0f1e1150989b9924679b441f3404d48',
"info_dict": { 'info_dict': {
"id": "manofsteel-trailer", 'id': 'manofsteel-trailer',
"ext": "mov", 'ext': 'mov',
"duration": 148, 'duration': 148,
"title": "Trailer", 'title': 'Trailer',
"upload_date": "20121212", 'upload_date': '20121212',
"uploader_id": "wb", 'uploader_id': 'wb',
}, },
}, },
{ {
"md5": "5fe08795b943eb2e757fa95cb6def1cb", 'md5': '5fe08795b943eb2e757fa95cb6def1cb',
"info_dict": { 'info_dict': {
"id": "manofsteel-teaser", 'id': 'manofsteel-teaser',
"ext": "mov", 'ext': 'mov',
"duration": 93, 'duration': 93,
"title": "Teaser", 'title': 'Teaser',
"upload_date": "20120721", 'upload_date': '20120721',
"uploader_id": "wb", 'uploader_id': 'wb',
}, },
}, },
] ]

View File

@@ -14,8 +14,8 @@ from ..utils import (
parse_duration, parse_duration,
unified_strdate, unified_strdate,
xpath_text, xpath_text,
parse_xml,
) )
from ..compat import compat_etree_fromstring
class ARDMediathekIE(InfoExtractor): class ARDMediathekIE(InfoExtractor):
@@ -161,7 +161,7 @@ class ARDMediathekIE(InfoExtractor):
raise ExtractorError('This program is only suitable for those aged 12 and older. Video %s is therefore only available between 20 pm and 6 am.' % video_id, expected=True) raise ExtractorError('This program is only suitable for those aged 12 and older. Video %s is therefore only available between 20 pm and 6 am.' % video_id, expected=True)
if re.search(r'[\?&]rss($|[=&])', url): if re.search(r'[\?&]rss($|[=&])', url):
doc = parse_xml(webpage) doc = compat_etree_fromstring(webpage.encode('utf-8'))
if doc.tag == 'rss': if doc.tag == 'rss':
return GenericIE()._extract_rss(url, video_id, doc) return GenericIE()._extract_rss(url, video_id, doc)

View File

@@ -4,6 +4,10 @@ from __future__ import unicode_literals
import re import re
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import (
compat_parse_qs,
compat_urllib_parse_urlparse,
)
from ..utils import ( from ..utils import (
find_xpath_attr, find_xpath_attr,
unified_strdate, unified_strdate,
@@ -77,7 +81,13 @@ class ArteTVPlus7IE(InfoExtractor):
def _extract_from_webpage(self, webpage, video_id, lang): def _extract_from_webpage(self, webpage, video_id, lang):
json_url = self._html_search_regex( json_url = self._html_search_regex(
[r'arte_vp_url=["\'](.*?)["\']', r'data-url=["\']([^"]+)["\']'], [r'arte_vp_url=["\'](.*?)["\']', r'data-url=["\']([^"]+)["\']'],
webpage, 'json vp url') webpage, 'json vp url', default=None)
if not json_url:
iframe_url = self._html_search_regex(
r'<iframe[^>]+src=(["\'])(?P<url>.+\bjson_url=.+?)\1',
webpage, 'iframe url', group='url')
json_url = compat_parse_qs(
compat_urllib_parse_urlparse(iframe_url).query)['json_url'][0]
return self._extract_from_json_url(json_url, video_id, lang) return self._extract_from_json_url(json_url, video_id, lang)
def _extract_from_json_url(self, json_url, video_id, lang): def _extract_from_json_url(self, json_url, video_id, lang):

View File

@@ -7,11 +7,11 @@ from .common import InfoExtractor
from ..compat import ( from ..compat import (
compat_str, compat_str,
compat_urllib_parse, compat_urllib_parse,
compat_urllib_request,
) )
from ..utils import ( from ..utils import (
int_or_none, int_or_none,
float_or_none, float_or_none,
sanitized_Request,
xpath_text, xpath_text,
ExtractorError, ExtractorError,
) )
@@ -63,7 +63,7 @@ class AtresPlayerIE(InfoExtractor):
'j_password': password, 'j_password': password,
} }
request = compat_urllib_request.Request( request = sanitized_Request(
self._LOGIN_URL, compat_urllib_parse.urlencode(login_form).encode('utf-8')) self._LOGIN_URL, compat_urllib_parse.urlencode(login_form).encode('utf-8'))
request.add_header('Content-Type', 'application/x-www-form-urlencoded') request.add_header('Content-Type', 'application/x-www-form-urlencoded')
response = self._download_webpage( response = self._download_webpage(
@@ -94,7 +94,7 @@ class AtresPlayerIE(InfoExtractor):
formats = [] formats = []
for fmt in ['windows', 'android_tablet']: for fmt in ['windows', 'android_tablet']:
request = compat_urllib_request.Request( request = sanitized_Request(
self._URL_VIDEO_TEMPLATE.format(fmt, episode_id, timestamp_shifted, token)) self._URL_VIDEO_TEMPLATE.format(fmt, episode_id, timestamp_shifted, token))
request.add_header('User-Agent', self._USER_AGENT) request.add_header('User-Agent', self._USER_AGENT)

View File

@@ -0,0 +1,80 @@
# coding: utf-8
from __future__ import unicode_literals
from .common import InfoExtractor
from ..utils import (
int_or_none,
parse_iso8601,
sanitized_Request,
)
class AudiMediaIE(InfoExtractor):
_VALID_URL = r'https?://(?:www\.)?audimedia\.tv/(?:en|de)/vid/(?P<id>[^/?#]+)'
_TEST = {
'url': 'https://audimedia.tv/en/vid/60-seconds-of-audi-sport-104-2015-wec-bahrain-rookie-test',
'md5': '79a8b71c46d49042609795ab59779b66',
'info_dict': {
'id': '1564',
'ext': 'mp4',
'title': '60 Seconds of Audi Sport 104/2015 - WEC Bahrain, Rookie Test',
'description': 'md5:60e5d30a78ced725f7b8d34370762941',
'upload_date': '20151124',
'timestamp': 1448354940,
'duration': 74022,
'view_count': int,
}
}
# extracted from https://audimedia.tv/assets/embed/embedded-player.js (dataSourceAuthToken)
_AUTH_TOKEN = 'e25b42847dba18c6c8816d5d8ce94c326e06823ebf0859ed164b3ba169be97f2'
def _real_extract(self, url):
display_id = self._match_id(url)
webpage = self._download_webpage(url, display_id)
raw_payload = self._search_regex(r'<script[^>]+class="amtv-embed"[^>]+id="([^"]+)"', webpage, 'raw payload')
_, stage_mode, video_id, lang = raw_payload.split('-')
# TODO: handle s and e stage_mode (live streams and ended live streams)
if stage_mode not in ('s', 'e'):
request = sanitized_Request(
'https://audimedia.tv/api/video/v1/videos/%s?embed[]=video_versions&embed[]=thumbnail_image&where[content_language_iso]=%s' % (video_id, lang),
headers={'X-Auth-Token': self._AUTH_TOKEN})
json_data = self._download_json(request, video_id)['results']
formats = []
stream_url_hls = json_data.get('stream_url_hls')
if stream_url_hls:
m3u8_formats = self._extract_m3u8_formats(stream_url_hls, video_id, 'mp4', entry_protocol='m3u8_native', m3u8_id='hls', fatal=False)
if m3u8_formats:
formats.extend(m3u8_formats)
stream_url_hds = json_data.get('stream_url_hds')
if stream_url_hds:
f4m_formats = self._extract_f4m_formats(json_data.get('stream_url_hds') + '?hdcore=3.4.0', video_id, -1, f4m_id='hds', fatal=False)
if f4m_formats:
formats.extend(f4m_formats)
for video_version in json_data.get('video_versions'):
video_version_url = video_version.get('download_url') or video_version.get('stream_url')
if not video_version_url:
continue
formats.append({
'url': video_version_url,
'width': int_or_none(video_version.get('width')),
'height': int_or_none(video_version.get('height')),
'abr': int_or_none(video_version.get('audio_bitrate')),
'vbr': int_or_none(video_version.get('video_bitrate')),
})
self._sort_formats(formats)
return {
'id': video_id,
'title': json_data['title'],
'description': json_data.get('subtitle'),
'thumbnail': json_data.get('thumbnail_image', {}).get('file'),
'timestamp': parse_iso8601(json_data.get('publication_date')),
'duration': int_or_none(json_data.get('duration')),
'view_count': int_or_none(json_data.get('view_count')),
'formats': formats,
}

View File

@@ -6,13 +6,13 @@ import itertools
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import ( from ..compat import (
compat_urllib_parse, compat_urllib_parse,
compat_urllib_request,
compat_str, compat_str,
) )
from ..utils import ( from ..utils import (
ExtractorError, ExtractorError,
int_or_none, int_or_none,
float_or_none, float_or_none,
sanitized_Request,
) )
@@ -57,7 +57,7 @@ class BambuserIE(InfoExtractor):
'pass': password, 'pass': password,
} }
request = compat_urllib_request.Request( request = sanitized_Request(
self._LOGIN_URL, compat_urllib_parse.urlencode(login_form).encode('utf-8')) self._LOGIN_URL, compat_urllib_parse.urlencode(login_form).encode('utf-8'))
request.add_header('Referer', self._LOGIN_URL) request.add_header('Referer', self._LOGIN_URL)
response = self._download_webpage( response = self._download_webpage(
@@ -126,7 +126,7 @@ class BambuserChannelIE(InfoExtractor):
'&sort=created&access_mode=0%2C1%2C2&limit={count}' '&sort=created&access_mode=0%2C1%2C2&limit={count}'
'&method=broadcast&format=json&vid_older_than={last}' '&method=broadcast&format=json&vid_older_than={last}'
).format(user=user, count=self._STEP, last=last_id) ).format(user=user, count=self._STEP, last=last_id)
req = compat_urllib_request.Request(req_url) req = sanitized_Request(req_url)
# Without setting this header, we wouldn't get any result # Without setting this header, we wouldn't get any result
req.add_header('Referer', 'http://bambuser.com/channel/%s' % user) req.add_header('Referer', 'http://bambuser.com/channel/%s' % user)
data = self._download_json( data = self._download_json(

View File

@@ -10,6 +10,8 @@ from ..compat import (
) )
from ..utils import ( from ..utils import (
ExtractorError, ExtractorError,
float_or_none,
int_or_none,
) )
@@ -52,11 +54,11 @@ class BandcampIE(InfoExtractor):
ext, abr_str = format_id.split('-', 1) ext, abr_str = format_id.split('-', 1)
formats.append({ formats.append({
'format_id': format_id, 'format_id': format_id,
'url': format_url, 'url': self._proto_relative_url(format_url, 'http:'),
'ext': ext, 'ext': ext,
'vcodec': 'none', 'vcodec': 'none',
'acodec': ext, 'acodec': ext,
'abr': int(abr_str), 'abr': int_or_none(abr_str),
}) })
self._sort_formats(formats) self._sort_formats(formats)
@@ -65,7 +67,7 @@ class BandcampIE(InfoExtractor):
'id': compat_str(data['id']), 'id': compat_str(data['id']),
'title': data['title'], 'title': data['title'],
'formats': formats, 'formats': formats,
'duration': float(data['duration']), 'duration': float_or_none(data.get('duration')),
} }
else: else:
raise ExtractorError('No free songs found') raise ExtractorError('No free songs found')
@@ -93,8 +95,8 @@ class BandcampIE(InfoExtractor):
final_url_webpage = self._download_webpage(request_url, video_id, 'Requesting download url') final_url_webpage = self._download_webpage(request_url, video_id, 'Requesting download url')
# If we could correctly generate the .rand field the url would be # If we could correctly generate the .rand field the url would be
# in the "download_url" key # in the "download_url" key
final_url = self._search_regex( final_url = self._proto_relative_url(self._search_regex(
r'"retry_url":"(.*?)"', final_url_webpage, 'final video URL') r'"retry_url":"(.+?)"', final_url_webpage, 'final video URL'), 'http:')
return { return {
'id': video_id, 'id': video_id,

View File

@@ -2,7 +2,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import re import re
import xml.etree.ElementTree
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import ( from ..utils import (
@@ -11,28 +10,45 @@ from ..utils import (
int_or_none, int_or_none,
parse_duration, parse_duration,
parse_iso8601, parse_iso8601,
remove_end,
unescapeHTML,
)
from ..compat import (
compat_etree_fromstring,
compat_HTTPError,
) )
from ..compat import compat_HTTPError
class BBCCoUkIE(InfoExtractor): class BBCCoUkIE(InfoExtractor):
IE_NAME = 'bbc.co.uk' IE_NAME = 'bbc.co.uk'
IE_DESC = 'BBC iPlayer' IE_DESC = 'BBC iPlayer'
_VALID_URL = r'https?://(?:www\.)?bbc\.co\.uk/(?:(?:(?:programmes|iplayer(?:/[^/]+)?/(?:episode|playlist))/)|music/clips[/#])(?P<id>[\da-z]{8})' _ID_REGEX = r'[pb][\da-z]{7}'
_VALID_URL = r'https?://(?:www\.)?bbc\.co\.uk/(?:(?:programmes/(?!articles/)|iplayer(?:/[^/]+)?/(?:episode/|playlist/))|music/clips[/#])(?P<id>%s)' % _ID_REGEX
_MEDIASELECTOR_URLS = [ _MEDIASELECTOR_URLS = [
# Provides HQ HLS streams with even better quality that pc mediaset but fails
# with geolocation in some cases when it's even not geo restricted at all (e.g.
# http://www.bbc.co.uk/programmes/b06bp7lf). Also may fail with selectionunavailable.
'http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/iptv-all/vpid/%s',
'http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/pc/vpid/%s', 'http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/pc/vpid/%s',
] ]
_MEDIASELECTION_NS = 'http://bbc.co.uk/2008/mp/mediaselection'
_EMP_PLAYLIST_NS = 'http://bbc.co.uk/2008/emp/playlist'
_NAMESPACES = (
_MEDIASELECTION_NS,
_EMP_PLAYLIST_NS,
)
_TESTS = [ _TESTS = [
{ {
'url': 'http://www.bbc.co.uk/programmes/b039g8p7', 'url': 'http://www.bbc.co.uk/programmes/b039g8p7',
'info_dict': { 'info_dict': {
'id': 'b039d07m', 'id': 'b039d07m',
'ext': 'flv', 'ext': 'flv',
'title': 'Kaleidoscope, Leonard Cohen', 'title': 'Leonard Cohen, Kaleidoscope - BBC Radio 4',
'description': 'The Canadian poet and songwriter reflects on his musical career.', 'description': 'The Canadian poet and songwriter reflects on his musical career.',
'duration': 1740,
}, },
'params': { 'params': {
# rtmp download # rtmp download
@@ -95,7 +111,8 @@ class BBCCoUkIE(InfoExtractor):
'params': { 'params': {
# rtmp download # rtmp download
'skip_download': True, 'skip_download': True,
} },
'skip': 'Episode is no longer available on BBC iPlayer Radio',
}, { }, {
'url': 'http://www.bbc.co.uk/music/clips/p02frcc3', 'url': 'http://www.bbc.co.uk/music/clips/p02frcc3',
'note': 'Audio', 'note': 'Audio',
@@ -152,6 +169,21 @@ class BBCCoUkIE(InfoExtractor):
'skip_download': True, 'skip_download': True,
}, },
'skip': 'geolocation', 'skip': 'geolocation',
}, {
# iptv-all mediaset fails with geolocation however there is no geo restriction
# for this programme at all
'url': 'http://www.bbc.co.uk/programmes/b06bp7lf',
'info_dict': {
'id': 'b06bp7kf',
'ext': 'flv',
'title': "Annie Mac's Friday Night, B.Traits sits in for Annie",
'description': 'B.Traits sits in for Annie Mac with a Mini-Mix from Disclosure.',
'duration': 10800,
},
'params': {
# rtmp download
'skip_download': True,
},
}, { }, {
'url': 'http://www.bbc.co.uk/iplayer/playlist/p01dvks4', 'url': 'http://www.bbc.co.uk/iplayer/playlist/p01dvks4',
'only_matching': True, 'only_matching': True,
@@ -174,6 +206,7 @@ class BBCCoUkIE(InfoExtractor):
def _extract_connection(self, connection, programme_id): def _extract_connection(self, connection, programme_id):
formats = [] formats = []
kind = connection.get('kind')
protocol = connection.get('protocol') protocol = connection.get('protocol')
supplier = connection.get('supplier') supplier = connection.get('supplier')
if protocol == 'http': if protocol == 'http':
@@ -189,11 +222,17 @@ class BBCCoUkIE(InfoExtractor):
# Skip DASH until supported # Skip DASH until supported
elif transfer_format == 'dash': elif transfer_format == 'dash':
pass pass
elif transfer_format == 'hls':
m3u8_formats = self._extract_m3u8_formats(
href, programme_id, ext='mp4', entry_protocol='m3u8_native',
m3u8_id=supplier, fatal=False)
if m3u8_formats:
formats.extend(m3u8_formats)
# Direct link # Direct link
else: else:
formats.append({ formats.append({
'url': href, 'url': href,
'format_id': supplier, 'format_id': supplier or kind or protocol,
}) })
elif protocol == 'rtmp': elif protocol == 'rtmp':
application = connection.get('application', 'ondemand') application = connection.get('application', 'ondemand')
@@ -213,16 +252,24 @@ class BBCCoUkIE(InfoExtractor):
return formats return formats
def _extract_items(self, playlist): def _extract_items(self, playlist):
return playlist.findall('./{http://bbc.co.uk/2008/emp/playlist}item') return playlist.findall('./{%s}item' % self._EMP_PLAYLIST_NS)
def _findall_ns(self, element, xpath):
elements = []
for ns in self._NAMESPACES:
elements.extend(element.findall(xpath % ns))
return elements
def _extract_medias(self, media_selection): def _extract_medias(self, media_selection):
error = media_selection.find('./{http://bbc.co.uk/2008/mp/mediaselection}error') error = media_selection.find('./{%s}error' % self._MEDIASELECTION_NS)
if error is None:
media_selection.find('./{%s}error' % self._EMP_PLAYLIST_NS)
if error is not None: if error is not None:
raise BBCCoUkIE.MediaSelectionError(error.get('id')) raise BBCCoUkIE.MediaSelectionError(error.get('id'))
return media_selection.findall('./{http://bbc.co.uk/2008/mp/mediaselection}media') return self._findall_ns(media_selection, './{%s}media')
def _extract_connections(self, media): def _extract_connections(self, media):
return media.findall('./{http://bbc.co.uk/2008/mp/mediaselection}connection') return self._findall_ns(media, './{%s}connection')
def _extract_video(self, media, programme_id): def _extract_video(self, media, programme_id):
formats = [] formats = []
@@ -236,13 +283,14 @@ class BBCCoUkIE(InfoExtractor):
conn_formats = self._extract_connection(connection, programme_id) conn_formats = self._extract_connection(connection, programme_id)
for format in conn_formats: for format in conn_formats:
format.update({ format.update({
'format_id': '%s_%s' % (service, format['format_id']),
'width': width, 'width': width,
'height': height, 'height': height,
'vbr': vbr, 'vbr': vbr,
'vcodec': vcodec, 'vcodec': vcodec,
'filesize': file_size, 'filesize': file_size,
}) })
if service:
format['format_id'] = '%s_%s' % (service, format['format_id'])
formats.extend(conn_formats) formats.extend(conn_formats)
return formats return formats
@@ -287,7 +335,7 @@ class BBCCoUkIE(InfoExtractor):
return self._download_media_selector_url( return self._download_media_selector_url(
mediaselector_url % programme_id, programme_id) mediaselector_url % programme_id, programme_id)
except BBCCoUkIE.MediaSelectionError as e: except BBCCoUkIE.MediaSelectionError as e:
if e.id == 'notukerror': if e.id in ('notukerror', 'geolocation', 'selectionunavailable'):
last_exception = e last_exception = e
continue continue
self._raise_extractor_error(e) self._raise_extractor_error(e)
@@ -298,8 +346,8 @@ class BBCCoUkIE(InfoExtractor):
media_selection = self._download_xml( media_selection = self._download_xml(
url, programme_id, 'Downloading media selection XML') url, programme_id, 'Downloading media selection XML')
except ExtractorError as ee: except ExtractorError as ee:
if isinstance(ee.cause, compat_HTTPError) and ee.cause.code == 403: if isinstance(ee.cause, compat_HTTPError) and ee.cause.code in (403, 404):
media_selection = xml.etree.ElementTree.fromstring(ee.cause.read().decode('utf-8')) media_selection = compat_etree_fromstring(ee.cause.read().decode('utf-8'))
else: else:
raise raise
return self._process_media_selector(media_selection, programme_id) return self._process_media_selector(media_selection, programme_id)
@@ -357,7 +405,7 @@ class BBCCoUkIE(InfoExtractor):
url, playlist_id, 'Downloading legacy playlist XML') url, playlist_id, 'Downloading legacy playlist XML')
def _extract_from_legacy_playlist(self, playlist, playlist_id): def _extract_from_legacy_playlist(self, playlist, playlist_id):
no_items = playlist.find('./{http://bbc.co.uk/2008/emp/playlist}noItems') no_items = playlist.find('./{%s}noItems' % self._EMP_PLAYLIST_NS)
if no_items is not None: if no_items is not None:
reason = no_items.get('reason') reason = no_items.get('reason')
if reason == 'preAvailability': if reason == 'preAvailability':
@@ -374,8 +422,9 @@ class BBCCoUkIE(InfoExtractor):
kind = item.get('kind') kind = item.get('kind')
if kind != 'programme' and kind != 'radioProgramme': if kind != 'programme' and kind != 'radioProgramme':
continue continue
title = playlist.find('./{http://bbc.co.uk/2008/emp/playlist}title').text title = playlist.find('./{%s}title' % self._EMP_PLAYLIST_NS).text
description = playlist.find('./{http://bbc.co.uk/2008/emp/playlist}summary').text description_el = playlist.find('./{%s}summary' % self._EMP_PLAYLIST_NS)
description = description_el.text if description_el is not None else None
def get_programme_id(item): def get_programme_id(item):
def get_from_attributes(item): def get_from_attributes(item):
@@ -384,16 +433,18 @@ class BBCCoUkIE(InfoExtractor):
if value and re.match(r'^[pb][\da-z]{7}$', value): if value and re.match(r'^[pb][\da-z]{7}$', value):
return value return value
get_from_attributes(item) get_from_attributes(item)
mediator = item.find('./{http://bbc.co.uk/2008/emp/playlist}mediator') mediator = item.find('./{%s}mediator' % self._EMP_PLAYLIST_NS)
if mediator is not None: if mediator is not None:
return get_from_attributes(mediator) return get_from_attributes(mediator)
programme_id = get_programme_id(item) programme_id = get_programme_id(item)
duration = int_or_none(item.get('duration')) duration = int_or_none(item.get('duration'))
# TODO: programme_id can be None and media items can be incorporated right inside
# playlist's item (e.g. http://www.bbc.com/turkce/haberler/2015/06/150615_telabyad_kentin_cogu) if programme_id:
# as f4m and m3u8 formats, subtitles = self._download_media_selector(programme_id)
formats, subtitles = self._download_media_selector(programme_id) else:
formats, subtitles = self._process_media_selector(item, playlist_id)
programme_id = playlist_id
return programme_id, title, description, duration, formats, subtitles return programme_id, title, description, duration, formats, subtitles
@@ -403,6 +454,7 @@ class BBCCoUkIE(InfoExtractor):
webpage = self._download_webpage(url, group_id, 'Downloading video page') webpage = self._download_webpage(url, group_id, 'Downloading video page')
programme_id = None programme_id = None
duration = None
tviplayer = self._search_regex( tviplayer = self._search_regex(
r'mediator\.bind\(({.+?})\s*,\s*document\.getElementById', r'mediator\.bind\(({.+?})\s*,\s*document\.getElementById',
@@ -415,14 +467,16 @@ class BBCCoUkIE(InfoExtractor):
if not programme_id: if not programme_id:
programme_id = self._search_regex( programme_id = self._search_regex(
r'"vpid"\s*:\s*"([\da-z]{8})"', webpage, 'vpid', fatal=False, default=None) r'"vpid"\s*:\s*"(%s)"' % self._ID_REGEX, webpage, 'vpid', fatal=False, default=None)
if programme_id: if programme_id:
formats, subtitles = self._download_media_selector(programme_id) formats, subtitles = self._download_media_selector(programme_id)
title = self._og_search_title(webpage) title = self._og_search_title(webpage)
description = self._search_regex( description = self._search_regex(
r'<p class="[^"]*medium-description[^"]*">([^<]+)</p>', r'<p class="[^"]*medium-description[^"]*">([^<]+)</p>',
webpage, 'description', fatal=False) webpage, 'description', default=None)
if not description:
description = self._html_search_meta('description', webpage)
else: else:
programme_id, title, description, duration, formats, subtitles = self._download_playlist(group_id) programme_id, title, description, duration, formats, subtitles = self._download_playlist(group_id)
@@ -445,6 +499,9 @@ class BBCIE(BBCCoUkIE):
_VALID_URL = r'https?://(?:www\.)?bbc\.(?:com|co\.uk)/(?:[^/]+/)+(?P<id>[^/#?]+)' _VALID_URL = r'https?://(?:www\.)?bbc\.(?:com|co\.uk)/(?:[^/]+/)+(?P<id>[^/#?]+)'
_MEDIASELECTOR_URLS = [ _MEDIASELECTOR_URLS = [
# Provides HQ HLS streams but fails with geolocation in some cases when it's
# even not geo restricted at all
'http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/iptv-all/vpid/%s',
# Provides more formats, namely direct mp4 links, but fails on some videos with # Provides more formats, namely direct mp4 links, but fails on some videos with
# notukerror for non UK (?) users (e.g. # notukerror for non UK (?) users (e.g.
# http://www.bbc.com/travel/story/20150625-sri-lankas-spicy-secret) # http://www.bbc.com/travel/story/20150625-sri-lankas-spicy-secret)
@@ -454,8 +511,7 @@ class BBCIE(BBCCoUkIE):
] ]
_TESTS = [{ _TESTS = [{
# article with multiple videos embedded with data-media-meta containing # article with multiple videos embedded with data-playable containing vpids
# playlist.sxml, externalId and no direct video links
'url': 'http://www.bbc.com/news/world-europe-32668511', 'url': 'http://www.bbc.com/news/world-europe-32668511',
'info_dict': { 'info_dict': {
'id': 'world-europe-32668511', 'id': 'world-europe-32668511',
@@ -464,7 +520,7 @@ class BBCIE(BBCCoUkIE):
}, },
'playlist_count': 2, 'playlist_count': 2,
}, { }, {
# article with multiple videos embedded with data-media-meta (more videos) # article with multiple videos embedded with data-playable (more videos)
'url': 'http://www.bbc.com/news/business-28299555', 'url': 'http://www.bbc.com/news/business-28299555',
'info_dict': { 'info_dict': {
'id': 'business-28299555', 'id': 'business-28299555',
@@ -475,6 +531,7 @@ class BBCIE(BBCCoUkIE):
'skip': 'Save time', 'skip': 'Save time',
}, { }, {
# article with multiple videos embedded with `new SMP()` # article with multiple videos embedded with `new SMP()`
# broken
'url': 'http://www.bbc.co.uk/blogs/adamcurtis/entries/3662a707-0af9-3149-963f-47bea720b460', 'url': 'http://www.bbc.co.uk/blogs/adamcurtis/entries/3662a707-0af9-3149-963f-47bea720b460',
'info_dict': { 'info_dict': {
'id': '3662a707-0af9-3149-963f-47bea720b460', 'id': '3662a707-0af9-3149-963f-47bea720b460',
@@ -482,12 +539,13 @@ class BBCIE(BBCCoUkIE):
}, },
'playlist_count': 18, 'playlist_count': 18,
}, { }, {
# single video embedded with mediaAssetPage.init() # single video embedded with data-playable containing vpid
'url': 'http://www.bbc.com/news/world-europe-32041533', 'url': 'http://www.bbc.com/news/world-europe-32041533',
'info_dict': { 'info_dict': {
'id': 'p02mprgb', 'id': 'p02mprgb',
'ext': 'mp4', 'ext': 'mp4',
'title': 'Aerial footage showed the site of the crash in the Alps - courtesy BFM TV', 'title': 'Aerial footage showed the site of the crash in the Alps - courtesy BFM TV',
'description': 'md5:2868290467291b37feda7863f7a83f54',
'duration': 47, 'duration': 47,
'timestamp': 1427219242, 'timestamp': 1427219242,
'upload_date': '20150324', 'upload_date': '20150324',
@@ -497,15 +555,14 @@ class BBCIE(BBCCoUkIE):
'skip_download': True, 'skip_download': True,
} }
}, { }, {
# article with single video embedded with data-media-meta containing # article with single video embedded with data-playable containing XML playlist
# direct video links (for now these are extracted) and playlist.xml (with # with direct video links as progressiveDownloadUrl (for now these are extracted)
# media items as f4m and m3u8 - currently unsupported) # and playlist with f4m and m3u8 as streamingUrl
'url': 'http://www.bbc.com/turkce/haberler/2015/06/150615_telabyad_kentin_cogu', 'url': 'http://www.bbc.com/turkce/haberler/2015/06/150615_telabyad_kentin_cogu',
'info_dict': { 'info_dict': {
'id': '150615_telabyad_kentin_cogu', 'id': '150615_telabyad_kentin_cogu',
'ext': 'mp4', 'ext': 'mp4',
'title': "YPG: Tel Abyad'ın tamamı kontrolümüzde", 'title': "YPG: Tel Abyad'ın tamamı kontrolümüzde",
'duration': 47,
'timestamp': 1434397334, 'timestamp': 1434397334,
'upload_date': '20150615', 'upload_date': '20150615',
}, },
@@ -513,13 +570,12 @@ class BBCIE(BBCCoUkIE):
'skip_download': True, 'skip_download': True,
} }
}, { }, {
# single video embedded with mediaAssetPage.init() (regional section) # single video embedded with data-playable containing XML playlists (regional section)
'url': 'http://www.bbc.com/mundo/video_fotos/2015/06/150619_video_honduras_militares_hospitales_corrupcion_aw', 'url': 'http://www.bbc.com/mundo/video_fotos/2015/06/150619_video_honduras_militares_hospitales_corrupcion_aw',
'info_dict': { 'info_dict': {
'id': '150619_video_honduras_militares_hospitales_corrupcion_aw', 'id': '150619_video_honduras_militares_hospitales_corrupcion_aw',
'ext': 'mp4', 'ext': 'mp4',
'title': 'Honduras militariza sus hospitales por nuevo escándalo de corrupción', 'title': 'Honduras militariza sus hospitales por nuevo escándalo de corrupción',
'duration': 87,
'timestamp': 1434713142, 'timestamp': 1434713142,
'upload_date': '20150619', 'upload_date': '20150619',
}, },
@@ -534,6 +590,7 @@ class BBCIE(BBCCoUkIE):
'ext': 'mp4', 'ext': 'mp4',
'title': '''Judge Mindy Glazer: "I'm sorry to see you here... I always wondered what happened to you"''', 'title': '''Judge Mindy Glazer: "I'm sorry to see you here... I always wondered what happened to you"''',
'duration': 56, 'duration': 56,
'description': '''Judge Mindy Glazer: "I'm sorry to see you here... I always wondered what happened to you"''',
}, },
'params': { 'params': {
'skip_download': True, 'skip_download': True,
@@ -561,27 +618,35 @@ class BBCIE(BBCCoUkIE):
'ext': 'mp4', 'ext': 'mp4',
'title': 'Hyundai Santa Fe Sport: Rock star', 'title': 'Hyundai Santa Fe Sport: Rock star',
'description': 'md5:b042a26142c4154a6e472933cf20793d', 'description': 'md5:b042a26142c4154a6e472933cf20793d',
'timestamp': 1368473503, 'timestamp': 1415867444,
'upload_date': '20130513', 'upload_date': '20141113',
}, },
'params': { 'params': {
# rtmp download # rtmp download
'skip_download': True, 'skip_download': True,
} }
}, { }, {
# single video with playlist.sxml URL # single video with playlist.sxml URL in playlist param
'url': 'http://www.bbc.com/sport/0/football/33653409', 'url': 'http://www.bbc.com/sport/0/football/33653409',
'info_dict': { 'info_dict': {
'id': 'p02xycnp', 'id': 'p02xycnp',
'ext': 'mp4', 'ext': 'mp4',
'title': 'Transfers: Cristiano Ronaldo to Man Utd, Arsenal to spend?', 'title': 'Transfers: Cristiano Ronaldo to Man Utd, Arsenal to spend?',
'description': 'md5:398fca0e2e701c609d726e034fa1fc89', 'description': 'BBC Sport\'s David Ornstein has the latest transfer gossip, including rumours of a Manchester United return for Cristiano Ronaldo.',
'duration': 140, 'duration': 140,
}, },
'params': { 'params': {
# rtmp download # rtmp download
'skip_download': True, 'skip_download': True,
} }
}, {
# article with multiple videos embedded with playlist.sxml in playlist param
'url': 'http://www.bbc.com/sport/0/football/34475836',
'info_dict': {
'id': '34475836',
'title': 'What Liverpool can expect from Klopp',
},
'playlist_count': 3,
}, { }, {
# single video with playlist URL from weather section # single video with playlist URL from weather section
'url': 'http://www.bbc.com/weather/features/33601775', 'url': 'http://www.bbc.com/weather/features/33601775',
@@ -594,7 +659,7 @@ class BBCIE(BBCCoUkIE):
@classmethod @classmethod
def suitable(cls, url): def suitable(cls, url):
return False if BBCCoUkIE.suitable(url) else super(BBCIE, cls).suitable(url) return False if BBCCoUkIE.suitable(url) or BBCCoUkArticleIE.suitable(url) else super(BBCIE, cls).suitable(url)
def _extract_from_media_meta(self, media_meta, video_id): def _extract_from_media_meta(self, media_meta, video_id):
# Direct links to media in media metadata (e.g. # Direct links to media in media metadata (e.g.
@@ -623,40 +688,109 @@ class BBCIE(BBCCoUkIE):
return [], [] return [], []
def _extract_from_playlist_sxml(self, url, playlist_id, timestamp):
programme_id, title, description, duration, formats, subtitles = \
self._process_legacy_playlist_url(url, playlist_id)
self._sort_formats(formats)
return {
'id': programme_id,
'title': title,
'description': description,
'duration': duration,
'timestamp': timestamp,
'formats': formats,
'subtitles': subtitles,
}
def _real_extract(self, url): def _real_extract(self, url):
playlist_id = self._match_id(url) playlist_id = self._match_id(url)
webpage = self._download_webpage(url, playlist_id) webpage = self._download_webpage(url, playlist_id)
timestamp = parse_iso8601(self._search_regex( timestamp = None
[r'"datePublished":\s*"([^"]+)', playlist_title = None
r'<meta[^>]+property="article:published_time"[^>]+content="([^"]+)"', playlist_description = None
r'itemprop="datePublished"[^>]+datetime="([^"]+)"'],
webpage, 'date', default=None))
# single video with playlist.sxml URL (e.g. http://www.bbc.com/sport/0/football/3365340ng) ld = self._parse_json(
playlist = self._search_regex( self._search_regex(
r'<param[^>]+name="playlist"[^>]+value="([^"]+)"', r'(?s)<script type="application/ld\+json">(.+?)</script>',
webpage, 'playlist', default=None) webpage, 'ld json', default='{}'),
if playlist: playlist_id, fatal=False)
programme_id, title, description, duration, formats, subtitles = \ if ld:
self._process_legacy_playlist_url(playlist, playlist_id) timestamp = parse_iso8601(ld.get('datePublished'))
self._sort_formats(formats) playlist_title = ld.get('headline')
return { playlist_description = ld.get('articleBody')
'id': programme_id,
'title': title, if not timestamp:
'description': description, timestamp = parse_iso8601(self._search_regex(
'duration': duration, [r'<meta[^>]+property="article:published_time"[^>]+content="([^"]+)"',
'timestamp': timestamp, r'itemprop="datePublished"[^>]+datetime="([^"]+)"',
'formats': formats, r'"datePublished":\s*"([^"]+)'],
'subtitles': subtitles, webpage, 'date', default=None))
}
entries = []
# article with multiple videos embedded with playlist.sxml (e.g.
# http://www.bbc.com/sport/0/football/34475836)
playlists = re.findall(r'<param[^>]+name="playlist"[^>]+value="([^"]+)"', webpage)
playlists.extend(re.findall(r'data-media-id="([^"]+/playlist\.sxml)"', webpage))
if playlists:
entries = [
self._extract_from_playlist_sxml(playlist_url, playlist_id, timestamp)
for playlist_url in playlists]
# news article with multiple videos embedded with data-playable
data_playables = re.findall(r'data-playable=(["\'])({.+?})\1', webpage)
if data_playables:
for _, data_playable_json in data_playables:
data_playable = self._parse_json(
unescapeHTML(data_playable_json), playlist_id, fatal=False)
if not data_playable:
continue
settings = data_playable.get('settings', {})
if settings:
# data-playable with video vpid in settings.playlistObject.items (e.g.
# http://www.bbc.com/news/world-us-canada-34473351)
playlist_object = settings.get('playlistObject', {})
if playlist_object:
items = playlist_object.get('items')
if items and isinstance(items, list):
title = playlist_object['title']
description = playlist_object.get('summary')
duration = int_or_none(items[0].get('duration'))
programme_id = items[0].get('vpid')
formats, subtitles = self._download_media_selector(programme_id)
self._sort_formats(formats)
entries.append({
'id': programme_id,
'title': title,
'description': description,
'timestamp': timestamp,
'duration': duration,
'formats': formats,
'subtitles': subtitles,
})
else:
# data-playable without vpid but with a playlist.sxml URLs
# in otherSettings.playlist (e.g.
# http://www.bbc.com/turkce/multimedya/2015/10/151010_vid_ankara_patlama_ani)
playlist = data_playable.get('otherSettings', {}).get('playlist', {})
if playlist:
entries.append(self._extract_from_playlist_sxml(
playlist.get('progressiveDownloadUrl'), playlist_id, timestamp))
if entries:
playlist_title = playlist_title or remove_end(self._og_search_title(webpage), ' - BBC News')
playlist_description = playlist_description or self._og_search_description(webpage, default=None)
return self.playlist_result(entries, playlist_id, playlist_title, playlist_description)
# single video story (e.g. http://www.bbc.com/travel/story/20150625-sri-lankas-spicy-secret) # single video story (e.g. http://www.bbc.com/travel/story/20150625-sri-lankas-spicy-secret)
programme_id = self._search_regex( programme_id = self._search_regex(
[r'data-video-player-vpid="([\da-z]{8})"', [r'data-video-player-vpid="(%s)"' % self._ID_REGEX,
r'<param[^>]+name="externalIdentifier"[^>]+value="([\da-z]{8})"'], r'<param[^>]+name="externalIdentifier"[^>]+value="(%s)"' % self._ID_REGEX,
r'videoId\s*:\s*["\'](%s)["\']' % self._ID_REGEX],
webpage, 'vpid', default=None) webpage, 'vpid', default=None)
if programme_id: if programme_id:
formats, subtitles = self._download_media_selector(programme_id) formats, subtitles = self._download_media_selector(programme_id)
self._sort_formats(formats) self._sort_formats(formats)
@@ -689,7 +823,7 @@ class BBCIE(BBCCoUkIE):
# Multiple video article (e.g. # Multiple video article (e.g.
# http://www.bbc.co.uk/blogs/adamcurtis/entries/3662a707-0af9-3149-963f-47bea720b460) # http://www.bbc.co.uk/blogs/adamcurtis/entries/3662a707-0af9-3149-963f-47bea720b460)
EMBED_URL = r'https?://(?:www\.)?bbc\.co\.uk/(?:[^/]+/)+[\da-z]{8}(?:\b[^"]+)?' EMBED_URL = r'https?://(?:www\.)?bbc\.co\.uk/(?:[^/]+/)+%s(?:\b[^"]+)?' % self._ID_REGEX
entries = [] entries = []
for match in extract_all(r'new\s+SMP\(({.+?})\)'): for match in extract_all(r'new\s+SMP\(({.+?})\)'):
embed_url = match.get('playerSettings', {}).get('externalEmbedUrl') embed_url = match.get('playerSettings', {}).get('externalEmbedUrl')
@@ -778,3 +912,33 @@ class BBCIE(BBCCoUkIE):
}) })
return self.playlist_result(entries, playlist_id, playlist_title, playlist_description) return self.playlist_result(entries, playlist_id, playlist_title, playlist_description)
class BBCCoUkArticleIE(InfoExtractor):
_VALID_URL = 'http://www.bbc.co.uk/programmes/articles/(?P<id>[a-zA-Z0-9]+)'
IE_NAME = 'bbc.co.uk:article'
IE_DESC = 'BBC articles'
_TEST = {
'url': 'http://www.bbc.co.uk/programmes/articles/3jNQLTMrPlYGTBn0WV6M2MS/not-your-typical-role-model-ada-lovelace-the-19th-century-programmer',
'info_dict': {
'id': '3jNQLTMrPlYGTBn0WV6M2MS',
'title': 'Calculating Ada: The Countess of Computing - Not your typical role model: Ada Lovelace the 19th century programmer - BBC Four',
'description': 'Hannah Fry reveals some of her surprising discoveries about Ada Lovelace during filming.',
},
'playlist_count': 4,
'add_ie': ['BBCCoUk'],
}
def _real_extract(self, url):
playlist_id = self._match_id(url)
webpage = self._download_webpage(url, playlist_id)
title = self._og_search_title(webpage)
description = self._og_search_description(webpage).strip()
entries = [self.url_result(programme_url) for programme_url in re.findall(
r'<div[^>]+typeof="Clip"[^>]+resource="([^"]+)"', webpage)]
return self.playlist_result(entries, playlist_id, title, description)

View File

@@ -1,65 +1,105 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import re
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import (
compat_chr,
compat_ord,
compat_urllib_parse_unquote,
)
from ..utils import (
int_or_none,
parse_iso8601,
)
class BeegIE(InfoExtractor): class BeegIE(InfoExtractor):
_VALID_URL = r'https?://(?:www\.)?beeg\.com/(?P<id>\d+)' _VALID_URL = r'https?://(?:www\.)?beeg\.com/(?P<id>\d+)'
_TEST = { _TEST = {
'url': 'http://beeg.com/5416503', 'url': 'http://beeg.com/5416503',
'md5': '1bff67111adb785c51d1b42959ec10e5', 'md5': '46c384def73b33dbc581262e5ee67cef',
'info_dict': { 'info_dict': {
'id': '5416503', 'id': '5416503',
'ext': 'mp4', 'ext': 'mp4',
'title': 'Sultry Striptease', 'title': 'Sultry Striptease',
'description': 'md5:6db3c6177972822aaba18652ff59c773', 'description': 'md5:d22219c09da287c14bed3d6c37ce4bc2',
'categories': list, # NSFW 'timestamp': 1391813355,
'thumbnail': 're:https?://.*\.jpg$', 'upload_date': '20140207',
'duration': 383,
'tags': list,
'age_limit': 18, 'age_limit': 18,
} }
} }
def _real_extract(self, url): def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url) video_id = self._match_id(url)
video_id = mobj.group('id')
webpage = self._download_webpage(url, video_id) video = self._download_json(
'http://beeg.com/api/v5/video/%s' % video_id, video_id)
quality_arr = self._search_regex( def split(o, e):
r'(?s)var\s+qualityArr\s*=\s*{\s*(.+?)\s*}', webpage, 'quality formats') def cut(s, x):
n.append(s[:x])
return s[x:]
n = []
r = len(o) % e
if r > 0:
o = cut(o, r)
while len(o) > e:
o = cut(o, e)
n.append(o)
return n
formats = [{ def decrypt_key(key):
'url': fmt[1], # Reverse engineered from http://static.beeg.com/cpl/1105.js
'format_id': fmt[0], a = '5ShMcIQlssOd7zChAIOlmeTZDaUxULbJRnywYaiB'
'height': int(fmt[0][:-1]), e = compat_urllib_parse_unquote(key)
} for fmt in re.findall(r"'([^']+)'\s*:\s*'([^']+)'", quality_arr)] o = ''.join([
compat_chr(compat_ord(e[n]) - compat_ord(a[n % len(a)]) % 21)
for n in range(len(e))])
return ''.join(split(o, 3)[::-1])
def decrypt_url(encrypted_url):
encrypted_url = self._proto_relative_url(
encrypted_url.replace('{DATA_MARKERS}', ''), 'http:')
key = self._search_regex(
r'/key=(.*?)%2Cend=', encrypted_url, 'key', default=None)
if not key:
return encrypted_url
return encrypted_url.replace(key, decrypt_key(key))
formats = []
for format_id, video_url in video.items():
if not video_url:
continue
height = self._search_regex(
r'^(\d+)[pP]$', format_id, 'height', default=None)
if not height:
continue
formats.append({
'url': decrypt_url(video_url),
'format_id': format_id,
'height': int(height),
})
self._sort_formats(formats) self._sort_formats(formats)
title = self._html_search_regex( title = video['title']
r'<title>([^<]+)\s*-\s*beeg\.?</title>', webpage, 'title') video_id = video.get('id') or video_id
display_id = video.get('code')
description = video.get('desc')
description = self._html_search_regex( timestamp = parse_iso8601(video.get('date'), ' ')
r'<meta name="description" content="([^"]*)"', duration = int_or_none(video.get('duration'))
webpage, 'description', fatal=False)
thumbnail = self._html_search_regex(
r'\'previewer.url\'\s*:\s*"([^"]*)"',
webpage, 'thumbnail', fatal=False)
categories_str = self._html_search_regex( tags = [tag.strip() for tag in video['tags'].split(',')] if video.get('tags') else None
r'<meta name="keywords" content="([^"]+)"', webpage, 'categories', fatal=False)
categories = (
None if categories_str is None
else categories_str.split(','))
return { return {
'id': video_id, 'id': video_id,
'display_id': display_id,
'title': title, 'title': title,
'description': description, 'description': description,
'thumbnail': thumbnail, 'timestamp': timestamp,
'categories': categories, 'duration': duration,
'tags': tags,
'formats': formats, 'formats': formats,
'age_limit': 18, 'age_limit': 18,
} }

View File

@@ -4,7 +4,7 @@ from __future__ import unicode_literals
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import ( from ..utils import (
int_or_none, int_or_none,
fix_xml_ampersands, unescapeHTML,
) )
@@ -17,26 +17,24 @@ class BildIE(InfoExtractor):
'info_dict': { 'info_dict': {
'id': '38184146', 'id': '38184146',
'ext': 'mp4', 'ext': 'mp4',
'title': 'BILD hat sie getestet', 'title': 'Das können die neuen iPads',
'description': 'md5:a4058c4fa2a804ab59c00d7244bbf62f',
'thumbnail': 're:^https?://.*\.jpg$', 'thumbnail': 're:^https?://.*\.jpg$',
'duration': 196, 'duration': 196,
'description': 'Mit dem iPad Air 2 und dem iPad Mini 3 hat Apple zwei neue Tablet-Modelle präsentiert. BILD-Reporter Sven Stein durfte die Geräte bereits testen. ',
} }
} }
def _real_extract(self, url): def _real_extract(self, url):
video_id = self._match_id(url) video_id = self._match_id(url)
xml_url = url.split(".bild.html")[0] + ",view=xml.bild.xml" video_data = self._download_json(
doc = self._download_xml(xml_url, video_id, transform_source=fix_xml_ampersands) url.split('.bild.html')[0] + ',view=json.bild.html', video_id)
duration = int_or_none(doc.attrib.get('duration'), scale=1000)
return { return {
'id': video_id, 'id': video_id,
'title': doc.attrib['ueberschrift'], 'title': unescapeHTML(video_data['title']).strip(),
'description': doc.attrib.get('text'), 'description': unescapeHTML(video_data.get('description')),
'url': doc.attrib['src'], 'url': video_data['clipList'][0]['srces'][0]['src'],
'thumbnail': doc.attrib.get('img'), 'thumbnail': video_data.get('poster'),
'duration': duration, 'duration': int_or_none(video_data.get('durationSec')),
} }

View File

@@ -2,141 +2,109 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import re import re
import itertools
import json
import xml.etree.ElementTree as ET
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import compat_str
from ..utils import ( from ..utils import (
int_or_none, int_or_none,
unified_strdate, unescapeHTML,
ExtractorError, ExtractorError,
xpath_text,
) )
class BiliBiliIE(InfoExtractor): class BiliBiliIE(InfoExtractor):
_VALID_URL = r'http://www\.bilibili\.(?:tv|com)/video/av(?P<id>[0-9]+)/' _VALID_URL = r'http://www\.bilibili\.(?:tv|com)/video/av(?P<id>\d+)(?:/index_(?P<page_num>\d+).html)?'
_TESTS = [{ _TESTS = [{
'url': 'http://www.bilibili.tv/video/av1074402/', 'url': 'http://www.bilibili.tv/video/av1074402/',
'md5': '2c301e4dab317596e837c3e7633e7d86', 'md5': '2c301e4dab317596e837c3e7633e7d86',
'info_dict': { 'info_dict': {
'id': '1074402_part1', 'id': '1554319',
'ext': 'flv', 'ext': 'flv',
'title': '【金坷垃】金泡沫', 'title': '【金坷垃】金泡沫',
'duration': 308, 'duration': 308313,
'upload_date': '20140420', 'upload_date': '20140420',
'thumbnail': 're:^https?://.+\.jpg', 'thumbnail': 're:^https?://.+\.jpg',
'description': 'md5:ce18c2a2d2193f0df2917d270f2e5923',
'timestamp': 1397983878,
'uploader': '菊子桑',
}, },
}, { }, {
'url': 'http://www.bilibili.com/video/av1041170/', 'url': 'http://www.bilibili.com/video/av1041170/',
'info_dict': { 'info_dict': {
'id': '1041170', 'id': '1041170',
'title': '【BD1080P】刀语【诸神&异域】', 'title': '【BD1080P】刀语【诸神&异域】',
'description': '这是个神奇的故事~每个人不留弹幕不给走哦~切利哦!~',
'uploader': '枫叶逝去',
'timestamp': 1396501299,
}, },
'playlist_count': 9, 'playlist_count': 9,
}] }]
def _real_extract(self, url): def _real_extract(self, url):
video_id = self._match_id(url) mobj = re.match(self._VALID_URL, url)
webpage = self._download_webpage(url, video_id) video_id = mobj.group('id')
page_num = mobj.group('page_num') or '1'
if '(此视频不存在或被删除)' in webpage: view_data = self._download_json(
raise ExtractorError( 'http://api.bilibili.com/view?type=json&appkey=8e9fc618fbd41e28&id=%s&page=%s' % (video_id, page_num),
'The video does not exist or was deleted', expected=True) video_id)
if 'error' in view_data:
raise ExtractorError('%s said: %s' % (self.IE_NAME, view_data['error']), expected=True)
if '>你没有权限浏览! 由于版权相关问题 我们不对您所在的地区提供服务<' in webpage: cid = view_data['cid']
raise ExtractorError( title = unescapeHTML(view_data['title'])
'The video is not available in your region due to copyright reasons',
expected=True)
video_code = self._search_regex( doc = self._download_xml(
r'(?s)<div itemprop="video".*?>(.*?)</div>', webpage, 'video code') 'http://interface.bilibili.com/v_cdn_play?appkey=8e9fc618fbd41e28&cid=%s' % cid,
cid,
'Downloading page %s/%s' % (page_num, view_data['pages'])
)
title = self._html_search_meta( if xpath_text(doc, './result') == 'error':
'media:title', video_code, 'title', fatal=True) raise ExtractorError('%s said: %s' % (self.IE_NAME, xpath_text(doc, './message')), expected=True)
duration_str = self._html_search_meta(
'duration', video_code, 'duration')
if duration_str is None:
duration = None
else:
duration_mobj = re.match(
r'^T(?:(?P<hours>[0-9]+)H)?(?P<minutes>[0-9]+)M(?P<seconds>[0-9]+)S$',
duration_str)
duration = (
int_or_none(duration_mobj.group('hours'), default=0) * 3600 +
int(duration_mobj.group('minutes')) * 60 +
int(duration_mobj.group('seconds')))
upload_date = unified_strdate(self._html_search_meta(
'uploadDate', video_code, fatal=False))
thumbnail = self._html_search_meta(
'thumbnailUrl', video_code, 'thumbnail', fatal=False)
cid = self._search_regex(r'cid=(\d+)', webpage, 'cid')
entries = [] entries = []
lq_page = self._download_webpage( for durl in doc.findall('./durl'):
'http://interface.bilibili.com/v_cdn_play?appkey=1&cid=%s' % cid, size = xpath_text(durl, ['./filesize', './size'])
video_id,
note='Downloading LQ video info'
)
try:
err_info = json.loads(lq_page)
raise ExtractorError(
'BiliBili said: ' + err_info['error_text'], expected=True)
except ValueError:
pass
lq_doc = ET.fromstring(lq_page)
lq_durls = lq_doc.findall('./durl')
hq_doc = self._download_xml(
'http://interface.bilibili.com/playurl?appkey=1&cid=%s' % cid,
video_id,
note='Downloading HQ video info',
fatal=False,
)
if hq_doc is not False:
hq_durls = hq_doc.findall('./durl')
assert len(lq_durls) == len(hq_durls)
else:
hq_durls = itertools.repeat(None)
i = 1
for lq_durl, hq_durl in zip(lq_durls, hq_durls):
formats = [{ formats = [{
'format_id': 'lq', 'url': durl.find('./url').text,
'quality': 1, 'filesize': int_or_none(size),
'url': lq_durl.find('./url').text, 'ext': 'flv',
'filesize': int_or_none(
lq_durl.find('./size'), get_attr='text'),
}] }]
if hq_durl is not None: backup_urls = durl.find('./backup_url')
formats.append({ if backup_urls is not None:
'format_id': 'hq', for backup_url in backup_urls.findall('./url'):
'quality': 2, formats.append({'url': backup_url.text})
'ext': 'flv', formats.reverse()
'url': hq_durl.find('./url').text,
'filesize': int_or_none(
hq_durl.find('./size'), get_attr='text'),
})
self._sort_formats(formats)
entries.append({ entries.append({
'id': '%s_part%d' % (video_id, i), 'id': '%s_part%s' % (cid, xpath_text(durl, './order')),
'title': title, 'title': title,
'duration': int_or_none(xpath_text(durl, './length'), 1000),
'formats': formats, 'formats': formats,
'duration': duration,
'upload_date': upload_date,
'thumbnail': thumbnail,
}) })
i += 1 info = {
'id': compat_str(cid),
return { 'title': title,
'_type': 'multi_video', 'description': view_data.get('description'),
'entries': entries, 'thumbnail': view_data.get('pic'),
'id': video_id, 'uploader': view_data.get('author'),
'title': title 'timestamp': int_or_none(view_data.get('created')),
'view_count': int_or_none(view_data.get('play')),
'duration': int_or_none(xpath_text(doc, './timelength')),
} }
if len(entries) == 1:
entries[0].update(info)
return entries[0]
else:
info.update({
'_type': 'multi_video',
'id': video_id,
'entries': entries,
})
return info

View File

@@ -4,14 +4,12 @@ import re
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import ( from ..compat import compat_urlparse
compat_urllib_request,
compat_urlparse,
)
from ..utils import ( from ..utils import (
clean_html, clean_html,
int_or_none, int_or_none,
parse_iso8601, parse_iso8601,
sanitized_Request,
unescapeHTML, unescapeHTML,
xpath_text, xpath_text,
xpath_with_ns, xpath_with_ns,
@@ -219,7 +217,7 @@ class BlipTVIE(InfoExtractor):
for lang, url in subtitles_urls.items(): for lang, url in subtitles_urls.items():
# For some weird reason, blip.tv serves a video instead of subtitles # For some weird reason, blip.tv serves a video instead of subtitles
# when we request with a common UA # when we request with a common UA
req = compat_urllib_request.Request(url) req = sanitized_Request(url)
req.add_header('User-Agent', 'youtube-dl') req.add_header('User-Agent', 'youtube-dl')
subtitles[lang] = [{ subtitles[lang] = [{
# The extension is 'srt' but it's actually an 'ass' file # The extension is 'srt' but it's actually an 'ass' file

View File

@@ -6,9 +6,9 @@ from .common import InfoExtractor
class BloombergIE(InfoExtractor): class BloombergIE(InfoExtractor):
_VALID_URL = r'https?://www\.bloomberg\.com/news/videos/[^/]+/(?P<id>[^/?#]+)' _VALID_URL = r'https?://(?:www\.)?bloomberg\.com/(?:[^/]+/)*(?P<id>[^/?#]+)'
_TEST = { _TESTS = [{
'url': 'http://www.bloomberg.com/news/videos/b/aaeae121-5949-481e-a1ce-4562db6f5df2', 'url': 'http://www.bloomberg.com/news/videos/b/aaeae121-5949-481e-a1ce-4562db6f5df2',
# The md5 checksum changes # The md5 checksum changes
'info_dict': { 'info_dict': {
@@ -17,22 +17,39 @@ class BloombergIE(InfoExtractor):
'title': 'Shah\'s Presentation on Foreign-Exchange Strategies', 'title': 'Shah\'s Presentation on Foreign-Exchange Strategies',
'description': 'md5:a8ba0302912d03d246979735c17d2761', 'description': 'md5:a8ba0302912d03d246979735c17d2761',
}, },
} }, {
'url': 'http://www.bloomberg.com/news/articles/2015-11-12/five-strange-things-that-have-been-happening-in-financial-markets',
'only_matching': True,
}, {
'url': 'http://www.bloomberg.com/politics/videos/2015-11-25/karl-rove-on-jeb-bush-s-struggles-stopping-trump',
'only_matching': True,
}]
def _real_extract(self, url): def _real_extract(self, url):
name = self._match_id(url) name = self._match_id(url)
webpage = self._download_webpage(url, name) webpage = self._download_webpage(url, name)
video_id = self._search_regex(r'"bmmrId":"(.+?)"', webpage, 'id') video_id = self._search_regex(
r'["\']bmmrId["\']\s*:\s*(["\'])(?P<url>.+?)\1',
webpage, 'id', group='url')
title = re.sub(': Video$', '', self._og_search_title(webpage)) title = re.sub(': Video$', '', self._og_search_title(webpage))
embed_info = self._download_json( embed_info = self._download_json(
'http://www.bloomberg.com/api/embed?id=%s' % video_id, video_id) 'http://www.bloomberg.com/api/embed?id=%s' % video_id, video_id)
formats = [] formats = []
for stream in embed_info['streams']: for stream in embed_info['streams']:
if stream["muxing_format"] == "TS": stream_url = stream.get('url')
formats.extend(self._extract_m3u8_formats(stream['url'], video_id)) if not stream_url:
continue
if stream['muxing_format'] == 'TS':
m3u8_formats = self._extract_m3u8_formats(
stream_url, video_id, 'mp4', m3u8_id='hls', fatal=False)
if m3u8_formats:
formats.extend(m3u8_formats)
else: else:
formats.extend(self._extract_f4m_formats(stream['url'], video_id)) f4m_formats = self._extract_f4m_formats(
stream_url, video_id, f4m_id='hds', fatal=False)
if f4m_formats:
formats.extend(f4m_formats)
self._sort_formats(formats) self._sort_formats(formats)
return { return {

View File

@@ -18,6 +18,7 @@ class BreakIE(InfoExtractor):
'id': '2468056', 'id': '2468056',
'ext': 'mp4', 'ext': 'mp4',
'title': 'When Girls Act Like D-Bags', 'title': 'When Girls Act Like D-Bags',
'age_limit': 13,
} }
}, { }, {
'url': 'http://www.break.com/video/ugc/baby-flex-2773063', 'url': 'http://www.break.com/video/ugc/baby-flex-2773063',

View File

@@ -3,15 +3,14 @@ from __future__ import unicode_literals
import re import re
import json import json
import xml.etree.ElementTree
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import ( from ..compat import (
compat_etree_fromstring,
compat_parse_qs, compat_parse_qs,
compat_str, compat_str,
compat_urllib_parse, compat_urllib_parse,
compat_urllib_parse_urlparse, compat_urllib_parse_urlparse,
compat_urllib_request,
compat_urlparse, compat_urlparse,
compat_xml_parse_error, compat_xml_parse_error,
) )
@@ -20,12 +19,18 @@ from ..utils import (
ExtractorError, ExtractorError,
find_xpath_attr, find_xpath_attr,
fix_xml_ampersands, fix_xml_ampersands,
float_or_none,
js_to_json,
int_or_none,
parse_iso8601,
sanitized_Request,
unescapeHTML, unescapeHTML,
unsmuggle_url, unsmuggle_url,
) )
class BrightcoveIE(InfoExtractor): class BrightcoveLegacyIE(InfoExtractor):
IE_NAME = 'brightcove:legacy'
_VALID_URL = r'(?:https?://.*brightcove\.com/(services|viewer).*?\?|brightcove:)(?P<query>.*)' _VALID_URL = r'(?:https?://.*brightcove\.com/(services|viewer).*?\?|brightcove:)(?P<query>.*)'
_FEDERATED_URL_TEMPLATE = 'http://c.brightcove.com/services/viewer/htmlFederated?%s' _FEDERATED_URL_TEMPLATE = 'http://c.brightcove.com/services/viewer/htmlFederated?%s'
@@ -119,7 +124,7 @@ class BrightcoveIE(InfoExtractor):
object_str = fix_xml_ampersands(object_str) object_str = fix_xml_ampersands(object_str)
try: try:
object_doc = xml.etree.ElementTree.fromstring(object_str.encode('utf-8')) object_doc = compat_etree_fromstring(object_str.encode('utf-8'))
except compat_xml_parse_error: except compat_xml_parse_error:
return return
@@ -245,7 +250,7 @@ class BrightcoveIE(InfoExtractor):
def _get_video_info(self, video_id, query_str, query, referer=None): def _get_video_info(self, video_id, query_str, query, referer=None):
request_url = self._FEDERATED_URL_TEMPLATE % query_str request_url = self._FEDERATED_URL_TEMPLATE % query_str
req = compat_urllib_request.Request(request_url) req = sanitized_Request(request_url)
linkBase = query.get('linkBaseURL') linkBase = query.get('linkBaseURL')
if linkBase is not None: if linkBase is not None:
referer = linkBase[0] referer = linkBase[0]
@@ -346,3 +351,172 @@ class BrightcoveIE(InfoExtractor):
if 'url' not in info and not info.get('formats'): if 'url' not in info and not info.get('formats'):
raise ExtractorError('Unable to extract video url for %s' % info['id']) raise ExtractorError('Unable to extract video url for %s' % info['id'])
return info return info
class BrightcoveNewIE(InfoExtractor):
IE_NAME = 'brightcove:new'
_VALID_URL = r'https?://players\.brightcove\.net/(?P<account_id>\d+)/(?P<player_id>[^/]+)_(?P<embed>[^/]+)/index\.html\?.*videoId=(?P<video_id>\d+)'
_TESTS = [{
'url': 'http://players.brightcove.net/929656772001/e41d32dc-ec74-459e-a845-6c69f7b724ea_default/index.html?videoId=4463358922001',
'md5': 'c8100925723840d4b0d243f7025703be',
'info_dict': {
'id': '4463358922001',
'ext': 'mp4',
'title': 'Meet the man behind Popcorn Time',
'description': 'md5:eac376a4fe366edc70279bfb681aea16',
'duration': 165.768,
'timestamp': 1441391203,
'upload_date': '20150904',
'uploader_id': '929656772001',
'formats': 'mincount:22',
},
}, {
# with rtmp streams
'url': 'http://players.brightcove.net/4036320279001/5d112ed9-283f-485f-a7f9-33f42e8bc042_default/index.html?videoId=4279049078001',
'info_dict': {
'id': '4279049078001',
'ext': 'mp4',
'title': 'Titansgrave: Chapter 0',
'description': 'Titansgrave: Chapter 0',
'duration': 1242.058,
'timestamp': 1433556729,
'upload_date': '20150606',
'uploader_id': '4036320279001',
'formats': 'mincount:41',
},
'params': {
'skip_download': True,
}
}]
@staticmethod
def _extract_urls(webpage):
# Reference:
# 1. http://docs.brightcove.com/en/video-cloud/brightcove-player/guides/publish-video.html#setvideoiniframe
# 2. http://docs.brightcove.com/en/video-cloud/brightcove-player/guides/publish-video.html#setvideousingjavascript)
# 3. http://docs.brightcove.com/en/video-cloud/brightcove-player/guides/embed-in-page.html
entries = []
# Look for iframe embeds [1]
for _, url in re.findall(
r'<iframe[^>]+src=(["\'])((?:https?:)//players\.brightcove\.net/\d+/[^/]+/index\.html.+?)\1', webpage):
entries.append(url)
# Look for embed_in_page embeds [2]
for video_id, account_id, player_id, embed in re.findall(
# According to examples from [3] it's unclear whether video id
# may be optional and what to do when it is
r'''(?sx)
<video[^>]+
data-video-id=["\'](\d+)["\'][^>]*>.*?
</video>.*?
<script[^>]+
src=["\'](?:https?:)?//players\.brightcove\.net/
(\d+)/([\da-f-]+)_([^/]+)/index\.min\.js
''', webpage):
entries.append(
'http://players.brightcove.net/%s/%s_%s/index.html?videoId=%s'
% (account_id, player_id, embed, video_id))
return entries
def _real_extract(self, url):
account_id, player_id, embed, video_id = re.match(self._VALID_URL, url).groups()
webpage = self._download_webpage(
'http://players.brightcove.net/%s/%s_%s/index.min.js'
% (account_id, player_id, embed), video_id)
policy_key = None
catalog = self._search_regex(
r'catalog\(({.+?})\);', webpage, 'catalog', default=None)
if catalog:
catalog = self._parse_json(
js_to_json(catalog), video_id, fatal=False)
if catalog:
policy_key = catalog.get('policyKey')
if not policy_key:
policy_key = self._search_regex(
r'policyKey\s*:\s*(["\'])(?P<pk>.+?)\1',
webpage, 'policy key', group='pk')
req = sanitized_Request(
'https://edge.api.brightcove.com/playback/v1/accounts/%s/videos/%s'
% (account_id, video_id),
headers={'Accept': 'application/json;pk=%s' % policy_key})
json_data = self._download_json(req, video_id)
title = json_data['name']
formats = []
for source in json_data.get('sources', []):
source_type = source.get('type')
src = source.get('src')
if source_type == 'application/x-mpegURL':
if not src:
continue
m3u8_formats = self._extract_m3u8_formats(
src, video_id, 'mp4', entry_protocol='m3u8_native',
m3u8_id='hls', fatal=False)
if m3u8_formats:
formats.extend(m3u8_formats)
else:
streaming_src = source.get('streaming_src')
stream_name, app_name = source.get('stream_name'), source.get('app_name')
if not src and not streaming_src and (not stream_name or not app_name):
continue
tbr = float_or_none(source.get('avg_bitrate'), 1000)
height = int_or_none(source.get('height'))
f = {
'tbr': tbr,
'width': int_or_none(source.get('width')),
'height': height,
'filesize': int_or_none(source.get('size')),
'container': source.get('container'),
'vcodec': source.get('codec'),
'ext': source.get('container').lower(),
}
def build_format_id(kind):
format_id = kind
if tbr:
format_id += '-%dk' % int(tbr)
if height:
format_id += '-%dp' % height
return format_id
if src or streaming_src:
f.update({
'url': src or streaming_src,
'format_id': build_format_id('http' if src else 'http-streaming'),
'preference': 2 if src else 1,
})
else:
f.update({
'url': app_name,
'play_path': stream_name,
'format_id': build_format_id('rtmp'),
})
formats.append(f)
self._sort_formats(formats)
description = json_data.get('description')
thumbnail = json_data.get('thumbnail')
timestamp = parse_iso8601(json_data.get('published_at'))
duration = float_or_none(json_data.get('duration'), 1000)
tags = json_data.get('tags', [])
return {
'id': video_id,
'title': title,
'description': description,
'thumbnail': thumbnail,
'duration': duration,
'timestamp': timestamp,
'uploader_id': account_id,
'formats': formats,
'tags': tags,
}

View File

@@ -14,9 +14,10 @@ class BYUtvIE(InfoExtractor):
'info_dict': { 'info_dict': {
'id': 'studio-c-season-5-episode-5', 'id': 'studio-c-season-5-episode-5',
'ext': 'mp4', 'ext': 'mp4',
'description': 'md5:5438d33774b6bdc662f9485a340401cc', 'description': 'md5:e07269172baff037f8e8bf9956bc9747',
'title': 'Season 5 Episode 5', 'title': 'Season 5 Episode 5',
'thumbnail': 're:^https?://.*\.jpg$' 'thumbnail': 're:^https?://.*\.jpg$',
'duration': 1486.486,
}, },
'params': { 'params': {
'skip_download': True, 'skip_download': True,

View File

@@ -4,38 +4,53 @@ from __future__ import unicode_literals
import re import re
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import parse_duration
class Canalc2IE(InfoExtractor): class Canalc2IE(InfoExtractor):
IE_NAME = 'canalc2.tv' IE_NAME = 'canalc2.tv'
_VALID_URL = r'http://.*?\.canalc2\.tv/video\.asp\?.*?idVideo=(?P<id>\d+)' _VALID_URL = r'https?://(?:www\.)?canalc2\.tv/video/(?P<id>\d+)'
_TEST = { _TEST = {
'url': 'http://www.canalc2.tv/video.asp?idVideo=12163&voir=oui', 'url': 'http://www.canalc2.tv/video/12163',
'md5': '060158428b650f896c542dfbb3d6487f', 'md5': '060158428b650f896c542dfbb3d6487f',
'info_dict': { 'info_dict': {
'id': '12163', 'id': '12163',
'ext': 'mp4', 'ext': 'flv',
'title': 'Terrasses du Numérique' 'title': 'Terrasses du Numérique',
'duration': 122,
},
'params': {
'skip_download': True, # Requires rtmpdump
} }
} }
def _real_extract(self, url): def _real_extract(self, url):
video_id = re.match(self._VALID_URL, url).group('id') video_id = self._match_id(url)
# We need to set the voir field for getting the file name
url = 'http://www.canalc2.tv/video.asp?idVideo=%s&voir=oui' % video_id
webpage = self._download_webpage(url, video_id) webpage = self._download_webpage(url, video_id)
file_name = self._search_regex( video_url = self._search_regex(
r"so\.addVariable\('file','(.*?)'\);", r'jwplayer\((["\'])Player\1\)\.setup\({[^}]*file\s*:\s*(["\'])(?P<file>.+?)\2',
webpage, 'file name') webpage, 'video_url', group='file')
video_url = 'http://vod-flash.u-strasbg.fr:8080/' + file_name formats = [{'url': video_url}]
if video_url.startswith('rtmp://'):
rtmp = re.search(r'^(?P<url>rtmp://[^/]+/(?P<app>.+/))(?P<play_path>mp4:.+)$', video_url)
formats[0].update({
'url': rtmp.group('url'),
'ext': 'flv',
'app': rtmp.group('app'),
'play_path': rtmp.group('play_path'),
'page_url': url,
})
title = self._html_search_regex( title = self._html_search_regex(
r'class="evenement8">(.*?)</a>', webpage, 'title') r'(?s)class="[^"]*col_description[^"]*">.*?<h3>(.*?)</h3>', webpage, 'title')
duration = parse_duration(self._search_regex(
r'id=["\']video_duree["\'][^>]*>([^<]+)',
webpage, 'duration', fatal=False))
return { return {
'id': video_id, 'id': video_id,
'ext': 'mp4',
'url': video_url,
'title': title, 'title': title,
'duration': duration,
'formats': formats,
} }

View File

@@ -78,7 +78,8 @@ class CanalplusIE(InfoExtractor):
if video_id is None: if video_id is None:
webpage = self._download_webpage(url, display_id) webpage = self._download_webpage(url, display_id)
video_id = self._search_regex( video_id = self._search_regex(
r'<canal:player[^>]+?videoId="(\d+)"', webpage, 'video id') [r'<canal:player[^>]+?videoId=(["\'])(?P<id>\d+)', r'id=["\']canal_video_player(?P<id>\d+)'],
webpage, 'video id', group='id')
info_url = self._VIDEO_INFO_TEMPLATE % (site_id, video_id) info_url = self._VIDEO_INFO_TEMPLATE % (site_id, video_id)
doc = self._download_xml(info_url, video_id, 'Downloading video XML') doc = self._download_xml(info_url, video_id, 'Downloading video XML')

View File

@@ -1,6 +1,10 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import (
sanitized_Request,
smuggle_url,
)
class CBSIE(InfoExtractor): class CBSIE(InfoExtractor):
@@ -46,13 +50,19 @@ class CBSIE(InfoExtractor):
def _real_extract(self, url): def _real_extract(self, url):
display_id = self._match_id(url) display_id = self._match_id(url)
webpage = self._download_webpage(url, display_id) request = sanitized_Request(url)
# Android UA is served with higher quality (720p) streams (see
# https://github.com/rg3/youtube-dl/issues/7490)
request.add_header('User-Agent', 'Mozilla/5.0 (Linux; Android 4.4; Nexus 5)')
webpage = self._download_webpage(request, display_id)
real_id = self._search_regex( real_id = self._search_regex(
[r"video\.settings\.pid\s*=\s*'([^']+)';", r"cbsplayer\.pid\s*=\s*'([^']+)';"], [r"video\.settings\.pid\s*=\s*'([^']+)';", r"cbsplayer\.pid\s*=\s*'([^']+)';"],
webpage, 'real video ID') webpage, 'real video ID')
return { return {
'_type': 'url_transparent', '_type': 'url_transparent',
'ie_key': 'ThePlatform', 'ie_key': 'ThePlatform',
'url': 'theplatform:%s' % real_id, 'url': smuggle_url(
'http://link.theplatform.com/s/dJ5BDC/%s?mbr=true&manifest=m3u' % real_id,
{'force_smil_url': True}),
'display_id': display_id, 'display_id': display_id,
} }

View File

@@ -67,9 +67,12 @@ class CBSNewsIE(InfoExtractor):
'format_id': format_id, 'format_id': format_id,
} }
if uri.startswith('rtmp'): if uri.startswith('rtmp'):
play_path = re.sub(
r'{slistFilePath}', '',
uri.split('<break>')[-1].split('{break}')[-1])
fmt.update({ fmt.update({
'app': 'ondemand?auth=cbs', 'app': 'ondemand?auth=cbs',
'play_path': 'mp4:' + uri.split('<break>')[-1], 'play_path': 'mp4:' + play_path,
'player_url': 'http://www.cbsnews.com/[[IMPORT]]/vidtech.cbsinteractive.com/player/3_3_0/CBSI_PLAYER_HD.swf', 'player_url': 'http://www.cbsnews.com/[[IMPORT]]/vidtech.cbsinteractive.com/player/3_3_0/CBSI_PLAYER_HD.swf',
'page_url': 'http://www.cbsnews.com', 'page_url': 'http://www.cbsnews.com',
'ext': 'flv', 'ext': 'flv',

View File

@@ -5,7 +5,6 @@ import re
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import ( from ..compat import (
compat_urllib_request,
compat_urllib_parse, compat_urllib_parse,
compat_urllib_parse_unquote, compat_urllib_parse_unquote,
compat_urllib_parse_urlparse, compat_urllib_parse_urlparse,
@@ -13,59 +12,86 @@ from ..compat import (
from ..utils import ( from ..utils import (
ExtractorError, ExtractorError,
float_or_none, float_or_none,
sanitized_Request,
) )
class CeskaTelevizeIE(InfoExtractor): class CeskaTelevizeIE(InfoExtractor):
_VALID_URL = r'https?://www\.ceskatelevize\.cz/(porady|ivysilani)/(.+/)?(?P<id>[^?#]+)' _VALID_URL = r'https?://www\.ceskatelevize\.cz/(porady|ivysilani)/(?:[^/]+/)*(?P<id>[^/#?]+)/*(?:[#?].*)?$'
_TESTS = [{
_TESTS = [ 'url': 'http://www.ceskatelevize.cz/ivysilani/ivysilani/10441294653-hyde-park-civilizace/214411058091220',
{ 'info_dict': {
'url': 'http://www.ceskatelevize.cz/ivysilani/ivysilani/10441294653-hyde-park-civilizace/214411058091220', 'id': '61924494876951776',
'info_dict': { 'ext': 'mp4',
'id': '214411058091220', 'title': 'Hyde Park Civilizace',
'ext': 'mp4', 'description': 'md5:fe93f6eda372d150759d11644ebbfb4a',
'title': 'Hyde Park Civilizace', 'thumbnail': 're:^https?://.*\.jpg',
'description': 'Věda a současná civilizace. Interaktivní pořad - prostor pro vaše otázky a komentáře', 'duration': 3350,
'thumbnail': 're:^https?://.*\.jpg',
'duration': 3350,
},
'params': {
# m3u8 download
'skip_download': True,
},
}, },
{ 'params': {
'url': 'http://www.ceskatelevize.cz/ivysilani/10532695142-prvni-republika/bonus/14716-zpevacka-z-duparny-bobina', # m3u8 download
'info_dict': { 'skip_download': True,
'id': '14716',
'ext': 'mp4',
'title': 'První republika: Zpěvačka z Dupárny Bobina',
'description': 'Sága mapující atmosféru první republiky od r. 1918 do r. 1945.',
'thumbnail': 're:^https?://.*\.jpg',
'duration': 88.4,
},
'params': {
# m3u8 download
'skip_download': True,
},
}, },
] }, {
'url': 'http://www.ceskatelevize.cz/ivysilani/10532695142-prvni-republika/bonus/14716-zpevacka-z-duparny-bobina',
'info_dict': {
'id': '61924494876844374',
'ext': 'mp4',
'title': 'První republika: Zpěvačka z Dupárny Bobina',
'description': 'Sága mapující atmosféru první republiky od r. 1918 do r. 1945.',
'thumbnail': 're:^https?://.*\.jpg',
'duration': 88.4,
},
'params': {
# m3u8 download
'skip_download': True,
},
}, {
# video with 18+ caution trailer
'url': 'http://www.ceskatelevize.cz/porady/10520528904-queer/215562210900007-bogotart/',
'info_dict': {
'id': '215562210900007-bogotart',
'title': 'Queer: Bogotart',
'description': 'Alternativní průvodce současným queer světem',
},
'playlist': [{
'info_dict': {
'id': '61924494876844842',
'ext': 'mp4',
'title': 'Queer: Bogotart (Varování 18+)',
'duration': 10.2,
},
}, {
'info_dict': {
'id': '61924494877068022',
'ext': 'mp4',
'title': 'Queer: Bogotart (Queer)',
'thumbnail': 're:^https?://.*\.jpg',
'duration': 1558.3,
},
}],
'params': {
# m3u8 download
'skip_download': True,
},
}]
def _real_extract(self, url): def _real_extract(self, url):
url = url.replace('/porady/', '/ivysilani/').replace('/video/', '') url = url.replace('/porady/', '/ivysilani/').replace('/video/', '')
mobj = re.match(self._VALID_URL, url) mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id') playlist_id = mobj.group('id')
webpage = self._download_webpage(url, video_id) webpage = self._download_webpage(url, playlist_id)
NOT_AVAILABLE_STRING = 'This content is not available at your territory due to limited copyright.' NOT_AVAILABLE_STRING = 'This content is not available at your territory due to limited copyright.'
if '%s</p>' % NOT_AVAILABLE_STRING in webpage: if '%s</p>' % NOT_AVAILABLE_STRING in webpage:
raise ExtractorError(NOT_AVAILABLE_STRING, expected=True) raise ExtractorError(NOT_AVAILABLE_STRING, expected=True)
typ = self._html_search_regex(r'getPlaylistUrl\(\[\{"type":"(.+?)","id":".+?"\}\],', webpage, 'type') typ = self._html_search_regex(
episode_id = self._html_search_regex(r'getPlaylistUrl\(\[\{"type":".+?","id":"(.+?)"\}\],', webpage, 'episode_id') r'getPlaylistUrl\(\[\{"type":"(.+?)","id":".+?"\}\],', webpage, 'type')
episode_id = self._html_search_regex(
r'getPlaylistUrl\(\[\{"type":".+?","id":"(.+?)"\}\],', webpage, 'episode_id')
data = { data = {
'playlist[0][type]': typ, 'playlist[0][type]': typ,
@@ -74,7 +100,7 @@ class CeskaTelevizeIE(InfoExtractor):
'requestSource': 'iVysilani', 'requestSource': 'iVysilani',
} }
req = compat_urllib_request.Request( req = sanitized_Request(
'http://www.ceskatelevize.cz/ivysilani/ajax/get-client-playlist', 'http://www.ceskatelevize.cz/ivysilani/ajax/get-client-playlist',
data=compat_urllib_parse.urlencode(data)) data=compat_urllib_parse.urlencode(data))
@@ -83,42 +109,52 @@ class CeskaTelevizeIE(InfoExtractor):
req.add_header('X-Requested-With', 'XMLHttpRequest') req.add_header('X-Requested-With', 'XMLHttpRequest')
req.add_header('Referer', url) req.add_header('Referer', url)
playlistpage = self._download_json(req, video_id) playlistpage = self._download_json(req, playlist_id)
playlist_url = playlistpage['url'] playlist_url = playlistpage['url']
if playlist_url == 'error_region': if playlist_url == 'error_region':
raise ExtractorError(NOT_AVAILABLE_STRING, expected=True) raise ExtractorError(NOT_AVAILABLE_STRING, expected=True)
req = compat_urllib_request.Request(compat_urllib_parse_unquote(playlist_url)) req = sanitized_Request(compat_urllib_parse_unquote(playlist_url))
req.add_header('Referer', url) req.add_header('Referer', url)
playlist = self._download_json(req, video_id) playlist_title = self._og_search_title(webpage)
playlist_description = self._og_search_description(webpage)
item = playlist['playlist'][0] playlist = self._download_json(req, playlist_id)['playlist']
formats = [] playlist_len = len(playlist)
for format_id, stream_url in item['streamUrls'].items():
formats.extend(self._extract_m3u8_formats(stream_url, video_id, 'mp4'))
self._sort_formats(formats)
title = self._og_search_title(webpage) entries = []
description = self._og_search_description(webpage) for item in playlist:
duration = float_or_none(item.get('duration')) formats = []
thumbnail = item.get('previewImageUrl') for format_id, stream_url in item['streamUrls'].items():
formats.extend(self._extract_m3u8_formats(
stream_url, playlist_id, 'mp4', entry_protocol='m3u8_native'))
self._sort_formats(formats)
subtitles = {} item_id = item.get('id') or item['assetId']
subs = item.get('subtitles') title = item['title']
if subs:
subtitles = self.extract_subtitles(episode_id, subs)
return { duration = float_or_none(item.get('duration'))
'id': episode_id, thumbnail = item.get('previewImageUrl')
'title': title,
'description': description, subtitles = {}
'thumbnail': thumbnail, if item.get('type') == 'VOD':
'duration': duration, subs = item.get('subtitles')
'formats': formats, if subs:
'subtitles': subtitles, subtitles = self.extract_subtitles(episode_id, subs)
}
entries.append({
'id': item_id,
'title': playlist_title if playlist_len == 1 else '%s (%s)' % (playlist_title, title),
'description': playlist_description if playlist_len == 1 else None,
'thumbnail': thumbnail,
'duration': duration,
'formats': formats,
'subtitles': subtitles,
})
return self.playlist_result(entries, playlist_id, playlist_title, playlist_description)
def _get_subtitles(self, episode_id, subs): def _get_subtitles(self, episode_id, subs):
original_subtitles = self._download_webpage( original_subtitles = self._download_webpage(

View File

@@ -3,7 +3,11 @@ from __future__ import unicode_literals
import re import re
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import ExtractorError from ..utils import (
ExtractorError,
parse_filesize,
qualities,
)
class Channel9IE(InfoExtractor): class Channel9IE(InfoExtractor):
@@ -28,7 +32,7 @@ class Channel9IE(InfoExtractor):
'title': 'Developer Kick-Off Session: Stuff We Love', 'title': 'Developer Kick-Off Session: Stuff We Love',
'description': 'md5:c08d72240b7c87fcecafe2692f80e35f', 'description': 'md5:c08d72240b7c87fcecafe2692f80e35f',
'duration': 4576, 'duration': 4576,
'thumbnail': 'http://video.ch9.ms/ch9/9d51/03902f2d-fc97-4d3c-b195-0bfe15a19d51/KOS002_220.jpg', 'thumbnail': 're:http://.*\.jpg',
'session_code': 'KOS002', 'session_code': 'KOS002',
'session_day': 'Day 1', 'session_day': 'Day 1',
'session_room': 'Arena 1A', 'session_room': 'Arena 1A',
@@ -44,31 +48,29 @@ class Channel9IE(InfoExtractor):
'title': 'Self-service BI with Power BI - nuclear testing', 'title': 'Self-service BI with Power BI - nuclear testing',
'description': 'md5:d1e6ecaafa7fb52a2cacdf9599829f5b', 'description': 'md5:d1e6ecaafa7fb52a2cacdf9599829f5b',
'duration': 1540, 'duration': 1540,
'thumbnail': 'http://video.ch9.ms/ch9/87e1/0300391f-a455-4c72-bec3-4422f19287e1/selfservicenuk_512.jpg', 'thumbnail': 're:http://.*\.jpg',
'authors': ['Mike Wilmot'], 'authors': ['Mike Wilmot'],
}, },
},
{
# low quality mp4 is best
'url': 'https://channel9.msdn.com/Events/CPP/CppCon-2015/Ranges-for-the-Standard-Library',
'info_dict': {
'id': 'Events/CPP/CppCon-2015/Ranges-for-the-Standard-Library',
'ext': 'mp4',
'title': 'Ranges for the Standard Library',
'description': 'md5:2e6b4917677af3728c5f6d63784c4c5d',
'duration': 5646,
'thumbnail': 're:http://.*\.jpg',
},
'params': {
'skip_download': True,
},
} }
] ]
_RSS_URL = 'http://channel9.msdn.com/%s/RSS' _RSS_URL = 'http://channel9.msdn.com/%s/RSS'
# Sorted by quality
_known_formats = ['MP3', 'MP4', 'Mid Quality WMV', 'Mid Quality MP4', 'High Quality WMV', 'High Quality MP4']
def _restore_bytes(self, formatted_size):
if not formatted_size:
return 0
m = re.match(r'^(?P<size>\d+(?:\.\d+)?)\s+(?P<units>[a-zA-Z]+)', formatted_size)
if not m:
return 0
units = m.group('units')
try:
exponent = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'].index(units.upper())
except ValueError:
return 0
size = float(m.group('size'))
return int(size * (1024 ** exponent))
def _formats_from_html(self, html): def _formats_from_html(self, html):
FORMAT_REGEX = r''' FORMAT_REGEX = r'''
(?x) (?x)
@@ -78,16 +80,20 @@ class Channel9IE(InfoExtractor):
<h3>File\s+size</h3>\s*(?P<filesize>.*?)\s* <h3>File\s+size</h3>\s*(?P<filesize>.*?)\s*
</div>)? # File size part may be missing </div>)? # File size part may be missing
''' '''
# Extract known formats quality = qualities((
'MP3', 'MP4',
'Low Quality WMV', 'Low Quality MP4',
'Mid Quality WMV', 'Mid Quality MP4',
'High Quality WMV', 'High Quality MP4'))
formats = [{ formats = [{
'url': x.group('url'), 'url': x.group('url'),
'format_id': x.group('quality'), 'format_id': x.group('quality'),
'format_note': x.group('note'), 'format_note': x.group('note'),
'format': '%s (%s)' % (x.group('quality'), x.group('note')), 'format': '%s (%s)' % (x.group('quality'), x.group('note')),
'filesize': self._restore_bytes(x.group('filesize')), # File size is approximate 'filesize_approx': parse_filesize(x.group('filesize')),
'preference': self._known_formats.index(x.group('quality')), 'quality': quality(x.group('quality')),
'vcodec': 'none' if x.group('note') == 'Audio only' else None, 'vcodec': 'none' if x.group('note') == 'Audio only' else None,
} for x in list(re.finditer(FORMAT_REGEX, html)) if x.group('quality') in self._known_formats] } for x in list(re.finditer(FORMAT_REGEX, html))]
self._sort_formats(formats) self._sort_formats(formats)
@@ -158,7 +164,7 @@ class Channel9IE(InfoExtractor):
def _extract_session_day(self, html): def _extract_session_day(self, html):
m = re.search(r'<li class="day">\s*<a href="/Events/[^"]+">(?P<day>[^<]+)</a>\s*</li>', html) m = re.search(r'<li class="day">\s*<a href="/Events/[^"]+">(?P<day>[^<]+)</a>\s*</li>', html)
return m.group('day') if m is not None else None return m.group('day').strip() if m is not None else None
def _extract_session_room(self, html): def _extract_session_room(self, html):
m = re.search(r'<li class="room">\s*(?P<room>.+?)\s*</li>', html) m = re.search(r'<li class="room">\s*(?P<room>.+?)\s*</li>', html)
@@ -224,12 +230,12 @@ class Channel9IE(InfoExtractor):
if contents is None: if contents is None:
return contents return contents
authors = self._extract_authors(html) if len(contents) > 1:
raise ExtractorError('Got more than one entry')
result = contents[0]
result['authors'] = self._extract_authors(html)
for content in contents: return result
content['authors'] = authors
return contents
def _extract_session(self, html, content_path): def _extract_session(self, html, content_path):
contents = self._extract_content(html, content_path) contents = self._extract_content(html, content_path)

View File

@@ -0,0 +1,50 @@
from __future__ import unicode_literals
from .common import InfoExtractor
from ..utils import ExtractorError
class ChaturbateIE(InfoExtractor):
_VALID_URL = r'https?://(?:[^/]+\.)?chaturbate\.com/(?P<id>[^/?#]+)'
_TESTS = [{
'url': 'https://www.chaturbate.com/siswet19/',
'info_dict': {
'id': 'siswet19',
'ext': 'mp4',
'title': 're:^siswet19 [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
'age_limit': 18,
'is_live': True,
},
'params': {
'skip_download': True,
}
}, {
'url': 'https://en.chaturbate.com/siswet19/',
'only_matching': True,
}]
def _real_extract(self, url):
video_id = self._match_id(url)
webpage = self._download_webpage(url, video_id)
m3u8_url = self._search_regex(
r'src=(["\'])(?P<url>http.+?\.m3u8.*?)\1', webpage,
'playlist', default=None, group='url')
if not m3u8_url:
error = self._search_regex(
r'<span[^>]+class=(["\'])desc_span\1[^>]*>(?P<error>[^<]+)</span>',
webpage, 'error', group='error')
raise ExtractorError(error, expected=True)
formats = self._extract_m3u8_formats(m3u8_url, video_id, ext='mp4')
return {
'id': video_id,
'title': self._live_title(video_id),
'thumbnail': 'https://cdn-s.highwebmedia.com/uHK3McUtGCG3SMFcd4ZJsRv8/roomimage/%s.jpg' % video_id,
'age_limit': self._rta_search(webpage),
'is_live': True,
'formats': formats,
}

View File

@@ -6,6 +6,7 @@ import re
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import ExtractorError from ..utils import ExtractorError
from .bliptv import BlipTVIE from .bliptv import BlipTVIE
from .screenwavemedia import ScreenwaveMediaIE
class CinemassacreIE(InfoExtractor): class CinemassacreIE(InfoExtractor):
@@ -83,10 +84,10 @@ class CinemassacreIE(InfoExtractor):
playerdata_url = self._search_regex( playerdata_url = self._search_regex(
[ [
r'src="(http://(?:player2\.screenwavemedia\.com|player\.screenwavemedia\.com/play)/[a-zA-Z]+\.php\?[^"]*\bid=.+?)"', ScreenwaveMediaIE.EMBED_PATTERN,
r'<iframe[^>]+src="((?:https?:)?//(?:[^.]+\.)?youtube\.com/.+?)"', r'<iframe[^>]+src="(?P<url>(?:https?:)?//(?:[^.]+\.)?youtube\.com/.+?)"',
], ],
webpage, 'player data URL', default=None) webpage, 'player data URL', default=None, group='url')
if not playerdata_url: if not playerdata_url:
playerdata_url = BlipTVIE._extract_url(webpage) playerdata_url = BlipTVIE._extract_url(webpage)
if not playerdata_url: if not playerdata_url:

View File

@@ -1,53 +1,62 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import re
import time
import xml.etree.ElementTree
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import ( from ..utils import (
ExtractorError, int_or_none,
parse_duration, unified_strdate,
) )
class ClipfishIE(InfoExtractor): class ClipfishIE(InfoExtractor):
IE_NAME = 'clipfish' _VALID_URL = r'https?://(?:www\.)?clipfish\.de/(?:[^/]+/)+video/(?P<id>[0-9]+)'
_VALID_URL = r'^https?://(?:www\.)?clipfish\.de/.*?/video/(?P<id>[0-9]+)/'
_TEST = { _TEST = {
'url': 'http://www.clipfish.de/special/game-trailer/video/3966754/fifa-14-e3-2013-trailer/', 'url': 'http://www.clipfish.de/special/game-trailer/video/3966754/fifa-14-e3-2013-trailer/',
'md5': '2521cd644e862936cf2e698206e47385', 'md5': '79bc922f3e8a9097b3d68a93780fd475',
'info_dict': { 'info_dict': {
'id': '3966754', 'id': '3966754',
'ext': 'mp4', 'ext': 'mp4',
'title': 'FIFA 14 - E3 2013 Trailer', 'title': 'FIFA 14 - E3 2013 Trailer',
'description': 'Video zu FIFA 14: E3 2013 Trailer',
'upload_date': '20130611',
'duration': 82, 'duration': 82,
}, 'view_count': int,
'skip': 'Blocked in the US' }
} }
def _real_extract(self, url): def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url) video_id = self._match_id(url)
video_id = mobj.group(1)
info_url = ('http://www.clipfish.de/devxml/videoinfo/%s?ts=%d' % video_info = self._download_json(
(video_id, int(time.time()))) 'http://www.clipfish.de/devapi/id/%s?format=json&apikey=hbbtv' % video_id,
doc = self._download_xml( video_id)['items'][0]
info_url, video_id, note='Downloading info page')
title = doc.find('title').text formats = []
video_url = doc.find('filename').text
if video_url is None: m3u8_url = video_info.get('media_videourl_hls')
xml_bytes = xml.etree.ElementTree.tostring(doc) if m3u8_url:
raise ExtractorError('Cannot find video URL in document %r' % formats.append({
xml_bytes) 'url': m3u8_url.replace('de.hls.fra.clipfish.de', 'hls.fra.clipfish.de'),
thumbnail = doc.find('imageurl').text 'ext': 'mp4',
duration = parse_duration(doc.find('duration').text) 'format_id': 'hls',
})
mp4_url = video_info.get('media_videourl')
if mp4_url:
formats.append({
'url': mp4_url,
'format_id': 'mp4',
'width': int_or_none(video_info.get('width')),
'height': int_or_none(video_info.get('height')),
'tbr': int_or_none(video_info.get('bitrate')),
})
return { return {
'id': video_id, 'id': video_id,
'title': title, 'title': video_info['title'],
'url': video_url, 'description': video_info.get('descr'),
'thumbnail': thumbnail, 'formats': formats,
'duration': duration, 'thumbnail': video_info.get('media_content_thumbnail_large') or video_info.get('media_thumbnail'),
'duration': int_or_none(video_info.get('media_length')),
'upload_date': unified_strdate(video_info.get('pubDate')),
'view_count': int_or_none(video_info.get('media_views'))
} }

View File

@@ -1,7 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import determine_ext from ..utils import int_or_none
_translation_table = { _translation_table = {
@@ -42,31 +42,26 @@ class CliphunterIE(InfoExtractor):
video_title = self._search_regex( video_title = self._search_regex(
r'mediaTitle = "([^"]+)"', webpage, 'title') r'mediaTitle = "([^"]+)"', webpage, 'title')
fmts = {} gexo_files = self._parse_json(
for fmt in ('mp4', 'flv'): self._search_regex(
fmt_list = self._parse_json(self._search_regex( r'var\s+gexoFiles\s*=\s*({.+?});', webpage, 'gexo files'),
r'var %sjson\s*=\s*(\[.*?\]);' % fmt, webpage, '%s formats' % fmt), video_id) video_id)
for f in fmt_list:
fmts[f['fname']] = _decode(f['sUrl'])
qualities = self._parse_json(self._search_regex(
r'var player_btns\s*=\s*(.*?);\n', webpage, 'quality info'), video_id)
formats = [] formats = []
for fname, url in fmts.items(): for format_id, f in gexo_files.items():
f = { video_url = f.get('url')
'url': url, if not video_url:
} continue
if fname in qualities: fmt = f.get('fmt')
qual = qualities[fname] height = f.get('h')
f.update({ format_id = '%s_%sp' % (fmt, height) if fmt and height else format_id
'format_id': '%s_%sp' % (determine_ext(url), qual['h']), formats.append({
'width': qual['w'], 'url': _decode(video_url),
'height': qual['h'], 'format_id': format_id,
'tbr': qual['br'], 'width': int_or_none(f.get('w')),
}) 'height': int_or_none(height),
formats.append(f) 'tbr': int_or_none(f.get('br')),
})
self._sort_formats(formats) self._sort_formats(formats)
thumbnail = self._search_regex( thumbnail = self._search_regex(

View File

@@ -12,9 +12,9 @@ from ..utils import (
class ClubicIE(InfoExtractor): class ClubicIE(InfoExtractor):
_VALID_URL = r'http://(?:www\.)?clubic\.com/video/[^/]+/video.*-(?P<id>[0-9]+)\.html' _VALID_URL = r'http://(?:www\.)?clubic\.com/video/(?:[^/]+/)*video.*-(?P<id>[0-9]+)\.html'
_TEST = { _TESTS = [{
'url': 'http://www.clubic.com/video/clubic-week/video-clubic-week-2-0-le-fbi-se-lance-dans-la-photo-d-identite-448474.html', 'url': 'http://www.clubic.com/video/clubic-week/video-clubic-week-2-0-le-fbi-se-lance-dans-la-photo-d-identite-448474.html',
'md5': '1592b694ba586036efac1776b0b43cd3', 'md5': '1592b694ba586036efac1776b0b43cd3',
'info_dict': { 'info_dict': {
@@ -24,7 +24,10 @@ class ClubicIE(InfoExtractor):
'description': 're:Gueule de bois chez Nokia. Le constructeur a indiqué cette.*', 'description': 're:Gueule de bois chez Nokia. Le constructeur a indiqué cette.*',
'thumbnail': 're:^http://img\.clubic\.com/.*\.jpg$', 'thumbnail': 're:^http://img\.clubic\.com/.*\.jpg$',
} }
} }, {
'url': 'http://www.clubic.com/video/video-clubic-week-2-0-apple-iphone-6s-et-plus-mais-surtout-le-pencil-469792.html',
'only_matching': True,
}]
def _real_extract(self, url): def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url) mobj = re.match(self._VALID_URL, url)

View File

@@ -0,0 +1,57 @@
from __future__ import unicode_literals
from .common import InfoExtractor
from ..utils import (
float_or_none,
parse_iso8601,
)
class ClypIE(InfoExtractor):
_VALID_URL = r'https?://(?:www\.)?clyp\.it/(?P<id>[a-z0-9]+)'
_TEST = {
'url': 'https://clyp.it/ojz2wfah',
'md5': '1d4961036c41247ecfdcc439c0cddcbb',
'info_dict': {
'id': 'ojz2wfah',
'ext': 'mp3',
'title': 'Krisson80 - bits wip wip',
'description': '#Krisson80BitsWipWip #chiptune\n#wip',
'duration': 263.21,
'timestamp': 1443515251,
'upload_date': '20150929',
},
}
def _real_extract(self, url):
audio_id = self._match_id(url)
metadata = self._download_json(
'https://api.clyp.it/%s' % audio_id, audio_id)
formats = []
for secure in ('', 'Secure'):
for ext in ('Ogg', 'Mp3'):
format_id = '%s%s' % (secure, ext)
format_url = metadata.get('%sUrl' % format_id)
if format_url:
formats.append({
'url': format_url,
'format_id': format_id,
'vcodec': 'none',
})
self._sort_formats(formats)
title = metadata['Title']
description = metadata.get('Description')
duration = float_or_none(metadata.get('Duration'))
timestamp = parse_iso8601(metadata.get('DateCreated'))
return {
'id': audio_id,
'title': title,
'description': description,
'duration': duration,
'timestamp': timestamp,
'formats': formats,
}

View File

@@ -4,7 +4,7 @@ from .mtv import MTVIE
class CMTIE(MTVIE): class CMTIE(MTVIE):
IE_NAME = 'cmt.com' IE_NAME = 'cmt.com'
_VALID_URL = r'https?://www\.cmt\.com/videos/.+?/(?P<videoid>[^/]+)\.jhtml' _VALID_URL = r'https?://www\.cmt\.com/(?:videos|shows)/(?:[^/]+/)*(?P<videoid>\d+)'
_FEED_URL = 'http://www.cmt.com/sitewide/apps/player/embed/rss/' _FEED_URL = 'http://www.cmt.com/sitewide/apps/player/embed/rss/'
_TESTS = [{ _TESTS = [{
@@ -16,4 +16,7 @@ class CMTIE(MTVIE):
'title': 'Garth Brooks - "The Call (featuring Trisha Yearwood)"', 'title': 'Garth Brooks - "The Call (featuring Trisha Yearwood)"',
'description': 'Blame It All On My Roots', 'description': 'Blame It All On My Roots',
}, },
}, {
'url': 'http://www.cmt.com/shows/party-down-south/party-down-south-ep-407-gone-girl/1738172/playlist/#id=1738172',
'only_matching': True,
}] }]

View File

@@ -3,10 +3,10 @@ from __future__ import unicode_literals
import json import json
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import compat_urllib_request
from ..utils import ( from ..utils import (
float_or_none, float_or_none,
int_or_none, int_or_none,
sanitized_Request,
) )
@@ -52,7 +52,7 @@ class CollegeRamaIE(InfoExtractor):
} }
} }
request = compat_urllib_request.Request( request = sanitized_Request(
'http://collegerama.tudelft.nl/Mediasite/PlayerService/PlayerService.svc/json/GetPlayerOptions', 'http://collegerama.tudelft.nl/Mediasite/PlayerService/PlayerService.svc/json/GetPlayerOptions',
json.dumps(player_options_request)) json.dumps(player_options_request))
request.add_header('Content-Type', 'application/json') request.add_header('Content-Type', 'application/json')

View File

@@ -151,12 +151,7 @@ class ComedyCentralShowsIE(MTVServicesInfoExtractor):
mobj = re.match(self._VALID_URL, url) mobj = re.match(self._VALID_URL, url)
if mobj.group('shortname'): if mobj.group('shortname'):
if mobj.group('shortname') in ('tds', 'thedailyshow'): return self.url_result('http://www.cc.com/shows/the-daily-show-with-trevor-noah/full-episodes')
url = 'http://thedailyshow.cc.com/full-episodes/'
else:
url = 'http://thecolbertreport.cc.com/full-episodes/'
mobj = re.match(self._VALID_URL, url, re.VERBOSE)
assert mobj is not None
if mobj.group('clip'): if mobj.group('clip'):
if mobj.group('videotitle'): if mobj.group('videotitle'):

View File

@@ -10,18 +10,18 @@ import re
import socket import socket
import sys import sys
import time import time
import xml.etree.ElementTree
from ..compat import ( from ..compat import (
compat_cookiejar, compat_cookiejar,
compat_cookies, compat_cookies,
compat_HTTPError, compat_getpass,
compat_http_client, compat_http_client,
compat_urllib_error, compat_urllib_error,
compat_urllib_parse,
compat_urllib_parse_urlparse, compat_urllib_parse_urlparse,
compat_urllib_request,
compat_urlparse, compat_urlparse,
compat_str, compat_str,
compat_etree_fromstring,
) )
from ..utils import ( from ..utils import (
NO_DEFAULT, NO_DEFAULT,
@@ -36,7 +36,12 @@ from ..utils import (
int_or_none, int_or_none,
RegexNotFoundError, RegexNotFoundError,
sanitize_filename, sanitize_filename,
sanitized_Request,
unescapeHTML, unescapeHTML,
unified_strdate,
url_basename,
xpath_text,
xpath_with_ns,
) )
@@ -147,6 +152,7 @@ class InfoExtractor(object):
description: Full video description. description: Full video description.
uploader: Full name of the video uploader. uploader: Full name of the video uploader.
creator: The main artist who created the video. creator: The main artist who created the video.
release_date: The date (YYYYMMDD) when the video was released.
timestamp: UNIX timestamp of the moment the video became available. timestamp: UNIX timestamp of the moment the video became available.
upload_date: Video upload date (YYYYMMDD). upload_date: Video upload date (YYYYMMDD).
If not explicitly set, calculated from timestamp. If not explicitly set, calculated from timestamp.
@@ -158,12 +164,14 @@ class InfoExtractor(object):
with the "ext" entry and one of: with the "ext" entry and one of:
* "data": The subtitles file contents * "data": The subtitles file contents
* "url": A URL pointing to the subtitles file * "url": A URL pointing to the subtitles file
"ext" will be calculated from URL if missing
automatic_captions: Like 'subtitles', used by the YoutubeIE for automatic_captions: Like 'subtitles', used by the YoutubeIE for
automatically generated captions automatically generated captions
duration: Length of the video in seconds, as an integer. duration: Length of the video in seconds, as an integer or float.
view_count: How many users have watched the video on the platform. view_count: How many users have watched the video on the platform.
like_count: Number of positive ratings of the video like_count: Number of positive ratings of the video
dislike_count: Number of negative ratings of the video dislike_count: Number of negative ratings of the video
repost_count: Number of reposts of the video
average_rating: Average rating give by users, the scale used depends on the webpage average_rating: Average rating give by users, the scale used depends on the webpage
comment_count: Number of comments on the video comment_count: Number of comments on the video
comments: A list of comments, each with one or more of the following comments: A list of comments, each with one or more of the following
@@ -200,8 +208,8 @@ class InfoExtractor(object):
There must be a key "entries", which is a list, an iterable, or a PagedList There must be a key "entries", which is a list, an iterable, or a PagedList
object, each element of which is a valid dictionary by this specification. object, each element of which is a valid dictionary by this specification.
Additionally, playlists can have "title" and "id" attributes with the same Additionally, playlists can have "title", "description" and "id" attributes
semantics as videos (see above). with the same semantics as videos (see above).
_type "multi_video" indicates that there are multiple videos that _type "multi_video" indicates that there are multiple videos that
@@ -302,11 +310,11 @@ class InfoExtractor(object):
@classmethod @classmethod
def ie_key(cls): def ie_key(cls):
"""A string for getting the InfoExtractor with get_info_extractor""" """A string for getting the InfoExtractor with get_info_extractor"""
return cls.__name__[:-2] return compat_str(cls.__name__[:-2])
@property @property
def IE_NAME(self): def IE_NAME(self):
return type(self).__name__[:-2] return compat_str(type(self).__name__[:-2])
def _request_webpage(self, url_or_request, video_id, note=None, errnote=None, fatal=True): def _request_webpage(self, url_or_request, video_id, note=None, errnote=None, fatal=True):
""" Returns the response handle """ """ Returns the response handle """
@@ -453,7 +461,7 @@ class InfoExtractor(object):
return xml_string return xml_string
if transform_source: if transform_source:
xml_string = transform_source(xml_string) xml_string = transform_source(xml_string)
return xml.etree.ElementTree.fromstring(xml_string.encode('utf-8')) return compat_etree_fromstring(xml_string.encode('utf-8'))
def _download_json(self, url_or_request, video_id, def _download_json(self, url_or_request, video_id,
note='Downloading JSON metadata', note='Downloading JSON metadata',
@@ -505,6 +513,18 @@ class InfoExtractor(object):
"""Report attempt to log in.""" """Report attempt to log in."""
self.to_screen('Logging in') self.to_screen('Logging in')
@staticmethod
def raise_login_required(msg='This video is only available for registered users'):
raise ExtractorError(
'%s. Use --username and --password or --netrc to provide account credentials.' % msg,
expected=True)
@staticmethod
def raise_geo_restricted(msg='This video is not available from your location due to geo restriction'):
raise ExtractorError(
'%s. You might want to use --proxy to workaround.' % msg,
expected=True)
# Methods for following #608 # Methods for following #608
@staticmethod @staticmethod
def url_result(url, ie=None, video_id=None, video_title=None): def url_result(url, ie=None, video_id=None, video_title=None):
@@ -606,7 +626,7 @@ class InfoExtractor(object):
return (username, password) return (username, password)
def _get_tfa_info(self): def _get_tfa_info(self, note='two-factor verification code'):
""" """
Get the two-factor authentication info Get the two-factor authentication info
TODO - asking the user will be required for sms/phone verify TODO - asking the user will be required for sms/phone verify
@@ -620,13 +640,14 @@ class InfoExtractor(object):
if downloader_params.get('twofactor', None) is not None: if downloader_params.get('twofactor', None) is not None:
return downloader_params['twofactor'] return downloader_params['twofactor']
return None return compat_getpass('Type %s and press [Return]: ' % note)
# Helper functions for extracting OpenGraph info # Helper functions for extracting OpenGraph info
@staticmethod @staticmethod
def _og_regexes(prop): def _og_regexes(prop):
content_re = r'content=(?:"([^>]+?)"|\'([^>]+?)\')' content_re = r'content=(?:"([^"]+?)"|\'([^\']+?)\'|\s*([^\s"\'=<>`]+?))'
property_re = r'(?:name|property)=[\'"]og:%s[\'"]' % re.escape(prop) property_re = (r'(?:name|property)=(?:\'og:%(prop)s\'|"og:%(prop)s"|\s*og:%(prop)s\b)'
% {'prop': re.escape(prop)})
template = r'<meta[^>]+?%s[^>]+?%s' template = r'<meta[^>]+?%s[^>]+?%s'
return [ return [
template % (property_re, content_re), template % (property_re, content_re),
@@ -636,7 +657,7 @@ class InfoExtractor(object):
@staticmethod @staticmethod
def _meta_regex(prop): def _meta_regex(prop):
return r'''(?isx)<meta return r'''(?isx)<meta
(?=[^>]+(?:itemprop|name|property)=(["\']?)%s\1) (?=[^>]+(?:itemprop|name|property|id|http-equiv)=(["\']?)%s\1)
[^>]+?content=(["\'])(?P<content>.*?)\2''' % re.escape(prop) [^>]+?content=(["\'])(?P<content>.*?)\2''' % re.escape(prop)
def _og_search_property(self, prop, html, name=None, **kargs): def _og_search_property(self, prop, html, name=None, **kargs):
@@ -720,20 +741,23 @@ class InfoExtractor(object):
@staticmethod @staticmethod
def _hidden_inputs(html): def _hidden_inputs(html):
return dict([ html = re.sub(r'<!--(?:(?!<!--).)*-->', '', html)
(input.group('name'), input.group('value')) for input in re.finditer( hidden_inputs = {}
r'''(?x) for input in re.findall(r'(?i)<input([^>]+)>', html):
<input\s+ if not re.search(r'type=(["\'])(?:hidden|submit)\1', input):
type=(?P<q_hidden>["\'])hidden(?P=q_hidden)\s+ continue
name=(?P<q_name>["\'])(?P<name>.+?)(?P=q_name)\s+ name = re.search(r'name=(["\'])(?P<value>.+?)\1', input)
(?:id=(?P<q_id>["\']).+?(?P=q_id)\s+)? if not name:
value=(?P<q_value>["\'])(?P<value>.*?)(?P=q_value) continue
''', html) value = re.search(r'value=(["\'])(?P<value>.*?)\1', input)
]) if not value:
continue
hidden_inputs[name.group('value')] = value.group('value')
return hidden_inputs
def _form_hidden_inputs(self, form_id, html): def _form_hidden_inputs(self, form_id, html):
form = self._search_regex( form = self._search_regex(
r'(?s)<form[^>]+?id=(["\'])%s\1[^>]*>(?P<form>.+?)</form>' % form_id, r'(?is)<form[^>]+?id=(["\'])%s\1[^>]*>(?P<form>.+?)</form>' % form_id,
html, '%s form' % form_id, group='form') html, '%s form' % form_id, group='form')
return self._hidden_inputs(form) return self._hidden_inputs(form)
@@ -817,7 +841,7 @@ class InfoExtractor(object):
self._request_webpage(url, video_id, 'Checking %s URL' % item) self._request_webpage(url, video_id, 'Checking %s URL' % item)
return True return True
except ExtractorError as e: except ExtractorError as e:
if isinstance(e.cause, compat_HTTPError): if isinstance(e.cause, compat_urllib_error.URLError):
self.to_screen( self.to_screen(
'%s: %s URL is invalid, skipping' % (video_id, item)) '%s: %s URL is invalid, skipping' % (video_id, item))
return False return False
@@ -848,13 +872,18 @@ class InfoExtractor(object):
time.sleep(timeout) time.sleep(timeout)
def _extract_f4m_formats(self, manifest_url, video_id, preference=None, f4m_id=None, def _extract_f4m_formats(self, manifest_url, video_id, preference=None, f4m_id=None,
transform_source=lambda s: fix_xml_ampersands(s).strip()): transform_source=lambda s: fix_xml_ampersands(s).strip(),
fatal=True):
manifest = self._download_xml( manifest = self._download_xml(
manifest_url, video_id, 'Downloading f4m manifest', manifest_url, video_id, 'Downloading f4m manifest',
'Unable to download f4m manifest', 'Unable to download f4m manifest',
# Some manifests may be malformed, e.g. prosiebensat1 generated manifests # Some manifests may be malformed, e.g. prosiebensat1 generated manifests
# (see https://github.com/rg3/youtube-dl/issues/6215#issuecomment-121704244) # (see https://github.com/rg3/youtube-dl/issues/6215#issuecomment-121704244)
transform_source=transform_source) transform_source=transform_source,
fatal=fatal)
if manifest is False:
return manifest
formats = [] formats = []
manifest_version = '1.0' manifest_version = '1.0'
@@ -862,6 +891,11 @@ class InfoExtractor(object):
if not media_nodes: if not media_nodes:
manifest_version = '2.0' manifest_version = '2.0'
media_nodes = manifest.findall('{http://ns.adobe.com/f4m/2.0}media') media_nodes = manifest.findall('{http://ns.adobe.com/f4m/2.0}media')
base_url = xpath_text(
manifest, ['{http://ns.adobe.com/f4m/1.0}baseURL', '{http://ns.adobe.com/f4m/2.0}baseURL'],
'base URL', default=None)
if base_url:
base_url = base_url.strip()
for i, media_el in enumerate(media_nodes): for i, media_el in enumerate(media_nodes):
if manifest_version == '2.0': if manifest_version == '2.0':
media_url = media_el.attrib.get('href') or media_el.attrib.get('url') media_url = media_el.attrib.get('href') or media_el.attrib.get('url')
@@ -869,13 +903,16 @@ class InfoExtractor(object):
continue continue
manifest_url = ( manifest_url = (
media_url if media_url.startswith('http://') or media_url.startswith('https://') media_url if media_url.startswith('http://') or media_url.startswith('https://')
else ('/'.join(manifest_url.split('/')[:-1]) + '/' + media_url)) else ((base_url or '/'.join(manifest_url.split('/')[:-1])) + '/' + media_url))
# If media_url is itself a f4m manifest do the recursive extraction # If media_url is itself a f4m manifest do the recursive extraction
# since bitrates in parent manifest (this one) and media_url manifest # since bitrates in parent manifest (this one) and media_url manifest
# may differ leading to inability to resolve the format by requested # may differ leading to inability to resolve the format by requested
# bitrate in f4m downloader # bitrate in f4m downloader
if determine_ext(manifest_url) == 'f4m': if determine_ext(manifest_url) == 'f4m':
formats.extend(self._extract_f4m_formats(manifest_url, video_id, preference, f4m_id)) f4m_formats = self._extract_f4m_formats(
manifest_url, video_id, preference, f4m_id, fatal=fatal)
if f4m_formats:
formats.extend(f4m_formats)
continue continue
tbr = int_or_none(media_el.attrib.get('bitrate')) tbr = int_or_none(media_el.attrib.get('bitrate'))
formats.append({ formats.append({
@@ -911,13 +948,15 @@ class InfoExtractor(object):
if re.match(r'^https?://', u) if re.match(r'^https?://', u)
else compat_urlparse.urljoin(m3u8_url, u)) else compat_urlparse.urljoin(m3u8_url, u))
m3u8_doc = self._download_webpage( res = self._download_webpage_handle(
m3u8_url, video_id, m3u8_url, video_id,
note=note or 'Downloading m3u8 information', note=note or 'Downloading m3u8 information',
errnote=errnote or 'Failed to download m3u8 information', errnote=errnote or 'Failed to download m3u8 information',
fatal=fatal) fatal=fatal)
if m3u8_doc is False: if res is False:
return m3u8_doc return res
m3u8_doc, urlh = res
m3u8_url = urlh.geturl()
last_info = None last_info = None
last_media = None last_media = None
kv_rex = re.compile( kv_rex = re.compile(
@@ -978,69 +1017,237 @@ class InfoExtractor(object):
self._sort_formats(formats) self._sort_formats(formats)
return formats return formats
# TODO: improve extraction @staticmethod
def _extract_smil_formats(self, smil_url, video_id, fatal=True): def _xpath_ns(path, namespace=None):
smil = self._download_xml( if not namespace:
smil_url, video_id, 'Downloading SMIL file', return path
'Unable to download SMIL file', fatal=fatal) out = []
for c in path.split('/'):
if not c or c == '.':
out.append(c)
else:
out.append('{%s}%s' % (namespace, c))
return '/'.join(out)
def _extract_smil_formats(self, smil_url, video_id, fatal=True, f4m_params=None):
smil = self._download_smil(smil_url, video_id, fatal=fatal)
if smil is False: if smil is False:
assert not fatal assert not fatal
return [] return []
base = smil.find('./head/meta').get('base') namespace = self._parse_smil_namespace(smil)
return self._parse_smil_formats(
smil, smil_url, video_id, namespace=namespace, f4m_params=f4m_params)
def _extract_smil_info(self, smil_url, video_id, fatal=True, f4m_params=None):
smil = self._download_smil(smil_url, video_id, fatal=fatal)
if smil is False:
return {}
return self._parse_smil(smil, smil_url, video_id, f4m_params=f4m_params)
def _download_smil(self, smil_url, video_id, fatal=True):
return self._download_xml(
smil_url, video_id, 'Downloading SMIL file',
'Unable to download SMIL file', fatal=fatal)
def _parse_smil(self, smil, smil_url, video_id, f4m_params=None):
namespace = self._parse_smil_namespace(smil)
formats = self._parse_smil_formats(
smil, smil_url, video_id, namespace=namespace, f4m_params=f4m_params)
subtitles = self._parse_smil_subtitles(smil, namespace=namespace)
video_id = os.path.splitext(url_basename(smil_url))[0]
title = None
description = None
upload_date = None
for meta in smil.findall(self._xpath_ns('./head/meta', namespace)):
name = meta.attrib.get('name')
content = meta.attrib.get('content')
if not name or not content:
continue
if not title and name == 'title':
title = content
elif not description and name in ('description', 'abstract'):
description = content
elif not upload_date and name == 'date':
upload_date = unified_strdate(content)
thumbnails = [{
'id': image.get('type'),
'url': image.get('src'),
'width': int_or_none(image.get('width')),
'height': int_or_none(image.get('height')),
} for image in smil.findall(self._xpath_ns('.//image', namespace)) if image.get('src')]
return {
'id': video_id,
'title': title or video_id,
'description': description,
'upload_date': upload_date,
'thumbnails': thumbnails,
'formats': formats,
'subtitles': subtitles,
}
def _parse_smil_namespace(self, smil):
return self._search_regex(
r'(?i)^{([^}]+)?}smil$', smil.tag, 'namespace', default=None)
def _parse_smil_formats(self, smil, smil_url, video_id, namespace=None, f4m_params=None, transform_rtmp_url=None):
base = smil_url
for meta in smil.findall(self._xpath_ns('./head/meta', namespace)):
b = meta.get('base') or meta.get('httpBase')
if b:
base = b
break
formats = [] formats = []
rtmp_count = 0 rtmp_count = 0
if smil.findall('./body/seq/video'): http_count = 0
video = smil.findall('./body/seq/video')[0]
fmts, rtmp_count = self._parse_smil_video(video, video_id, base, rtmp_count) videos = smil.findall(self._xpath_ns('.//video', namespace))
formats.extend(fmts) for video in videos:
else: src = video.get('src')
for video in smil.findall('./body/switch/video'): if not src:
fmts, rtmp_count = self._parse_smil_video(video, video_id, base, rtmp_count) continue
formats.extend(fmts)
bitrate = float_or_none(video.get('system-bitrate') or video.get('systemBitrate'), 1000)
filesize = int_or_none(video.get('size') or video.get('fileSize'))
width = int_or_none(video.get('width'))
height = int_or_none(video.get('height'))
proto = video.get('proto')
ext = video.get('ext')
src_ext = determine_ext(src)
streamer = video.get('streamer') or base
if proto == 'rtmp' or streamer.startswith('rtmp'):
rtmp_count += 1
formats.append({
'url': streamer,
'play_path': src,
'ext': 'flv',
'format_id': 'rtmp-%d' % (rtmp_count if bitrate is None else bitrate),
'tbr': bitrate,
'filesize': filesize,
'width': width,
'height': height,
})
if transform_rtmp_url:
streamer, src = transform_rtmp_url(streamer, src)
formats[-1].update({
'url': streamer,
'play_path': src,
})
continue
src_url = src if src.startswith('http') else compat_urlparse.urljoin(base, src)
if proto == 'm3u8' or src_ext == 'm3u8':
m3u8_formats = self._extract_m3u8_formats(
src_url, video_id, ext or 'mp4', m3u8_id='hls', fatal=False)
if m3u8_formats:
formats.extend(m3u8_formats)
continue
if src_ext == 'f4m':
f4m_url = src_url
if not f4m_params:
f4m_params = {
'hdcore': '3.2.0',
'plugin': 'flowplayer-3.2.0.1',
}
f4m_url += '&' if '?' in f4m_url else '?'
f4m_url += compat_urllib_parse.urlencode(f4m_params)
f4m_formats = self._extract_f4m_formats(f4m_url, video_id, f4m_id='hds', fatal=False)
if f4m_formats:
formats.extend(f4m_formats)
continue
if src_url.startswith('http') and self._is_valid_url(src, video_id):
http_count += 1
formats.append({
'url': src_url,
'ext': ext or src_ext or 'flv',
'format_id': 'http-%d' % (bitrate or http_count),
'tbr': bitrate,
'filesize': filesize,
'width': width,
'height': height,
})
continue
self._sort_formats(formats) self._sort_formats(formats)
return formats return formats
def _parse_smil_video(self, video, video_id, base, rtmp_count): def _parse_smil_subtitles(self, smil, namespace=None, subtitles_lang='en'):
src = video.get('src') subtitles = {}
if not src: for num, textstream in enumerate(smil.findall(self._xpath_ns('.//textstream', namespace))):
return [], rtmp_count src = textstream.get('src')
bitrate = int_or_none(video.get('system-bitrate') or video.get('systemBitrate'), 1000) if not src:
width = int_or_none(video.get('width')) continue
height = int_or_none(video.get('height')) ext = textstream.get('ext') or determine_ext(src)
proto = video.get('proto') if not ext:
if not proto: type_ = textstream.get('type')
if base: SUBTITLES_TYPES = {
if base.startswith('rtmp'): 'text/vtt': 'vtt',
proto = 'rtmp' 'text/srt': 'srt',
elif base.startswith('http'): 'application/smptett+xml': 'tt',
proto = 'http' }
ext = video.get('ext') if type_ in SUBTITLES_TYPES:
if proto == 'm3u8': ext = SUBTITLES_TYPES[type_]
return self._extract_m3u8_formats(src, video_id, ext), rtmp_count lang = textstream.get('systemLanguage') or textstream.get('systemLanguageName') or textstream.get('lang') or subtitles_lang
elif proto == 'rtmp': subtitles.setdefault(lang, []).append({
rtmp_count += 1 'url': src,
streamer = video.get('streamer') or base 'ext': ext,
return ([{ })
'url': streamer, return subtitles
'play_path': src,
'ext': 'flv', def _extract_xspf_playlist(self, playlist_url, playlist_id, fatal=True):
'format_id': 'rtmp-%d' % (rtmp_count if bitrate is None else bitrate), xspf = self._download_xml(
'tbr': bitrate, playlist_url, playlist_id, 'Downloading xpsf playlist',
'width': width, 'Unable to download xspf manifest', fatal=fatal)
'height': height, if xspf is False:
}], rtmp_count) return []
elif proto.startswith('http'): return self._parse_xspf(xspf, playlist_id)
return ([{
'url': base + src, def _parse_xspf(self, playlist, playlist_id):
'ext': ext or 'flv', NS_MAP = {
'tbr': bitrate, 'xspf': 'http://xspf.org/ns/0/',
'width': width, 's1': 'http://static.streamone.nl/player/ns/0',
'height': height, }
}], rtmp_count)
entries = []
for track in playlist.findall(xpath_with_ns('./xspf:trackList/xspf:track', NS_MAP)):
title = xpath_text(
track, xpath_with_ns('./xspf:title', NS_MAP), 'title', default=playlist_id)
description = xpath_text(
track, xpath_with_ns('./xspf:annotation', NS_MAP), 'description')
thumbnail = xpath_text(
track, xpath_with_ns('./xspf:image', NS_MAP), 'thumbnail')
duration = float_or_none(
xpath_text(track, xpath_with_ns('./xspf:duration', NS_MAP), 'duration'), 1000)
formats = [{
'url': location.text,
'format_id': location.get(xpath_with_ns('s1:label', NS_MAP)),
'width': int_or_none(location.get(xpath_with_ns('s1:width', NS_MAP))),
'height': int_or_none(location.get(xpath_with_ns('s1:height', NS_MAP))),
} for location in track.findall(xpath_with_ns('./xspf:location', NS_MAP))]
self._sort_formats(formats)
entries.append({
'id': playlist_id,
'title': title,
'description': description,
'thumbnail': thumbnail,
'duration': duration,
'formats': formats,
})
return entries
def _live_title(self, name): def _live_title(self, name):
""" Generate the title for a live video """ """ Generate the title for a live video """
@@ -1078,7 +1285,7 @@ class InfoExtractor(object):
def _get_cookies(self, url): def _get_cookies(self, url):
""" Return a compat_cookies.SimpleCookie with the cookies for the url """ """ Return a compat_cookies.SimpleCookie with the cookies for the url """
req = compat_urllib_request.Request(url) req = sanitized_Request(url)
self._downloader.cookiejar.add_cookie_header(req) self._downloader.cookiejar.add_cookie_header(req)
return compat_cookies.SimpleCookie(req.get_header('Cookie')) return compat_cookies.SimpleCookie(req.get_header('Cookie'))
@@ -1120,6 +1327,23 @@ class InfoExtractor(object):
def _get_subtitles(self, *args, **kwargs): def _get_subtitles(self, *args, **kwargs):
raise NotImplementedError("This method must be implemented by subclasses") raise NotImplementedError("This method must be implemented by subclasses")
@staticmethod
def _merge_subtitle_items(subtitle_list1, subtitle_list2):
""" Merge subtitle items for one language. Items with duplicated URLs
will be dropped. """
list1_urls = set([item['url'] for item in subtitle_list1])
ret = list(subtitle_list1)
ret.extend([item for item in subtitle_list2 if item['url'] not in list1_urls])
return ret
@classmethod
def _merge_subtitles(cls, subtitle_dict1, subtitle_dict2):
""" Merge two subtitle dictionaries, language by language. """
ret = dict(subtitle_dict1)
for lang in subtitle_dict2:
ret[lang] = cls._merge_subtitle_items(subtitle_dict1.get(lang, []), subtitle_dict2[lang])
return ret
def extract_automatic_captions(self, *args, **kwargs): def extract_automatic_captions(self, *args, **kwargs):
if (self._downloader.params.get('writeautomaticsub', False) or if (self._downloader.params.get('writeautomaticsub', False) or
self._downloader.params.get('listsubtitles')): self._downloader.params.get('listsubtitles')):

View File

@@ -2,7 +2,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import re import re
import json
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import ( from ..compat import (
@@ -12,6 +11,7 @@ from ..compat import (
) )
from ..utils import ( from ..utils import (
orderedSet, orderedSet,
remove_end,
) )
@@ -24,21 +24,33 @@ class CondeNastIE(InfoExtractor):
# The keys are the supported sites and the values are the name to be shown # The keys are the supported sites and the values are the name to be shown
# to the user and in the extractor description. # to the user and in the extractor description.
_SITES = { _SITES = {
'wired': 'WIRED', 'allure': 'Allure',
'gq': 'GQ', 'architecturaldigest': 'Architectural Digest',
'vogue': 'Vogue', 'arstechnica': 'Ars Technica',
'glamour': 'Glamour', 'bonappetit': 'Bon Appétit',
'wmagazine': 'W Magazine', 'brides': 'Brides',
'vanityfair': 'Vanity Fair',
'cnevids': 'Condé Nast', 'cnevids': 'Condé Nast',
'cntraveler': 'Condé Nast Traveler',
'details': 'Details',
'epicurious': 'Epicurious',
'glamour': 'Glamour',
'golfdigest': 'Golf Digest',
'gq': 'GQ',
'newyorker': 'The New Yorker',
'self': 'SELF',
'teenvogue': 'Teen Vogue',
'vanityfair': 'Vanity Fair',
'vogue': 'Vogue',
'wired': 'WIRED',
'wmagazine': 'W Magazine',
} }
_VALID_URL = r'http://(video|www|player)\.(?P<site>%s)\.com/(?P<type>watch|series|video|embed)/(?P<id>[^/?#]+)' % '|'.join(_SITES.keys()) _VALID_URL = r'http://(?:video|www|player)\.(?P<site>%s)\.com/(?P<type>watch|series|video|embed(?:js)?)/(?P<id>[^/?#]+)' % '|'.join(_SITES.keys())
IE_DESC = 'Condé Nast media group: %s' % ', '.join(sorted(_SITES.values())) IE_DESC = 'Condé Nast media group: %s' % ', '.join(sorted(_SITES.values()))
EMBED_URL = r'(?:https?:)?//player\.(?P<site>%s)\.com/(?P<type>embed)/.+?' % '|'.join(_SITES.keys()) EMBED_URL = r'(?:https?:)?//player\.(?P<site>%s)\.com/(?P<type>embed(?:js)?)/.+?' % '|'.join(_SITES.keys())
_TEST = { _TESTS = [{
'url': 'http://video.wired.com/watch/3d-printed-speakers-lit-with-led', 'url': 'http://video.wired.com/watch/3d-printed-speakers-lit-with-led',
'md5': '1921f713ed48aabd715691f774c451f7', 'md5': '1921f713ed48aabd715691f774c451f7',
'info_dict': { 'info_dict': {
@@ -47,7 +59,16 @@ class CondeNastIE(InfoExtractor):
'title': '3D Printed Speakers Lit With LED', 'title': '3D Printed Speakers Lit With LED',
'description': 'Check out these beautiful 3D printed LED speakers. You can\'t actually buy them, but LumiGeek is working on a board that will let you make you\'re own.', 'description': 'Check out these beautiful 3D printed LED speakers. You can\'t actually buy them, but LumiGeek is working on a board that will let you make you\'re own.',
} }
} }, {
# JS embed
'url': 'http://player.cnevids.com/embedjs/55f9cf8b61646d1acf00000c/5511d76261646d5566020000.js',
'md5': 'f1a6f9cafb7083bab74a710f65d08999',
'info_dict': {
'id': '55f9cf8b61646d1acf00000c',
'ext': 'mp4',
'title': '3D printed TSA Travel Sentry keys really do open TSA locks',
}
}]
def _extract_series(self, url, webpage): def _extract_series(self, url, webpage):
title = self._html_search_regex(r'<div class="cne-series-info">.*?<h1>(.+?)</h1>', title = self._html_search_regex(r'<div class="cne-series-info">.*?<h1>(.+?)</h1>',
@@ -86,8 +107,8 @@ class CondeNastIE(InfoExtractor):
info_url = base_info_url + data info_url = base_info_url + data
info_page = self._download_webpage(info_url, video_id, info_page = self._download_webpage(info_url, video_id,
'Downloading video info') 'Downloading video info')
video_info = self._search_regex(r'var video = ({.+?});', info_page, 'video info') video_info = self._search_regex(r'var\s+video\s*=\s*({.+?});', info_page, 'video info')
video_info = json.loads(video_info) video_info = self._parse_json(video_info, video_id)
formats = [{ formats = [{
'format_id': '%s-%s' % (fdata['type'].split('/')[-1], fdata['quality']), 'format_id': '%s-%s' % (fdata['type'].split('/')[-1], fdata['quality']),
@@ -111,6 +132,13 @@ class CondeNastIE(InfoExtractor):
url_type = mobj.group('type') url_type = mobj.group('type')
item_id = mobj.group('id') item_id = mobj.group('id')
# Convert JS embed to regular embed
if url_type == 'embedjs':
parsed_url = compat_urlparse.urlparse(url)
url = compat_urlparse.urlunparse(parsed_url._replace(
path=remove_end(parsed_url.path, '.js').replace('/embedjs/', '/embed/')))
url_type = 'embed'
self.to_screen('Extracting from %s with the Condé Nast extractor' % self._SITES[site]) self.to_screen('Extracting from %s with the Condé Nast extractor' % self._SITES[site])
webpage = self._download_webpage(url, item_id) webpage = self._download_webpage(url, item_id)

View File

@@ -27,9 +27,7 @@ class CriterionIE(InfoExtractor):
final_url = self._search_regex( final_url = self._search_regex(
r'so.addVariable\("videoURL", "(.+?)"\)\;', webpage, 'video url') r'so.addVariable\("videoURL", "(.+?)"\)\;', webpage, 'video url')
title = self._og_search_title(webpage) title = self._og_search_title(webpage)
description = self._html_search_regex( description = self._html_search_meta('description', webpage)
r'<meta name="description" content="(.+?)" />',
webpage, 'video description')
thumbnail = self._search_regex( thumbnail = self._search_regex(
r'so.addVariable\("thumbnailURL", "(.+?)"\)\;', r'so.addVariable\("thumbnailURL", "(.+?)"\)\;',
webpage, 'thumbnail url') webpage, 'thumbnail url')

View File

@@ -5,31 +5,85 @@ import re
import json import json
import base64 import base64
import zlib import zlib
import xml.etree.ElementTree
from hashlib import sha1 from hashlib import sha1
from math import pow, sqrt, floor from math import pow, sqrt, floor
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import ( from ..compat import (
compat_etree_fromstring,
compat_urllib_parse, compat_urllib_parse,
compat_urllib_parse_unquote, compat_urllib_parse_unquote,
compat_urllib_request, compat_urllib_request,
compat_urlparse,
) )
from ..utils import ( from ..utils import (
ExtractorError, ExtractorError,
bytes_to_intlist, bytes_to_intlist,
intlist_to_bytes, intlist_to_bytes,
int_or_none,
lowercase_escape,
remove_end,
sanitized_Request,
unified_strdate, unified_strdate,
urlencode_postdata, urlencode_postdata,
xpath_text,
) )
from ..aes import ( from ..aes import (
aes_cbc_decrypt, aes_cbc_decrypt,
) )
class CrunchyrollIE(InfoExtractor): class CrunchyrollBaseIE(InfoExtractor):
_VALID_URL = r'https?://(?:(?P<prefix>www|m)\.)?(?P<url>crunchyroll\.(?:com|fr)/(?:media(?:-|/\?id=)|[^/]*/[^/?&]*?)(?P<video_id>[0-9]+))(?:[/?&]|$)'
_NETRC_MACHINE = 'crunchyroll' _NETRC_MACHINE = 'crunchyroll'
def _login(self):
(username, password) = self._get_login_info()
if username is None:
return
self.report_login()
login_url = 'https://www.crunchyroll.com/?a=formhandler'
data = urlencode_postdata({
'formname': 'RpcApiUser_Login',
'name': username,
'password': password,
})
login_request = sanitized_Request(login_url, data)
login_request.add_header('Content-Type', 'application/x-www-form-urlencoded')
self._download_webpage(login_request, None, False, 'Wrong login info')
def _real_initialize(self):
self._login()
def _download_webpage(self, url_or_request, video_id, note=None, errnote=None, fatal=True, tries=1, timeout=5, encoding=None):
request = (url_or_request if isinstance(url_or_request, compat_urllib_request.Request)
else sanitized_Request(url_or_request))
# Accept-Language must be set explicitly to accept any language to avoid issues
# similar to https://github.com/rg3/youtube-dl/issues/6797.
# Along with IP address Crunchyroll uses Accept-Language to guess whether georestriction
# should be imposed or not (from what I can see it just takes the first language
# ignoring the priority and requires it to correspond the IP). By the way this causes
# Crunchyroll to not work in georestriction cases in some browsers that don't place
# the locale lang first in header. However allowing any language seems to workaround the issue.
request.add_header('Accept-Language', '*')
return super(CrunchyrollBaseIE, self)._download_webpage(
request, video_id, note, errnote, fatal, tries, timeout, encoding)
@staticmethod
def _add_skip_wall(url):
parsed_url = compat_urlparse.urlparse(url)
qs = compat_urlparse.parse_qs(parsed_url.query)
# Always force skip_wall to bypass maturity wall, namely 18+ confirmation message:
# > This content may be inappropriate for some people.
# > Are you sure you want to continue?
# since it's not disabled by default in crunchyroll account's settings.
# See https://github.com/rg3/youtube-dl/issues/7202.
qs['skip_wall'] = ['1']
return compat_urlparse.urlunparse(
parsed_url._replace(query=compat_urllib_parse.urlencode(qs, True)))
class CrunchyrollIE(CrunchyrollBaseIE):
_VALID_URL = r'https?://(?:(?P<prefix>www|m)\.)?(?P<url>crunchyroll\.(?:com|fr)/(?:media(?:-|/\?id=)|[^/]*/[^/?&]*?)(?P<video_id>[0-9]+))(?:[/?&]|$)'
_TESTS = [{ _TESTS = [{
'url': 'http://www.crunchyroll.com/wanna-be-the-strongest-in-the-world/episode-1-an-idol-wrestler-is-born-645513', 'url': 'http://www.crunchyroll.com/wanna-be-the-strongest-in-the-world/episode-1-an-idol-wrestler-is-born-645513',
'info_dict': { 'info_dict': {
@@ -52,7 +106,7 @@ class CrunchyrollIE(InfoExtractor):
'id': '589804', 'id': '589804',
'ext': 'flv', 'ext': 'flv',
'title': 'Culture Japan Episode 1 Rebuilding Japan after the 3.11', 'title': 'Culture Japan Episode 1 Rebuilding Japan after the 3.11',
'description': 'md5:fe2743efedb49d279552926d0bd0cd9e', 'description': 'md5:2fbc01f90b87e8e9137296f37b461c12',
'thumbnail': 're:^https?://.*\.jpg$', 'thumbnail': 're:^https?://.*\.jpg$',
'uploader': 'Danny Choo Network', 'uploader': 'Danny Choo Network',
'upload_date': '20120213', 'upload_date': '20120213',
@@ -61,10 +115,13 @@ class CrunchyrollIE(InfoExtractor):
# rtmp # rtmp
'skip_download': True, 'skip_download': True,
}, },
}, { }, {
'url': 'http://www.crunchyroll.fr/girl-friend-beta/episode-11-goodbye-la-mode-661697', 'url': 'http://www.crunchyroll.fr/girl-friend-beta/episode-11-goodbye-la-mode-661697',
'only_matching': True, 'only_matching': True,
}, {
# geo-restricted (US), 18+ maturity wall, non-premium available
'url': 'http://www.crunchyroll.com/cosplay-complex-ova/episode-1-the-birth-of-the-cosplay-club-565617',
'only_matching': True,
}] }]
_FORMAT_IDS = { _FORMAT_IDS = {
@@ -74,24 +131,6 @@ class CrunchyrollIE(InfoExtractor):
'1080': ('80', '108'), '1080': ('80', '108'),
} }
def _login(self):
(username, password) = self._get_login_info()
if username is None:
return
self.report_login()
login_url = 'https://www.crunchyroll.com/?a=formhandler'
data = urlencode_postdata({
'formname': 'RpcApiUser_Login',
'name': username,
'password': password,
})
login_request = compat_urllib_request.Request(login_url, data)
login_request.add_header('Content-Type', 'application/x-www-form-urlencoded')
self._download_webpage(login_request, None, False, 'Wrong login info')
def _real_initialize(self):
self._login()
def _decrypt_subtitles(self, data, iv, id): def _decrypt_subtitles(self, data, iv, id):
data = bytes_to_intlist(base64.b64decode(data.encode('utf-8'))) data = bytes_to_intlist(base64.b64decode(data.encode('utf-8')))
iv = bytes_to_intlist(base64.b64decode(iv.encode('utf-8'))) iv = bytes_to_intlist(base64.b64decode(iv.encode('utf-8')))
@@ -197,7 +236,7 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
return output return output
def _extract_subtitles(self, subtitle): def _extract_subtitles(self, subtitle):
sub_root = xml.etree.ElementTree.fromstring(subtitle) sub_root = compat_etree_fromstring(subtitle)
return [{ return [{
'ext': 'srt', 'ext': 'srt',
'data': self._convert_subtitles_to_srt(sub_root), 'data': self._convert_subtitles_to_srt(sub_root),
@@ -208,7 +247,7 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
def _get_subtitles(self, video_id, webpage): def _get_subtitles(self, video_id, webpage):
subtitles = {} subtitles = {}
for sub_id, sub_name in re.findall(r'\?ssid=([0-9]+)" title="([^"]+)', webpage): for sub_id, sub_name in re.findall(r'\bssid=([0-9]+)"[^>]+?\btitle="([^"]+)', webpage):
sub_page = self._download_webpage( sub_page = self._download_webpage(
'http://www.crunchyroll.com/xml/?req=RpcApiSubtitle_GetXml&subtitle_script_id=' + sub_id, 'http://www.crunchyroll.com/xml/?req=RpcApiSubtitle_GetXml&subtitle_script_id=' + sub_id,
video_id, note='Downloading subtitles for ' + sub_name) video_id, note='Downloading subtitles for ' + sub_name)
@@ -234,8 +273,10 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
else: else:
webpage_url = 'http://www.' + mobj.group('url') webpage_url = 'http://www.' + mobj.group('url')
webpage = self._download_webpage(webpage_url, video_id, 'Downloading webpage') webpage = self._download_webpage(self._add_skip_wall(webpage_url), video_id, 'Downloading webpage')
note_m = self._html_search_regex(r'<div class="showmedia-trailer-notice">(.+?)</div>', webpage, 'trailer-notice', default='') note_m = self._html_search_regex(
r'<div class="showmedia-trailer-notice">(.+?)</div>',
webpage, 'trailer-notice', default='')
if note_m: if note_m:
raise ExtractorError(note_m) raise ExtractorError(note_m)
@@ -245,18 +286,29 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
if msg.get('type') == 'error': if msg.get('type') == 'error':
raise ExtractorError('crunchyroll returned error: %s' % msg['message_body'], expected=True) raise ExtractorError('crunchyroll returned error: %s' % msg['message_body'], expected=True)
video_title = self._html_search_regex(r'<h1[^>]*>(.+?)</h1>', webpage, 'video_title', flags=re.DOTALL) if 'To view this, please log in to verify you are 18 or older.' in webpage:
self.raise_login_required()
video_title = self._html_search_regex(
r'(?s)<h1[^>]*>((?:(?!<h1).)*?<span[^>]+itemprop=["\']title["\'][^>]*>(?:(?!<h1).)+?)</h1>',
webpage, 'video_title')
video_title = re.sub(r' {2,}', ' ', video_title) video_title = re.sub(r' {2,}', ' ', video_title)
video_description = self._html_search_regex(r'"description":"([^"]+)', webpage, 'video_description', default='') video_description = self._html_search_regex(
if not video_description: r'<script[^>]*>\s*.+?\[media_id=%s\].+?"description"\s*:\s*"([^"]+)' % video_id,
video_description = None webpage, 'description', default=None)
video_upload_date = self._html_search_regex(r'<div>Availability for free users:(.+?)</div>', webpage, 'video_upload_date', fatal=False, flags=re.DOTALL) if video_description:
video_description = lowercase_escape(video_description.replace(r'\r\n', '\n'))
video_upload_date = self._html_search_regex(
[r'<div>Availability for free users:(.+?)</div>', r'<div>[^<>]+<span>\s*(.+?\d{4})\s*</span></div>'],
webpage, 'video_upload_date', fatal=False, flags=re.DOTALL)
if video_upload_date: if video_upload_date:
video_upload_date = unified_strdate(video_upload_date) video_upload_date = unified_strdate(video_upload_date)
video_uploader = self._html_search_regex(r'<div>\s*Publisher:(.+?)</div>', webpage, 'video_uploader', fatal=False, flags=re.DOTALL) video_uploader = self._html_search_regex(
r'<a[^>]+href="/publisher/[^"]+"[^>]*>([^<]+)</a>', webpage,
'video_uploader', fatal=False)
playerdata_url = compat_urllib_parse_unquote(self._html_search_regex(r'"config_url":"([^"]+)', webpage, 'playerdata_url')) playerdata_url = compat_urllib_parse_unquote(self._html_search_regex(r'"config_url":"([^"]+)', webpage, 'playerdata_url'))
playerdata_req = compat_urllib_request.Request(playerdata_url) playerdata_req = sanitized_Request(playerdata_url)
playerdata_req.data = compat_urllib_parse.urlencode({'current_page': webpage_url}) playerdata_req.data = compat_urllib_parse.urlencode({'current_page': webpage_url})
playerdata_req.add_header('Content-Type', 'application/x-www-form-urlencoded') playerdata_req.add_header('Content-Type', 'application/x-www-form-urlencoded')
playerdata = self._download_webpage(playerdata_req, video_id, note='Downloading media info') playerdata = self._download_webpage(playerdata_req, video_id, note='Downloading media info')
@@ -268,7 +320,7 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
for fmt in re.findall(r'showmedia\.([0-9]{3,4})p', webpage): for fmt in re.findall(r'showmedia\.([0-9]{3,4})p', webpage):
stream_quality, stream_format = self._FORMAT_IDS[fmt] stream_quality, stream_format = self._FORMAT_IDS[fmt]
video_format = fmt + 'p' video_format = fmt + 'p'
streamdata_req = compat_urllib_request.Request( streamdata_req = sanitized_Request(
'http://www.crunchyroll.com/xml/?req=RpcApiVideoPlayer_GetStandardConfig&media_id=%s&video_format=%s&video_quality=%s' 'http://www.crunchyroll.com/xml/?req=RpcApiVideoPlayer_GetStandardConfig&media_id=%s&video_format=%s&video_quality=%s'
% (stream_id, stream_format, stream_quality), % (stream_id, stream_format, stream_quality),
compat_urllib_parse.urlencode({'current_page': url}).encode('utf-8')) compat_urllib_parse.urlencode({'current_page': url}).encode('utf-8'))
@@ -279,13 +331,33 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
stream_info = streamdata.find('./{default}preload/stream_info') stream_info = streamdata.find('./{default}preload/stream_info')
video_url = stream_info.find('./host').text video_url = stream_info.find('./host').text
video_play_path = stream_info.find('./file').text video_play_path = stream_info.find('./file').text
formats.append({ metadata = stream_info.find('./metadata')
format_info = {
'format': video_format,
'format_id': video_format,
'height': int_or_none(xpath_text(metadata, './height')),
'width': int_or_none(xpath_text(metadata, './width')),
}
if '.fplive.net/' in video_url:
video_url = re.sub(r'^rtmpe?://', 'http://', video_url.strip())
parsed_video_url = compat_urlparse.urlparse(video_url)
direct_video_url = compat_urlparse.urlunparse(parsed_video_url._replace(
netloc='v.lvlt.crcdn.net',
path='%s/%s' % (remove_end(parsed_video_url.path, '/'), video_play_path.split(':')[-1])))
if self._is_valid_url(direct_video_url, video_id, video_format):
format_info.update({
'url': direct_video_url,
})
formats.append(format_info)
continue
format_info.update({
'url': video_url, 'url': video_url,
'play_path': video_play_path, 'play_path': video_play_path,
'ext': 'flv', 'ext': 'flv',
'format': video_format,
'format_id': video_format,
}) })
formats.append(format_info)
subtitles = self.extract_subtitles(video_id, webpage) subtitles = self.extract_subtitles(video_id, webpage)
@@ -301,9 +373,9 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
} }
class CrunchyrollShowPlaylistIE(InfoExtractor): class CrunchyrollShowPlaylistIE(CrunchyrollBaseIE):
IE_NAME = "crunchyroll:playlist" IE_NAME = "crunchyroll:playlist"
_VALID_URL = r'https?://(?:(?P<prefix>www|m)\.)?(?P<url>crunchyroll\.com/(?!(?:news|anime-news|library|forum|launchcalendar|lineup|store|comics|freetrial|login))(?P<id>[\w\-]+))/?$' _VALID_URL = r'https?://(?:(?P<prefix>www|m)\.)?(?P<url>crunchyroll\.com/(?!(?:news|anime-news|library|forum|launchcalendar|lineup|store|comics|freetrial|login))(?P<id>[\w\-]+))/?(?:\?|$)'
_TESTS = [{ _TESTS = [{
'url': 'http://www.crunchyroll.com/a-bridge-to-the-starry-skies-hoshizora-e-kakaru-hashi', 'url': 'http://www.crunchyroll.com/a-bridge-to-the-starry-skies-hoshizora-e-kakaru-hashi',
@@ -312,12 +384,25 @@ class CrunchyrollShowPlaylistIE(InfoExtractor):
'title': 'A Bridge to the Starry Skies - Hoshizora e Kakaru Hashi' 'title': 'A Bridge to the Starry Skies - Hoshizora e Kakaru Hashi'
}, },
'playlist_count': 13, 'playlist_count': 13,
}, {
# geo-restricted (US), 18+ maturity wall, non-premium available
'url': 'http://www.crunchyroll.com/cosplay-complex-ova',
'info_dict': {
'id': 'cosplay-complex-ova',
'title': 'Cosplay Complex OVA'
},
'playlist_count': 3,
'skip': 'Georestricted',
}, {
# geo-restricted (US), 18+ maturity wall, non-premium will be available since 2015.11.14
'url': 'http://www.crunchyroll.com/ladies-versus-butlers?skip_wall=1',
'only_matching': True,
}] }]
def _real_extract(self, url): def _real_extract(self, url):
show_id = self._match_id(url) show_id = self._match_id(url)
webpage = self._download_webpage(url, show_id) webpage = self._download_webpage(self._add_skip_wall(url), show_id)
title = self._html_search_regex( title = self._html_search_regex(
r'(?s)<h1[^>]*>\s*<span itemprop="name">(.*?)</span>', r'(?s)<h1[^>]*>\s*<span itemprop="name">(.*?)</span>',
webpage, 'title') webpage, 'title')

View File

@@ -9,6 +9,7 @@ from ..utils import (
find_xpath_attr, find_xpath_attr,
smuggle_url, smuggle_url,
determine_ext, determine_ext,
ExtractorError,
) )
from .senateisvp import SenateISVPIE from .senateisvp import SenateISVPIE
@@ -18,33 +19,32 @@ class CSpanIE(InfoExtractor):
IE_DESC = 'C-SPAN' IE_DESC = 'C-SPAN'
_TESTS = [{ _TESTS = [{
'url': 'http://www.c-span.org/video/?313572-1/HolderonV', 'url': 'http://www.c-span.org/video/?313572-1/HolderonV',
'md5': '8e44ce11f0f725527daccc453f553eb0', 'md5': '94b29a4f131ff03d23471dd6f60b6a1d',
'info_dict': { 'info_dict': {
'id': '315139', 'id': '315139',
'ext': 'mp4', 'ext': 'mp4',
'title': 'Attorney General Eric Holder on Voting Rights Act Decision', 'title': 'Attorney General Eric Holder on Voting Rights Act Decision',
'description': 'Attorney General Eric Holder spoke to reporters following the Supreme Court decision in Shelby County v. Holder in which the court ruled that the preclearance provisions of the Voting Rights Act could not be enforced until Congress established new guidelines for review.', 'description': 'Attorney General Eric Holder speaks to reporters following the Supreme Court decision in [Shelby County v. Holder], in which the court ruled that the preclearance provisions of the Voting Rights Act could not be enforced.',
}, },
'skip': 'Regularly fails on travis, for unknown reasons', 'skip': 'Regularly fails on travis, for unknown reasons',
}, { }, {
'url': 'http://www.c-span.org/video/?c4486943/cspan-international-health-care-models', 'url': 'http://www.c-span.org/video/?c4486943/cspan-international-health-care-models',
# For whatever reason, the served video alternates between 'md5': '8e5fbfabe6ad0f89f3012a7943c1287b',
# two different ones
'info_dict': { 'info_dict': {
'id': '340723', 'id': 'c4486943',
'ext': 'mp4', 'ext': 'mp4',
'title': 'International Health Care Models', 'title': 'CSPAN - International Health Care Models',
'description': 'md5:7a985a2d595dba00af3d9c9f0783c967', 'description': 'md5:7a985a2d595dba00af3d9c9f0783c967',
} }
}, { }, {
'url': 'http://www.c-span.org/video/?318608-1/gm-ignition-switch-recall', 'url': 'http://www.c-span.org/video/?318608-1/gm-ignition-switch-recall',
'md5': '446562a736c6bf97118e389433ed88d4', 'md5': '2ae5051559169baadba13fc35345ae74',
'info_dict': { 'info_dict': {
'id': '342759', 'id': '342759',
'ext': 'mp4', 'ext': 'mp4',
'title': 'General Motors Ignition Switch Recall', 'title': 'General Motors Ignition Switch Recall',
'duration': 14848, 'duration': 14848,
'description': 'md5:70c7c3b8fa63fa60d42772440596034c' 'description': 'md5:118081aedd24bf1d3b68b3803344e7f3'
}, },
}, { }, {
# Video from senate.gov # Video from senate.gov
@@ -57,67 +57,77 @@ class CSpanIE(InfoExtractor):
}] }]
def _real_extract(self, url): def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url) video_id = self._match_id(url)
page_id = mobj.group('id') webpage = self._download_webpage(url, video_id)
webpage = self._download_webpage(url, page_id) matches = re.search(r'data-(prog|clip)id=\'([0-9]+)\'', webpage)
video_id = self._search_regex(r'progid=\'?([0-9]+)\'?>', webpage, 'video id') if matches:
video_type, video_id = matches.groups()
if video_type == 'prog':
video_type = 'program'
else:
senate_isvp_url = SenateISVPIE._search_iframe_url(webpage)
if senate_isvp_url:
title = self._og_search_title(webpage)
surl = smuggle_url(senate_isvp_url, {'force_title': title})
return self.url_result(surl, 'SenateISVP', video_id, title)
description = self._html_search_regex( def get_text_attr(d, attr):
[ return d.get(attr, {}).get('#text')
# The full description
r'<div class=\'expandable\'>(.*?)<a href=\'#\'',
# If the description is small enough the other div is not
# present, otherwise this is a stripped version
r'<p class=\'initial\'>(.*?)</p>'
],
webpage, 'description', flags=re.DOTALL, default=None)
info_url = 'http://c-spanvideo.org/videoLibrary/assets/player/ajax-player.php?os=android&html5=program&id=' + video_id data = self._download_json(
data = self._download_json(info_url, video_id) 'http://www.c-span.org/assets/player/ajax-player.php?os=android&html5=%s&id=%s' % (video_type, video_id),
video_id)['video']
if data['@status'] != 'Success':
raise ExtractorError('%s said: %s' % (self.IE_NAME, get_text_attr(data, 'error')), expected=True)
doc = self._download_xml( doc = self._download_xml(
'http://www.c-span.org/common/services/flashXml.php?programid=' + video_id, 'http://www.c-span.org/common/services/flashXml.php?%sid=%s' % (video_type, video_id),
video_id) video_id)
description = self._html_search_meta('description', webpage)
title = find_xpath_attr(doc, './/string', 'name', 'title').text title = find_xpath_attr(doc, './/string', 'name', 'title').text
thumbnail = find_xpath_attr(doc, './/string', 'name', 'poster').text thumbnail = find_xpath_attr(doc, './/string', 'name', 'poster').text
senate_isvp_url = SenateISVPIE._search_iframe_url(webpage) files = data['files']
if senate_isvp_url: capfile = get_text_attr(data, 'capfile')
surl = smuggle_url(senate_isvp_url, {'force_title': title})
return self.url_result(surl, 'SenateISVP', video_id, title)
files = data['video']['files'] entries = []
try: for partnum, f in enumerate(files):
capfile = data['video']['capfile']['#text'] formats = []
except KeyError: for quality in f['qualities']:
capfile = None formats.append({
'format_id': '%s-%sp' % (get_text_attr(quality, 'bitrate'), get_text_attr(quality, 'height')),
entries = [{ 'url': unescapeHTML(get_text_attr(quality, 'file')),
'id': '%s_%d' % (video_id, partnum + 1), 'height': int_or_none(get_text_attr(quality, 'height')),
'title': ( 'tbr': int_or_none(get_text_attr(quality, 'bitrate')),
title if len(files) == 1 else })
'%s part %d' % (title, partnum + 1)), self._sort_formats(formats)
'url': unescapeHTML(f['path']['#text']), entries.append({
'description': description, 'id': '%s_%d' % (video_id, partnum + 1),
'thumbnail': thumbnail, 'title': (
'duration': int_or_none(f.get('length', {}).get('#text')), title if len(files) == 1 else
'subtitles': { '%s part %d' % (title, partnum + 1)),
'en': [{ 'formats': formats,
'url': capfile, 'description': description,
'ext': determine_ext(capfile, 'dfxp') 'thumbnail': thumbnail,
}], 'duration': int_or_none(get_text_attr(f, 'length')),
} if capfile else None, 'subtitles': {
} for partnum, f in enumerate(files)] 'en': [{
'url': capfile,
'ext': determine_ext(capfile, 'dfxp')
}],
} if capfile else None,
})
if len(entries) == 1: if len(entries) == 1:
entry = dict(entries[0]) entry = dict(entries[0])
entry['id'] = video_id entry['id'] = 'c' + video_id if video_type == 'clip' else video_id
return entry return entry
else: else:
return { return {
'_type': 'playlist', '_type': 'playlist',
'entries': entries, 'entries': entries,
'title': title, 'title': title,
'id': video_id, 'id': 'c' + video_id if video_type == 'clip' else video_id,
} }

View File

@@ -7,15 +7,13 @@ import itertools
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import ( from ..compat import compat_str
compat_str,
compat_urllib_request,
)
from ..utils import ( from ..utils import (
ExtractorError, ExtractorError,
determine_ext, determine_ext,
int_or_none, int_or_none,
parse_iso8601, parse_iso8601,
sanitized_Request,
str_to_int, str_to_int,
unescapeHTML, unescapeHTML,
) )
@@ -25,7 +23,7 @@ class DailymotionBaseInfoExtractor(InfoExtractor):
@staticmethod @staticmethod
def _build_request(url): def _build_request(url):
"""Build a request with the family filter disabled""" """Build a request with the family filter disabled"""
request = compat_urllib_request.Request(url) request = sanitized_Request(url)
request.add_header('Cookie', 'family_filter=off; ff=off') request.add_header('Cookie', 'family_filter=off; ff=off')
return request return request
@@ -96,6 +94,16 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
'uploader': 'HotWaves1012', 'uploader': 'HotWaves1012',
'age_limit': 18, 'age_limit': 18,
} }
},
# geo-restricted, player v5
{
'url': 'http://www.dailymotion.com/video/xhza0o',
'only_matching': True,
},
# with subtitles
{
'url': 'http://www.dailymotion.com/video/x20su5f_the-power-of-nightmares-1-the-rise-of-the-politics-of-fear-bbc-2004_news',
'only_matching': True,
} }
] ]
@@ -119,11 +127,16 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
webpage, 'comment count', fatal=False)) webpage, 'comment count', fatal=False))
player_v5 = self._search_regex( player_v5 = self._search_regex(
r'playerV5\s*=\s*dmp\.create\([^,]+?,\s*({.+?})\);', [r'buildPlayer\(({.+?})\);\n', # See https://github.com/rg3/youtube-dl/issues/7826
r'playerV5\s*=\s*dmp\.create\([^,]+?,\s*({.+?})\);',
r'buildPlayer\(({.+?})\);'],
webpage, 'player v5', default=None) webpage, 'player v5', default=None)
if player_v5: if player_v5:
player = self._parse_json(player_v5, video_id) player = self._parse_json(player_v5, video_id)
metadata = player['metadata'] metadata = player['metadata']
self._check_error(metadata)
formats = [] formats = []
for quality, media_list in metadata['qualities'].items(): for quality, media_list in metadata['qualities'].items():
for media in media_list: for media in media_list:
@@ -133,9 +146,17 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
type_ = media.get('type') type_ = media.get('type')
if type_ == 'application/vnd.lumberjack.manifest': if type_ == 'application/vnd.lumberjack.manifest':
continue continue
if type_ == 'application/x-mpegURL' or determine_ext(media_url) == 'm3u8': ext = determine_ext(media_url)
formats.extend(self._extract_m3u8_formats( if type_ == 'application/x-mpegURL' or ext == 'm3u8':
media_url, video_id, 'mp4', m3u8_id='hls')) m3u8_formats = self._extract_m3u8_formats(
media_url, video_id, 'mp4', m3u8_id='hls', fatal=False)
if m3u8_formats:
formats.extend(m3u8_formats)
elif type_ == 'application/f4m' or ext == 'f4m':
f4m_formats = self._extract_f4m_formats(
media_url, video_id, preference=-1, f4m_id='hds', fatal=False)
if f4m_formats:
formats.extend(f4m_formats)
else: else:
f = { f = {
'url': media_url, 'url': media_url,
@@ -158,11 +179,13 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
uploader_id = metadata.get('owner', {}).get('id') uploader_id = metadata.get('owner', {}).get('id')
subtitles = {} subtitles = {}
for subtitle_lang, subtitle in metadata.get('subtitles', {}).get('data', {}).items(): subtitles_data = metadata.get('subtitles', {}).get('data', {})
subtitles[subtitle_lang] = [{ if subtitles_data and isinstance(subtitles_data, dict):
'ext': determine_ext(subtitle_url), for subtitle_lang, subtitle in subtitles_data.items():
'url': subtitle_url, subtitles[subtitle_lang] = [{
} for subtitle_url in subtitle.get('urls', [])] 'ext': determine_ext(subtitle_url),
'url': subtitle_url,
} for subtitle_url in subtitle.get('urls', [])]
return { return {
'id': video_id, 'id': video_id,
@@ -201,9 +224,7 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
'video info', flags=re.MULTILINE), 'video info', flags=re.MULTILINE),
video_id) video_id)
if info.get('error') is not None: self._check_error(info)
msg = 'Couldn\'t get video, Dailymotion says: %s' % info['error']['title']
raise ExtractorError(msg, expected=True)
formats = [] formats = []
for (key, format_id) in self._FORMATS: for (key, format_id) in self._FORMATS:
@@ -246,6 +267,11 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
'duration': info['duration'] 'duration': info['duration']
} }
def _check_error(self, info):
if info.get('error') is not None:
raise ExtractorError(
'%s said: %s' % (self.IE_NAME, info['error']['title']), expected=True)
def _get_subtitles(self, video_id, webpage): def _get_subtitles(self, video_id, webpage):
try: try:
sub_list = self._download_webpage( sub_list = self._download_webpage(

View File

@@ -13,8 +13,8 @@ from ..utils import (
class DBTVIE(InfoExtractor): class DBTVIE(InfoExtractor):
_VALID_URL = r'http://dbtv\.no/(?P<id>[0-9]+)#(?P<display_id>.+)' _VALID_URL = r'https?://(?:www\.)?dbtv\.no/(?:(?:lazyplayer|player)/)?(?P<id>[0-9]+)(?:#(?P<display_id>.+))?'
_TEST = { _TESTS = [{
'url': 'http://dbtv.no/3649835190001#Skulle_teste_ut_fornøyelsespark,_men_kollegaen_var_bare_opptatt_av_bikinikroppen', 'url': 'http://dbtv.no/3649835190001#Skulle_teste_ut_fornøyelsespark,_men_kollegaen_var_bare_opptatt_av_bikinikroppen',
'md5': 'b89953ed25dacb6edb3ef6c6f430f8bc', 'md5': 'b89953ed25dacb6edb3ef6c6f430f8bc',
'info_dict': { 'info_dict': {
@@ -30,12 +30,18 @@ class DBTVIE(InfoExtractor):
'view_count': int, 'view_count': int,
'categories': list, 'categories': list,
} }
} }, {
'url': 'http://dbtv.no/3649835190001',
'only_matching': True,
}, {
'url': 'http://www.dbtv.no/lazyplayer/4631135248001',
'only_matching': True,
}]
def _real_extract(self, url): def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url) mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id') video_id = mobj.group('id')
display_id = mobj.group('display_id') display_id = mobj.group('display_id') or video_id
data = self._download_json( data = self._download_json(
'http://api.dbtv.no/discovery/%s' % video_id, display_id) 'http://api.dbtv.no/discovery/%s' % video_id, display_id)

View File

@@ -0,0 +1,82 @@
# coding: utf-8
from __future__ import unicode_literals
from .common import InfoExtractor
from ..compat import compat_urllib_parse
from ..utils import (
int_or_none,
parse_iso8601,
sanitized_Request,
)
class DCNIE(InfoExtractor):
_VALID_URL = r'https?://(?:www\.)?dcndigital\.ae/(?:#/)?(?:video/.+|show/\d+/.+?)/(?P<id>\d+)'
_TEST = {
'url': 'http://www.dcndigital.ae/#/show/199074/%D8%B1%D8%AD%D9%84%D8%A9-%D8%A7%D9%84%D8%B9%D9%85%D8%B1-%D8%A7%D9%84%D8%AD%D9%84%D9%82%D8%A9-1/17375/6887',
'info_dict':
{
'id': '17375',
'ext': 'mp4',
'title': 'رحلة العمر : الحلقة 1',
'description': 'md5:0156e935d870acb8ef0a66d24070c6d6',
'thumbnail': 're:^https?://.*\.jpg$',
'duration': 2041,
'timestamp': 1227504126,
'upload_date': '20081124',
},
'params': {
# m3u8 download
'skip_download': True,
},
}
def _real_extract(self, url):
video_id = self._match_id(url)
request = sanitized_Request(
'http://admin.mangomolo.com/analytics/index.php/plus/video?id=%s' % video_id,
headers={'Origin': 'http://www.dcndigital.ae'})
video = self._download_json(request, video_id)
title = video.get('title_en') or video['title_ar']
webpage = self._download_webpage(
'http://admin.mangomolo.com/analytics/index.php/customers/embed/video?' +
compat_urllib_parse.urlencode({
'id': video['id'],
'user_id': video['user_id'],
'signature': video['signature'],
'countries': 'Q0M=',
'filter': 'DENY',
}), video_id)
m3u8_url = self._html_search_regex(r'file:\s*"([^"]+)', webpage, 'm3u8 url')
formats = self._extract_m3u8_formats(
m3u8_url, video_id, 'mp4', entry_protocol='m3u8_native', m3u8_id='hls')
rtsp_url = self._search_regex(
r'<a[^>]+href="(rtsp://[^"]+)"', webpage, 'rtsp url', fatal=False)
if rtsp_url:
formats.append({
'url': rtsp_url,
'format_id': 'rtsp',
})
self._sort_formats(formats)
img = video.get('img')
thumbnail = 'http://admin.mangomolo.com/analytics/%s' % img if img else None
duration = int_or_none(video.get('duration'))
description = video.get('description_en') or video.get('description_ar')
timestamp = parse_iso8601(video.get('create_time') or video.get('update_time'), ' ')
return {
'id': video_id,
'title': title,
'description': description,
'thumbnail': thumbnail,
'duration': duration,
'timestamp': timestamp,
'formats': formats,
}

View File

@@ -0,0 +1,88 @@
# coding: utf-8
from __future__ import unicode_literals
import re
import os.path
from .common import InfoExtractor
from ..compat import compat_urlparse
from ..utils import (
url_basename,
remove_start,
)
class DemocracynowIE(InfoExtractor):
_VALID_URL = r'https?://(?:www\.)?democracynow.org/(?P<id>[^\?]*)'
IE_NAME = 'democracynow'
_TESTS = [{
'url': 'http://www.democracynow.org/shows/2015/7/3',
'md5': 'fbb8fe3d7a56a5e12431ce2f9b2fab0d',
'info_dict': {
'id': '2015-0703-001',
'ext': 'mp4',
'title': 'July 03, 2015 - Democracy Now!',
'description': 'A daily independent global news hour with Amy Goodman & Juan González "What to the Slave is 4th of July?": James Earl Jones Reads Frederick Douglass\u2019 Historic Speech : "This Flag Comes Down Today": Bree Newsome Scales SC Capitol Flagpole, Takes Down Confederate Flag : "We Shall Overcome": Remembering Folk Icon, Activist Pete Seeger in His Own Words & Songs',
},
}, {
'url': 'http://www.democracynow.org/2015/7/3/this_flag_comes_down_today_bree',
'md5': 'fbb8fe3d7a56a5e12431ce2f9b2fab0d',
'info_dict': {
'id': '2015-0703-001',
'ext': 'mp4',
'title': '"This Flag Comes Down Today": Bree Newsome Scales SC Capitol Flagpole, Takes Down Confederate Flag',
'description': 'md5:4d2bc4f0d29f5553c2210a4bc7761a21',
},
}]
def _real_extract(self, url):
display_id = self._match_id(url)
webpage = self._download_webpage(url, display_id)
description = self._og_search_description(webpage)
json_data = self._parse_json(self._search_regex(
r'<script[^>]+type="text/json"[^>]*>\s*({[^>]+})', webpage, 'json'),
display_id)
video_id = None
formats = []
default_lang = 'en'
subtitles = {}
def add_subtitle_item(lang, info_dict):
if lang not in subtitles:
subtitles[lang] = []
subtitles[lang].append(info_dict)
# chapter_file are not subtitles
if 'caption_file' in json_data:
add_subtitle_item(default_lang, {
'url': compat_urlparse.urljoin(url, json_data['caption_file']),
})
for subtitle_item in json_data.get('captions', []):
lang = subtitle_item.get('language', '').lower() or default_lang
add_subtitle_item(lang, {
'url': compat_urlparse.urljoin(url, subtitle_item['url']),
})
for key in ('file', 'audio', 'video'):
media_url = json_data.get(key, '')
if not media_url:
continue
media_url = re.sub(r'\?.*', '', compat_urlparse.urljoin(url, media_url))
video_id = video_id or remove_start(os.path.splitext(url_basename(media_url))[0], 'dn')
formats.append({
'url': media_url,
})
self._sort_formats(formats)
return {
'id': video_id or display_id,
'title': json_data['title'],
'description': description,
'subtitles': subtitles,
'formats': formats,
}

View File

@@ -1,10 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import ( from ..utils import parse_duration
xpath_text,
parse_duration,
)
class DHMIE(InfoExtractor): class DHMIE(InfoExtractor):
@@ -34,24 +31,14 @@ class DHMIE(InfoExtractor):
}] }]
def _real_extract(self, url): def _real_extract(self, url):
video_id = self._match_id(url) playlist_id = self._match_id(url)
webpage = self._download_webpage(url, video_id) webpage = self._download_webpage(url, playlist_id)
playlist_url = self._search_regex( playlist_url = self._search_regex(
r"file\s*:\s*'([^']+)'", webpage, 'playlist url') r"file\s*:\s*'([^']+)'", webpage, 'playlist url')
playlist = self._download_xml(playlist_url, video_id) entries = self._extract_xspf_playlist(playlist_url, playlist_id)
track = playlist.find(
'./{http://xspf.org/ns/0/}trackList/{http://xspf.org/ns/0/}track')
video_url = xpath_text(
track, './{http://xspf.org/ns/0/}location',
'video url', fatal=True)
thumbnail = xpath_text(
track, './{http://xspf.org/ns/0/}image',
'thumbnail')
title = self._search_regex( title = self._search_regex(
[r'dc:title="([^"]+)"', r'<title> &raquo;([^<]+)</title>'], [r'dc:title="([^"]+)"', r'<title> &raquo;([^<]+)</title>'],
@@ -63,11 +50,10 @@ class DHMIE(InfoExtractor):
r'<em>Length\s*</em>\s*:\s*</strong>([^<]+)', r'<em>Length\s*</em>\s*:\s*</strong>([^<]+)',
webpage, 'duration', default=None)) webpage, 'duration', default=None))
return { entries[0].update({
'id': video_id,
'url': video_url,
'title': title, 'title': title,
'description': description, 'description': description,
'duration': duration, 'duration': duration,
'thumbnail': thumbnail, })
}
return self.playlist_result(entries, playlist_id)

View File

@@ -1,27 +0,0 @@
from __future__ import unicode_literals
from .novamov import NovaMovIE
class DivxStageIE(NovaMovIE):
IE_NAME = 'divxstage'
IE_DESC = 'DivxStage'
_VALID_URL = NovaMovIE._VALID_URL_TEMPLATE % {'host': 'divxstage\.(?:eu|net|ch|co|at|ag|to)'}
_HOST = 'www.divxstage.eu'
_FILE_DELETED_REGEX = r'>This file no longer exists on our servers.<'
_TITLE_REGEX = r'<div class="video_det">\s*<strong>([^<]+)</strong>'
_DESCRIPTION_REGEX = r'<div class="video_det">\s*<strong>[^<]+</strong>\s*<p>([^<]+)</p>'
_TEST = {
'url': 'http://www.divxstage.eu/video/57f238e2e5e01',
'md5': '63969f6eb26533a1968c4d325be63e72',
'info_dict': {
'id': '57f238e2e5e01',
'ext': 'flv',
'title': 'youtubedl test video',
'description': 'This is a test video for youtubedl.',
}
}

View File

@@ -0,0 +1,51 @@
# encoding: utf-8
from __future__ import unicode_literals
import time
from .common import InfoExtractor
from ..utils import int_or_none
class DPlayIE(InfoExtractor):
_VALID_URL = r'http://www\.dplay\.se/[^/]+/(?P<id>[^/?#]+)'
_TEST = {
'url': 'http://www.dplay.se/nugammalt-77-handelser-som-format-sverige/season-1-svensken-lar-sig-njuta-av-livet/',
'info_dict': {
'id': '3172',
'ext': 'mp4',
'display_id': 'season-1-svensken-lar-sig-njuta-av-livet',
'title': 'Svensken lär sig njuta av livet',
'duration': 2650,
},
}
def _real_extract(self, url):
display_id = self._match_id(url)
webpage = self._download_webpage(url, display_id)
video_id = self._search_regex(
r'data-video-id="(\d+)"', webpage, 'video id')
info = self._download_json(
'http://www.dplay.se/api/v2/ajax/videos?video_id=' + video_id,
video_id)['data'][0]
self._set_cookie(
'secure.dplay.se', 'dsc-geo',
'{"countryCode":"NL","expiry":%d}' % ((time.time() + 20 * 60) * 1000))
# TODO: consider adding support for 'stream_type=hds', it seems to
# require setting some cookies
manifest_url = self._download_json(
'https://secure.dplay.se/secure/api/v2/user/authorization/stream/%s?stream_type=hls' % video_id,
video_id, 'Getting manifest url for hls stream')['hls']
formats = self._extract_m3u8_formats(
manifest_url, video_id, ext='mp4', entry_protocol='m3u8_native')
return {
'id': video_id,
'display_id': display_id,
'title': info['title'],
'formats': formats,
'duration': int_or_none(info.get('video_metadata_length'), scale=1000),
}

View File

@@ -7,7 +7,6 @@ from .common import InfoExtractor
from ..compat import ( from ..compat import (
compat_HTTPError, compat_HTTPError,
compat_urllib_parse, compat_urllib_parse,
compat_urllib_request,
compat_urlparse, compat_urlparse,
) )
from ..utils import ( from ..utils import (
@@ -16,6 +15,7 @@ from ..utils import (
determine_ext, determine_ext,
int_or_none, int_or_none,
parse_iso8601, parse_iso8601,
sanitized_Request,
) )
@@ -51,7 +51,7 @@ class DramaFeverBaseIE(InfoExtractor):
'password': password, 'password': password,
} }
request = compat_urllib_request.Request( request = sanitized_Request(
self._LOGIN_URL, compat_urllib_parse.urlencode(login_form).encode('utf-8')) self._LOGIN_URL, compat_urllib_parse.urlencode(login_form).encode('utf-8'))
response = self._download_webpage( response = self._download_webpage(
request, None, 'Logging in as %s' % username) request, None, 'Logging in as %s' % username)

View File

@@ -2,15 +2,18 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import base64 import base64
import re
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import compat_urllib_request from ..utils import (
from ..utils import qualities qualities,
sanitized_Request,
)
class DumpertIE(InfoExtractor): class DumpertIE(InfoExtractor):
_VALID_URL = r'https?://(?:www\.)?dumpert\.nl/mediabase/(?P<id>[0-9]+/[0-9a-zA-Z]+)' _VALID_URL = r'(?P<protocol>https?)://(?:www\.)?dumpert\.nl/(?:mediabase|embed)/(?P<id>[0-9]+/[0-9a-zA-Z]+)'
_TEST = { _TESTS = [{
'url': 'http://www.dumpert.nl/mediabase/6646981/951bc60f/', 'url': 'http://www.dumpert.nl/mediabase/6646981/951bc60f/',
'md5': '1b9318d7d5054e7dcb9dc7654f21d643', 'md5': '1b9318d7d5054e7dcb9dc7654f21d643',
'info_dict': { 'info_dict': {
@@ -20,12 +23,18 @@ class DumpertIE(InfoExtractor):
'description': 'Niet schrikken hoor', 'description': 'Niet schrikken hoor',
'thumbnail': 're:^https?://.*\.jpg$', 'thumbnail': 're:^https?://.*\.jpg$',
} }
} }, {
'url': 'http://www.dumpert.nl/embed/6675421/dc440fe7/',
'only_matching': True,
}]
def _real_extract(self, url): def _real_extract(self, url):
video_id = self._match_id(url) mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id')
protocol = mobj.group('protocol')
req = compat_urllib_request.Request(url) url = '%s://www.dumpert.nl/mediabase/%s' % (protocol, video_id)
req = sanitized_Request(url)
req.add_header('Cookie', 'nsfw=1; cpc=10') req.add_header('Cookie', 'nsfw=1; cpc=10')
webpage = self._download_webpage(req, video_id) webpage = self._download_webpage(req, video_id)

View File

@@ -21,7 +21,7 @@ class EaglePlatformIE(InfoExtractor):
_TESTS = [{ _TESTS = [{
# http://lenta.ru/news/2015/03/06/navalny/ # http://lenta.ru/news/2015/03/06/navalny/
'url': 'http://lentaru.media.eagleplatform.com/index/player?player=new&record_id=227304&player_template_id=5201', 'url': 'http://lentaru.media.eagleplatform.com/index/player?player=new&record_id=227304&player_template_id=5201',
'md5': '0b7994faa2bd5c0f69a3db6db28d078d', 'md5': '70f5187fb620f2c1d503b3b22fd4efe3',
'info_dict': { 'info_dict': {
'id': '227304', 'id': '227304',
'ext': 'mp4', 'ext': 'mp4',
@@ -36,7 +36,7 @@ class EaglePlatformIE(InfoExtractor):
# http://muz-tv.ru/play/7129/ # http://muz-tv.ru/play/7129/
# http://media.clipyou.ru/index/player?record_id=12820&width=730&height=415&autoplay=true # http://media.clipyou.ru/index/player?record_id=12820&width=730&height=415&autoplay=true
'url': 'eagleplatform:media.clipyou.ru:12820', 'url': 'eagleplatform:media.clipyou.ru:12820',
'md5': '6c2ebeab03b739597ce8d86339d5a905', 'md5': '90b26344ba442c8e44aa4cf8f301164a',
'info_dict': { 'info_dict': {
'id': '12820', 'id': '12820',
'ext': 'mp4', 'ext': 'mp4',
@@ -48,7 +48,8 @@ class EaglePlatformIE(InfoExtractor):
'skip': 'Georestricted', 'skip': 'Georestricted',
}] }]
def _handle_error(self, response): @staticmethod
def _handle_error(response):
status = int_or_none(response.get('status', 200)) status = int_or_none(response.get('status', 200))
if status != 200: if status != 200:
raise ExtractorError(' '.join(response['errors']), expected=True) raise ExtractorError(' '.join(response['errors']), expected=True)
@@ -58,6 +59,9 @@ class EaglePlatformIE(InfoExtractor):
self._handle_error(response) self._handle_error(response)
return response return response
def _get_video_url(self, url_or_request, video_id, note='Downloading JSON metadata'):
return self._download_json(url_or_request, video_id, note)['data'][0]
def _real_extract(self, url): def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url) mobj = re.match(self._VALID_URL, url)
host, video_id = mobj.group('custom_host') or mobj.group('host'), mobj.group('id') host, video_id = mobj.group('custom_host') or mobj.group('host'), mobj.group('id')
@@ -69,7 +73,7 @@ class EaglePlatformIE(InfoExtractor):
title = media['title'] title = media['title']
description = media.get('description') description = media.get('description')
thumbnail = media.get('snapshot') thumbnail = self._proto_relative_url(media.get('snapshot'), 'http:')
duration = int_or_none(media.get('duration')) duration = int_or_none(media.get('duration'))
view_count = int_or_none(media.get('views')) view_count = int_or_none(media.get('views'))
@@ -78,13 +82,20 @@ class EaglePlatformIE(InfoExtractor):
if age_restriction: if age_restriction:
age_limit = 0 if age_restriction == 'allow_all' else 18 age_limit = 0 if age_restriction == 'allow_all' else 18
m3u8_data = self._download_json( secure_m3u8 = self._proto_relative_url(media['sources']['secure_m3u8']['auto'], 'http:')
media['sources']['secure_m3u8']['auto'],
video_id, 'Downloading m3u8 JSON')
m3u8_url = self._get_video_url(secure_m3u8, video_id, 'Downloading m3u8 JSON')
formats = self._extract_m3u8_formats( formats = self._extract_m3u8_formats(
m3u8_data['data'][0], video_id, m3u8_url, video_id,
'mp4', entry_protocol='m3u8_native') 'mp4', entry_protocol='m3u8_native', m3u8_id='hls')
mp4_url = self._get_video_url(
# Secure mp4 URL is constructed according to Player.prototype.mp4 from
# http://lentaru.media.eagleplatform.com/player/player.js
re.sub(r'm3u8|hlsvod|hls|f4m', 'mp4', secure_m3u8),
video_id, 'Downloading mp4 JSON')
formats.append({'url': mp4_url, 'format_id': 'mp4'})
self._sort_formats(formats) self._sort_formats(formats)
return { return {

View File

@@ -1,39 +1,92 @@
# encoding: utf-8 # encoding: utf-8
from __future__ import unicode_literals from __future__ import unicode_literals
import re
from .common import InfoExtractor from .common import InfoExtractor
from .brightcove import BrightcoveIE from ..utils import (
from ..utils import ExtractorError float_or_none,
int_or_none,
parse_iso8601,
sanitized_Request,
)
class EitbIE(InfoExtractor): class EitbIE(InfoExtractor):
IE_NAME = 'eitb.tv' IE_NAME = 'eitb.tv'
_VALID_URL = r'https?://www\.eitb\.tv/(eu/bideoa|es/video)/[^/]+/(?P<playlist_id>\d+)/(?P<chapter_id>\d+)' _VALID_URL = r'https?://(?:www\.)?eitb\.tv/(?:eu/bideoa|es/video)/[^/]+/\d+/(?P<id>\d+)'
_TEST = { _TEST = {
'add_ie': ['Brightcove'], 'url': 'http://www.eitb.tv/es/video/60-minutos-60-minutos-2013-2014/4104995148001/4090227752001/lasa-y-zabala-30-anos/',
'url': 'http://www.eitb.tv/es/video/60-minutos-60-minutos-2013-2014/2677100210001/2743577154001/lasa-y-zabala-30-anos/',
'md5': 'edf4436247185adee3ea18ce64c47998', 'md5': 'edf4436247185adee3ea18ce64c47998',
'info_dict': { 'info_dict': {
'id': '2743577154001', 'id': '4090227752001',
'ext': 'mp4', 'ext': 'mp4',
'title': '60 minutos (Lasa y Zabala, 30 años)', 'title': '60 minutos (Lasa y Zabala, 30 años)',
# All videos from eitb has this description in the brightcove info 'description': 'Programa de reportajes de actualidad.',
'description': '.', 'duration': 3996.76,
'uploader': 'Euskal Telebista', 'timestamp': 1381789200,
'upload_date': '20131014',
'tags': list,
}, },
} }
def _real_extract(self, url): def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url) video_id = self._match_id(url)
chapter_id = mobj.group('chapter_id')
webpage = self._download_webpage(url, chapter_id) video = self._download_json(
bc_url = BrightcoveIE._extract_brightcove_url(webpage) 'http://mam.eitb.eus/mam/REST/ServiceMultiweb/Video/MULTIWEBTV/%s/' % video_id,
if bc_url is None: video_id, 'Downloading video JSON')
raise ExtractorError('Could not extract the Brightcove url')
# The BrightcoveExperience object doesn't contain the video id, we set media = video['web_media'][0]
# it manually
bc_url += '&%40videoPlayer={0}'.format(chapter_id) formats = []
return self.url_result(bc_url, BrightcoveIE.ie_key()) for rendition in media['RENDITIONS']:
video_url = rendition.get('PMD_URL')
if not video_url:
continue
tbr = float_or_none(rendition.get('ENCODING_RATE'), 1000)
format_id = 'http'
if tbr:
format_id += '-%d' % int(tbr)
formats.append({
'url': rendition['PMD_URL'],
'format_id': format_id,
'width': int_or_none(rendition.get('FRAME_WIDTH')),
'height': int_or_none(rendition.get('FRAME_HEIGHT')),
'tbr': tbr,
})
hls_url = media.get('HLS_SURL')
if hls_url:
request = sanitized_Request(
'http://mam.eitb.eus/mam/REST/ServiceMultiweb/DomainRestrictedSecurity/TokenAuth/',
headers={'Referer': url})
token_data = self._download_json(
request, video_id, 'Downloading auth token', fatal=False)
if token_data:
token = token_data.get('token')
if token:
m3u8_formats = self._extract_m3u8_formats(
'%s?hdnts=%s' % (hls_url, token), video_id, m3u8_id='hls', fatal=False)
if m3u8_formats:
formats.extend(m3u8_formats)
hds_url = media.get('HDS_SURL')
if hds_url:
f4m_formats = self._extract_f4m_formats(
'%s?hdcore=3.7.0' % hds_url.replace('euskalsvod', 'euskalvod'),
video_id, f4m_id='hds', fatal=False)
if f4m_formats:
formats.extend(f4m_formats)
self._sort_formats(formats)
return {
'id': video_id,
'title': media.get('NAME_ES') or media.get('name') or media['NAME_EU'],
'description': media.get('SHORT_DESC_ES') or video.get('desc_group') or media.get('SHORT_DESC_EU'),
'thumbnail': media.get('STILL_URL') or media.get('THUMBNAIL_URL'),
'duration': float_or_none(media.get('LENGTH'), 1000),
'timestamp': parse_iso8601(media.get('BROADCST_DATE'), ' '),
'tags': media.get('TAGS'),
'formats': formats,
}

View File

@@ -10,7 +10,7 @@ from ..utils import (
class EngadgetIE(InfoExtractor): class EngadgetIE(InfoExtractor):
_VALID_URL = r'''(?x)https?://www.engadget.com/ _VALID_URL = r'''(?x)https?://www.engadget.com/
(?:video/5min/(?P<id>\d+)| (?:video(?:/5min)?/(?P<id>\d+)|
[\d/]+/.*?) [\d/]+/.*?)
''' '''

View File

@@ -71,8 +71,7 @@ class EroProfileIE(InfoExtractor):
m = re.search(r'You must be logged in to view this video\.', webpage) m = re.search(r'You must be logged in to view this video\.', webpage)
if m: if m:
raise ExtractorError( self.raise_login_required('This video requires login')
'This video requires login. Please specify a username and password and try again.', expected=True)
video_id = self._search_regex( video_id = self._search_regex(
[r"glbUpdViews\s*\('\d*','(\d+)'", r'p/report/video/(\d+)'], [r"glbUpdViews\s*\('\d*','(\d+)'", r'p/report/video/(\d+)'],

View File

@@ -3,13 +3,12 @@ from __future__ import unicode_literals
import json import json
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import compat_urllib_request
from ..utils import ( from ..utils import (
determine_ext, determine_ext,
clean_html, clean_html,
int_or_none, int_or_none,
float_or_none, float_or_none,
sanitized_Request,
) )
@@ -75,7 +74,7 @@ class EscapistIE(InfoExtractor):
video_id = ims_video['videoID'] video_id = ims_video['videoID']
key = ims_video['hash'] key = ims_video['hash']
config_req = compat_urllib_request.Request( config_req = sanitized_Request(
'http://www.escapistmagazine.com/videos/' 'http://www.escapistmagazine.com/videos/'
'vidconfig.php?videoID=%s&hash=%s' % (video_id, key)) 'vidconfig.php?videoID=%s&hash=%s' % (video_id, key))
config_req.add_header('Referer', url) config_req.add_header('Referer', url)

View File

@@ -0,0 +1,74 @@
# coding: utf-8
from __future__ import unicode_literals
import re
from .common import InfoExtractor
from ..compat import compat_urlparse
from ..utils import (
int_or_none,
parse_filesize,
unified_strdate,
)
class EsriVideoIE(InfoExtractor):
_VALID_URL = r'https?://video\.esri\.com/watch/(?P<id>[0-9]+)'
_TEST = {
'url': 'https://video.esri.com/watch/1124/arcgis-online-_dash_-developing-applications',
'md5': 'd4aaf1408b221f1b38227a9bbaeb95bc',
'info_dict': {
'id': '1124',
'ext': 'mp4',
'title': 'ArcGIS Online - Developing Applications',
'description': 'Jeremy Bartley demonstrates how to develop applications with ArcGIS Online.',
'thumbnail': 're:^https?://.*\.jpg$',
'duration': 185,
'upload_date': '20120419',
}
}
def _real_extract(self, url):
video_id = self._match_id(url)
webpage = self._download_webpage(url, video_id)
formats = []
for width, height, content in re.findall(
r'(?s)<li><strong>(\d+)x(\d+):</strong>(.+?)</li>', webpage):
for video_url, ext, filesize in re.findall(
r'<a[^>]+href="([^"]+)">([^<]+)&nbsp;\(([^<]+)\)</a>', content):
formats.append({
'url': compat_urlparse.urljoin(url, video_url),
'ext': ext.lower(),
'format_id': '%s-%s' % (ext.lower(), height),
'width': int(width),
'height': int(height),
'filesize_approx': parse_filesize(filesize),
})
self._sort_formats(formats)
title = self._html_search_meta('title', webpage, 'title')
description = self._html_search_meta(
'description', webpage, 'description', fatal=False)
thumbnail = self._html_search_meta('thumbnail', webpage, 'thumbnail', fatal=False)
if thumbnail:
thumbnail = re.sub(r'_[st]\.jpg$', '_x.jpg', thumbnail)
duration = int_or_none(self._search_regex(
[r'var\s+videoSeconds\s*=\s*(\d+)', r"'duration'\s*:\s*(\d+)"],
webpage, 'duration', fatal=False))
upload_date = unified_strdate(self._html_search_meta(
'last-modified', webpage, 'upload date', fatal=None))
return {
'id': video_id,
'title': title,
'description': description,
'thumbnail': thumbnail,
'duration': duration,
'upload_date': upload_date,
'formats': formats
}

View File

@@ -0,0 +1,93 @@
# coding: utf-8
from __future__ import unicode_literals
from .common import InfoExtractor
from ..compat import compat_urlparse
from ..utils import (
int_or_none,
orderedSet,
parse_duration,
qualities,
unified_strdate,
xpath_text
)
class EuropaIE(InfoExtractor):
_VALID_URL = r'https?://ec\.europa\.eu/avservices/(?:video/player|audio/audioDetails)\.cfm\?.*?\bref=(?P<id>[A-Za-z0-9-]+)'
_TESTS = [{
'url': 'http://ec.europa.eu/avservices/video/player.cfm?ref=I107758',
'md5': '574f080699ddd1e19a675b0ddf010371',
'info_dict': {
'id': 'I107758',
'ext': 'mp4',
'title': 'TRADE - Wikileaks on TTIP',
'description': 'NEW LIVE EC Midday press briefing of 11/08/2015',
'thumbnail': 're:^https?://.*\.jpg$',
'upload_date': '20150811',
'duration': 34,
'view_count': int,
'formats': 'mincount:3',
}
}, {
'url': 'http://ec.europa.eu/avservices/video/player.cfm?sitelang=en&ref=I107786',
'only_matching': True,
}, {
'url': 'http://ec.europa.eu/avservices/audio/audioDetails.cfm?ref=I-109295&sitelang=en',
'only_matching': True,
}]
def _real_extract(self, url):
video_id = self._match_id(url)
playlist = self._download_xml(
'http://ec.europa.eu/avservices/video/player/playlist.cfm?ID=%s' % video_id, video_id)
def get_item(type_, preference):
items = {}
for item in playlist.findall('./info/%s/item' % type_):
lang, label = xpath_text(item, 'lg', default=None), xpath_text(item, 'label', default=None)
if lang and label:
items[lang] = label.strip()
for p in preference:
if items.get(p):
return items[p]
query = compat_urlparse.parse_qs(compat_urlparse.urlparse(url).query)
preferred_lang = query.get('sitelang', ('en', ))[0]
preferred_langs = orderedSet((preferred_lang, 'en', 'int'))
title = get_item('title', preferred_langs) or video_id
description = get_item('description', preferred_langs)
thumbnmail = xpath_text(playlist, './info/thumburl', 'thumbnail')
upload_date = unified_strdate(xpath_text(playlist, './info/date', 'upload date'))
duration = parse_duration(xpath_text(playlist, './info/duration', 'duration'))
view_count = int_or_none(xpath_text(playlist, './info/views', 'views'))
language_preference = qualities(preferred_langs[::-1])
formats = []
for file_ in playlist.findall('./files/file'):
video_url = xpath_text(file_, './url')
if not video_url:
continue
lang = xpath_text(file_, './lg')
formats.append({
'url': video_url,
'format_id': lang,
'format_note': xpath_text(file_, './lglabel'),
'language_preference': language_preference(lang)
})
self._sort_formats(formats)
return {
'id': video_id,
'title': title,
'description': description,
'thumbnail': thumbnmail,
'upload_date': upload_date,
'duration': duration,
'view_count': view_count,
'formats': formats
}

View File

@@ -3,11 +3,9 @@ from __future__ import unicode_literals
import re import re
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import (
compat_urllib_request,
)
from ..utils import ( from ..utils import (
ExtractorError, ExtractorError,
sanitized_Request,
) )
@@ -42,7 +40,7 @@ class EveryonesMixtapeIE(InfoExtractor):
playlist_id = mobj.group('id') playlist_id = mobj.group('id')
pllist_url = 'http://everyonesmixtape.com/mixtape.php?a=getMixes&u=-1&linked=%s&explore=' % playlist_id pllist_url = 'http://everyonesmixtape.com/mixtape.php?a=getMixes&u=-1&linked=%s&explore=' % playlist_id
pllist_req = compat_urllib_request.Request(pllist_url) pllist_req = sanitized_Request(pllist_url)
pllist_req.add_header('X-Requested-With', 'XMLHttpRequest') pllist_req.add_header('X-Requested-With', 'XMLHttpRequest')
playlist_list = self._download_json( playlist_list = self._download_json(
@@ -55,7 +53,7 @@ class EveryonesMixtapeIE(InfoExtractor):
raise ExtractorError('Playlist id not found') raise ExtractorError('Playlist id not found')
pl_url = 'http://everyonesmixtape.com/mixtape.php?a=getMix&id=%s&userId=null&code=' % playlist_no pl_url = 'http://everyonesmixtape.com/mixtape.php?a=getMix&id=%s&userId=null&code=' % playlist_no
pl_req = compat_urllib_request.Request(pl_url) pl_req = sanitized_Request(pl_url)
pl_req.add_header('X-Requested-With', 'XMLHttpRequest') pl_req.add_header('X-Requested-With', 'XMLHttpRequest')
playlist = self._download_json( playlist = self._download_json(
pl_req, playlist_id, note='Downloading playlist info') pl_req, playlist_id, note='Downloading playlist info')

View File

@@ -33,20 +33,27 @@ class ExpoTVIE(InfoExtractor):
webpage = self._download_webpage(url, video_id) webpage = self._download_webpage(url, video_id)
player_key = self._search_regex( player_key = self._search_regex(
r'<param name="playerKey" value="([^"]+)"', webpage, 'player key') r'<param name="playerKey" value="([^"]+)"', webpage, 'player key')
config_url = 'http://client.expotv.com/video/config/%s/%s' % (
video_id, player_key)
config = self._download_json( config = self._download_json(
config_url, video_id, 'http://client.expotv.com/video/config/%s/%s' % (video_id, player_key),
note='Downloading video configuration') video_id, 'Downloading video configuration')
formats = [{ formats = []
'url': fcfg['file'], for fcfg in config['sources']:
'height': int_or_none(fcfg.get('height')), media_url = fcfg.get('file')
'format_note': fcfg.get('label'), if not media_url:
'ext': self._search_regex( continue
r'filename=.*\.([a-z0-9_A-Z]+)&', fcfg['file'], if fcfg.get('type') == 'm3u8':
'file extension', default=None), formats.extend(self._extract_m3u8_formats(
} for fcfg in config['sources']] media_url, video_id, 'mp4', entry_protocol='m3u8_native', m3u8_id='hls'))
else:
formats.append({
'url': media_url,
'height': int_or_none(fcfg.get('height')),
'format_id': fcfg.get('label'),
'ext': self._search_regex(
r'filename=.*\.([a-z0-9_A-Z]+)&', media_url,
'file extension', default=None) or fcfg.get('type'),
})
self._sort_formats(formats) self._sort_formats(formats)
title = self._og_search_title(webpage) title = self._og_search_title(webpage)

View File

@@ -3,23 +3,20 @@ from __future__ import unicode_literals
import re import re
from .common import InfoExtractor from .common import InfoExtractor
from ..compat import (
compat_parse_qs,
compat_urllib_request,
)
from ..utils import ( from ..utils import (
qualities, int_or_none,
sanitized_Request,
str_to_int, str_to_int,
) )
class ExtremeTubeIE(InfoExtractor): class ExtremeTubeIE(InfoExtractor):
_VALID_URL = r'https?://(?:www\.)?(?P<url>extremetube\.com/.*?video/.+?(?P<id>[0-9]+))(?:[/?&]|$)' _VALID_URL = r'https?://(?:www\.)?extremetube\.com/(?:[^/]+/)?video/(?P<id>[^/#?&]+)'
_TESTS = [{ _TESTS = [{
'url': 'http://www.extremetube.com/video/music-video-14-british-euro-brit-european-cumshots-swallow-652431', 'url': 'http://www.extremetube.com/video/music-video-14-british-euro-brit-european-cumshots-swallow-652431',
'md5': '344d0c6d50e2f16b06e49ca011d8ac69', 'md5': '344d0c6d50e2f16b06e49ca011d8ac69',
'info_dict': { 'info_dict': {
'id': '652431', 'id': 'music-video-14-british-euro-brit-european-cumshots-swallow-652431',
'ext': 'mp4', 'ext': 'mp4',
'title': 'Music Video 14 british euro brit european cumshots swallow', 'title': 'Music Video 14 british euro brit european cumshots swallow',
'uploader': 'unknown', 'uploader': 'unknown',
@@ -29,14 +26,18 @@ class ExtremeTubeIE(InfoExtractor):
}, { }, {
'url': 'http://www.extremetube.com/gay/video/abcde-1234', 'url': 'http://www.extremetube.com/gay/video/abcde-1234',
'only_matching': True, 'only_matching': True,
}, {
'url': 'http://www.extremetube.com/video/latina-slut-fucked-by-fat-black-dick',
'only_matching': True,
}, {
'url': 'http://www.extremetube.com/video/652431',
'only_matching': True,
}] }]
def _real_extract(self, url): def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url) video_id = self._match_id(url)
video_id = mobj.group('id')
url = 'http://www.' + mobj.group('url')
req = compat_urllib_request.Request(url) req = sanitized_Request(url)
req.add_header('Cookie', 'age_verified=1') req.add_header('Cookie', 'age_verified=1')
webpage = self._download_webpage(req, video_id) webpage = self._download_webpage(req, video_id)
@@ -49,20 +50,36 @@ class ExtremeTubeIE(InfoExtractor):
r'Views:\s*</strong>\s*<span>([\d,\.]+)</span>', r'Views:\s*</strong>\s*<span>([\d,\.]+)</span>',
webpage, 'view count', fatal=False)) webpage, 'view count', fatal=False))
flash_vars = compat_parse_qs(self._search_regex( flash_vars = self._parse_json(
r'<param[^>]+?name="flashvars"[^>]+?value="([^"]+)"', webpage, 'flash vars')) self._search_regex(
r'var\s+flashvars\s*=\s*({.+?});', webpage, 'flash vars'),
video_id)
formats = [] formats = []
quality = qualities(['180p', '240p', '360p', '480p', '720p', '1080p']) for quality_key, video_url in flash_vars.items():
for k, vals in flash_vars.items(): height = int_or_none(self._search_regex(
m = re.match(r'quality_(?P<quality>[0-9]+p)$', k) r'quality_(\d+)[pP]$', quality_key, 'height', default=None))
if m is not None: if not height:
formats.append({ continue
'format_id': m.group('quality'), f = {
'quality': quality(m.group('quality')), 'url': video_url,
'url': vals[0], }
mobj = re.search(
r'/(?P<height>\d{3,4})[pP]_(?P<bitrate>\d+)[kK]_\d+', video_url)
if mobj:
height = int(mobj.group('height'))
bitrate = int(mobj.group('bitrate'))
f.update({
'format_id': '%dp-%dk' % (height, bitrate),
'height': height,
'tbr': bitrate,
}) })
else:
f.update({
'format_id': '%dp' % height,
'height': height,
})
formats.append(f)
self._sort_formats(formats) self._sort_formats(formats)
return { return {

Some files were not shown because too many files have changed in this diff Show More