Compare commits
	
		
			201 Commits
		
	
	
		
			2017.10.15
			...
			2017.12.23
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 307a7588b0 | ||
|  | c2f2f8b120 | ||
|  | f5a6321107 | ||
|  | 69d69da98a | ||
|  | 5c5e60cff8 | ||
|  | 2132edaa03 | ||
|  | 4b7dd1705a | ||
|  | 9e3682d555 | ||
|  | 3e191da6d9 | ||
|  | 963d237d26 | ||
|  | d2d766bc6d | ||
|  | 17c3aced5d | ||
|  | 78466fcab5 | ||
|  | 3961c6cb9d | ||
|  | 07aeced68e | ||
|  | c10c93238e | ||
|  | 4a109f81bc | ||
|  | 99081da90c | ||
|  | 7e81010987 | ||
|  | 549bb416f5 | ||
|  | 25475dfab3 | ||
|  | 3dfa9ec213 | ||
|  | 06dbcd7be4 | ||
|  | b555ae9bf1 | ||
|  | c402e7f3a0 | ||
|  | 498a8a4ca5 | ||
|  | d05ba4b89e | ||
|  | 23f511f5c7 | ||
|  | 1c4804ef9b | ||
|  | 8ff2b16435 | ||
|  | c6a5a811a1 | ||
|  | 3fae11ac00 | ||
|  | 7974e289a1 | ||
|  | 6bf9c28b0a | ||
|  | bec49996c6 | ||
|  | c8be7d5f74 | ||
|  | 15960255fe | ||
|  | 6b2d8c9182 | ||
|  | e6b8803d59 | ||
|  | cb0c2310fb | ||
|  | 23b6e23002 | ||
|  | 127e98d31d | ||
|  | e4f201bc1b | ||
|  | 08d77a95c9 | ||
|  | 5868079e99 | ||
|  | b6f78d76c1 | ||
|  | 1fa0dce2c0 | ||
|  | fa1dd6d2cd | ||
|  | c38970ca10 | ||
|  | 51f2863357 | ||
|  | 913b61eeee | ||
|  | 6f1ec339a0 | ||
|  | a3de5e6c0e | ||
|  | f4cc03d60b | ||
|  | 2a57b62b80 | ||
|  | e2707a832c | ||
|  | 1115271ac6 | ||
|  | d21d0ba6c1 | ||
|  | a670b1ba26 | ||
|  | 1bd4fc96e6 | ||
|  | 684ae10236 | ||
|  | 3c4fbfeca2 | ||
|  | b271e33526 | ||
|  | d3f8b76b69 | ||
|  | 91328f26b0 | ||
|  | 61d18c8a4b | ||
|  | c94427dd60 | ||
|  | d4f05d4731 | ||
|  | d7df308981 | ||
|  | 0d56eddc59 | ||
|  | e25ee72657 | ||
|  | 78593e294c | ||
|  | 593f2f7989 | ||
|  | 603fc4e0ea | ||
|  | 41bf647e89 | ||
|  | fea92aa65d | ||
|  | 0981585bef | ||
|  | f5ac68d88f | ||
|  | 1663b32946 | ||
|  | 5ea765fb72 | ||
|  | fb61b57d0f | ||
|  | 07cf18b9c5 | ||
|  | 5f699251e9 | ||
|  | a3474aa59e | ||
|  | 115afb77ec | ||
|  | 53f024e7c5 | ||
|  | ffe6979ef9 | ||
|  | dafb4c6647 | ||
|  | 82a62de192 | ||
|  | f58a506044 | ||
|  | 5ddeb7702a | ||
|  | 6c07f0b288 | ||
|  | e94d1adc36 | ||
|  | d08dcd2dbd | ||
|  | 7512aa986f | ||
|  | 93f3f10cdc | ||
|  | 87dac57cf6 | ||
|  | b485d5d6bf | ||
|  | a238a868ba | ||
|  | c0f647a179 | ||
|  | 6ff27b8d5a | ||
|  | 9ef909f2b2 | ||
|  | 8cfbcfab9a | ||
|  | b7785cf156 | ||
|  | 9105523818 | ||
|  | dbb25af657 | ||
|  | fe4bfe36e1 | ||
|  | 6f5c598a28 | ||
|  | cd9ff4ec5b | ||
|  | c6c6a64aa5 | ||
|  | e0a8686f48 | ||
|  | 6049176471 | ||
|  | 805f5bf759 | ||
|  | 32ad4f3faf | ||
|  | 6899b1d9e8 | ||
|  | 939be9adfe | ||
|  | 2688664762 | ||
|  | 8f63941104 | ||
|  | a9efdf3d4a | ||
|  | f610dbb05f | ||
|  | 38db52adf3 | ||
|  | 3192d4bc7a | ||
|  | 9cbd4dda10 | ||
|  | 08e45b39e7 | ||
|  | fae0eb42ec | ||
|  | ea2295842f | ||
|  | a2b6aba8de | ||
|  | ff31f2d5c3 | ||
|  | 0987f2ddb2 | ||
|  | 5871ebac47 | ||
|  | 05dee6c520 | ||
|  | 27adc9ec65 | ||
|  | 388beb86e0 | ||
|  | d4e31b72b9 | ||
|  | 5fc12b9549 | ||
|  | af85ce29c6 | ||
|  | e4d9586562 | ||
|  | 79d1f8ed68 | ||
|  | a5203935d6 | ||
|  | 59d2e6d04f | ||
|  | a9543e37c8 | ||
|  | 61fb07e156 | ||
|  | 4222346fb2 | ||
|  | cc6a960e13 | ||
|  | f34b841b51 | ||
|  | e0998333fa | ||
|  | 909191de91 | ||
|  | 477c97f86b | ||
|  | 6e71bbf4ab | ||
|  | 181e381fda | ||
|  | 187ee66c94 | ||
|  | 48107c198b | ||
|  | cd670befc4 | ||
|  | 44cca168cc | ||
|  | b0f4331002 | ||
|  | 044eeb1455 | ||
|  | 8fe767e072 | ||
|  | 6d0630d880 | ||
|  | 518d357b46 | ||
|  | 514e8aefd4 | ||
|  | 9211e3319e | ||
|  | 056653bbb1 | ||
|  | c3206d02e9 | ||
|  | eb4b5818e2 | ||
|  | 47a8587915 | ||
|  | 8e01f3ca81 | ||
|  | f2332f18e6 | ||
|  | 7c1f419341 | ||
|  | 30e6161799 | ||
|  | dc24a7d4a2 | ||
|  | d673ab6562 | ||
|  | b8c6ffc518 | ||
|  | 7913e0fca7 | ||
|  | cdd1ce92c4 | ||
|  | 55c727a547 | ||
|  | 36e2d3ca43 | ||
|  | f7a5038305 | ||
|  | 9ff6273cae | ||
|  | f03ee0b372 | ||
|  | cf6bda312b | ||
|  | 3ebbd9991e | ||
|  | 21ce434051 | ||
|  | 5c0e5bc4df | ||
|  | 9a9de2d7b2 | ||
|  | 424505df76 | ||
|  | fa3f0fd856 | ||
|  | c9dcd4b0c5 | ||
|  | fc5c47d13c | ||
|  | a26a3c6d34 | ||
|  | 382fa456ea | ||
|  | e1d168e592 | ||
|  | ca1c9f26fa | ||
|  | 6f3b4a98c9 | ||
|  | fa4bc6e712 | ||
|  | 6b9cbd023f | ||
|  | c233003afe | ||
|  | 83fcf19e2d | ||
|  | acc4ea6237 | ||
|  | 8cc1840ccb | ||
|  | a9ee4f6e49 | ||
|  | aaab8c5e71 | 
							
								
								
									
										6
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							| @@ -6,8 +6,8 @@ | ||||
|  | ||||
| --- | ||||
|  | ||||
| ### Make sure you are using the *latest* version: run `youtube-dl --version` and ensure your version is *2017.10.15.1*. If it's not, read [this FAQ entry](https://github.com/rg3/youtube-dl/blob/master/README.md#how-do-i-update-youtube-dl) and update. Issues with outdated version will be rejected. | ||||
| - [ ] I've **verified** and **I assure** that I'm running youtube-dl **2017.10.15.1** | ||||
| ### Make sure you are using the *latest* version: run `youtube-dl --version` and ensure your version is *2017.12.23*. If it's not, read [this FAQ entry](https://github.com/rg3/youtube-dl/blob/master/README.md#how-do-i-update-youtube-dl) and update. Issues with outdated version will be rejected. | ||||
| - [ ] I've **verified** and **I assure** that I'm running youtube-dl **2017.12.23** | ||||
|  | ||||
| ### Before submitting an *issue* make sure you have: | ||||
| - [ ] At least skimmed through the [README](https://github.com/rg3/youtube-dl/blob/master/README.md), **most notably** the [FAQ](https://github.com/rg3/youtube-dl#faq) and [BUGS](https://github.com/rg3/youtube-dl#bugs) sections | ||||
| @@ -35,7 +35,7 @@ Add the `-v` flag to **your command line** you run youtube-dl with (`youtube-dl | ||||
| [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 2017.10.15.1 | ||||
| [debug] youtube-dl version 2017.12.23 | ||||
| [debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2 | ||||
| [debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4 | ||||
| [debug] Proxy map: {} | ||||
|   | ||||
| @@ -11,12 +11,12 @@ sudo: false | ||||
| env: | ||||
|   - YTDL_TEST_SET=core | ||||
|   - YTDL_TEST_SET=download | ||||
| matrix: | ||||
|   fast_finish: true | ||||
|   allow_failures: | ||||
|     - env: YTDL_TEST_SET=download | ||||
| script: ./devscripts/run_tests.sh | ||||
| notifications: | ||||
|   email: | ||||
|     - filippo.valsorda@gmail.com | ||||
|     - yasoob.khld@gmail.com | ||||
| #  irc: | ||||
| #    channels: | ||||
| #      - "irc.freenode.org#youtube-dl" | ||||
| #    skip_join: true | ||||
|   | ||||
							
								
								
									
										215
									
								
								ChangeLog
									
									
									
									
									
								
							
							
						
						
									
										215
									
								
								ChangeLog
									
									
									
									
									
								
							| @@ -1,3 +1,216 @@ | ||||
| version 2017.12.23 | ||||
|  | ||||
| Core | ||||
| * [extractor/common] Move X-Forwarded-For setup code into _request_webpage | ||||
| + [YoutubeDL] Add support for playlist_uploader and playlist_uploader_id in | ||||
|   output template (#11427, #15018) | ||||
| + [extractor/common] Introduce uploader, uploader_id and uploader_url | ||||
|   meta fields for playlists (#11427, #15018) | ||||
| * [downloader/fragment] Encode filename of fragment being removed (#15020) | ||||
| + [utils] Add another date format pattern (#14999) | ||||
|  | ||||
| Extractors | ||||
| + [kaltura] Add another embed pattern for entry_id | ||||
| + [7plus] Add support for 7plus.com.au (#15043) | ||||
| * [animeondemand] Relax login error regular expression | ||||
| + [shahid] Add support for show pages (#7401) | ||||
| + [youtube] Extract uploader, uploader_id and uploader_url for playlists | ||||
|   (#11427, #15018) | ||||
| * [afreecatv] Improve format extraction (#15019) | ||||
| + [cspan] Add support for audio only pages and catch page errors (#14995) | ||||
| + [mailru] Add support for embed URLs (#14904) | ||||
| * [crunchyroll] Future-proof XML element checks (#15013) | ||||
| * [cbslocal] Fix timestamp extraction (#14999, #15000) | ||||
| * [discoverygo] Correct TTML subtitle extension | ||||
| * [vk] Make view count optional (#14979) | ||||
| * [disney] Skip Apple FairPlay formats (#14982) | ||||
| * [voot] Fix format extraction (#14758) | ||||
|  | ||||
|  | ||||
| version 2017.12.14 | ||||
|  | ||||
| Core | ||||
| * [postprocessor/xattr] Clarify NO_SPACE message (#14970) | ||||
| * [downloader/http] Return actual download result from real_download (#14971) | ||||
|  | ||||
| Extractors | ||||
| + [itv] Extract more subtitles and duration | ||||
| * [itv] Improve extraction (#14944) | ||||
| + [byutv] Add support for geo restricted videos | ||||
| * [byutv] Fix extraction (#14966, #14967) | ||||
| + [bbccouk] Fix extraction for 320k HLS streams | ||||
| + [toutv] Add support for special video URLs (#14179) | ||||
| * [discovery] Fix free videos extraction (#14157, #14954) | ||||
| * [tvnow] Fix extraction (#7831) | ||||
| + [nickelodeon:br] Add support for nickelodeon brazil websites (#14893) | ||||
| * [nick] Improve extraction (#14876) | ||||
| * [tbs] Fix extraction (#13658) | ||||
|  | ||||
|  | ||||
| version 2017.12.10 | ||||
|  | ||||
| Core | ||||
| + [utils] Add sami mimetype to mimetype2ext | ||||
|  | ||||
| Extractors | ||||
| * [culturebox] Improve video id extraction (#14947) | ||||
| * [twitter] Improve extraction (#14197) | ||||
| + [udemy] Extract more HLS formats | ||||
| * [udemy] Improve course id extraction (#14938) | ||||
| + [stretchinternet] Add support for portal.stretchinternet.com (#14576) | ||||
| * [ellentube] Fix extraction (#14407, #14570) | ||||
| + [raiplay:playlist] Add support for playlists (#14563) | ||||
| * [sonyliv] Bypass geo restriction | ||||
| * [sonyliv] Extract higher quality formats (#14922) | ||||
| * [fox] Extract subtitles | ||||
| + [fox] Add support for Adobe Pass authentication (#14205, #14489) | ||||
| - [dailymotion:cloud] Remove extractor (#6794) | ||||
| * [xhamster] Fix thumbnail extraction (#14780) | ||||
| + [xhamster] Add support for mobile URLs (#14780) | ||||
| * [generic] Don't pass video id as mpd id while extracting DASH (#14902) | ||||
| * [ard] Skip invalid stream URLs (#14906) | ||||
| * [porncom] Fix metadata extraction (#14911) | ||||
| * [pluralsight] Detect agreement request (#14913) | ||||
| * [toutv] Fix login (#14614) | ||||
|  | ||||
|  | ||||
| version 2017.12.02 | ||||
|  | ||||
| Core | ||||
| + [downloader/fragment] Commit part file after each fragment | ||||
| + [extractor/common] Add durations for DASH fragments with bare SegmentURLs | ||||
| + [extractor/common] Add support for DASH manifests with SegmentLists with | ||||
|   bare SegmentURLs (#14844) | ||||
| + [utils] Add hvc1 codec code to parse_codecs | ||||
|  | ||||
| Extractors | ||||
| * [xhamster] Fix extraction (#14884) | ||||
| * [youku] Update ccode (#14872) | ||||
| * [mnet] Fix format extraction (#14883) | ||||
| + [xiami] Add Referer header to API request | ||||
| * [mtv] Correct scc extention in extracted subtitles (#13730) | ||||
| * [vvvvid] Fix extraction for kenc videos (#13406) | ||||
| + [br] Add support for BR Mediathek videos (#14560, #14788) | ||||
| + [daisuki] Add support for motto.daisuki.com (#14681) | ||||
| * [odnoklassniki] Fix API metadata request (#14862) | ||||
| * [itv] Fix HLS formats extraction | ||||
| + [pbs] Add another media id regular expression | ||||
|  | ||||
|  | ||||
| version 2017.11.26 | ||||
|  | ||||
| Core | ||||
| * [extractor/common] Use final URL when dumping request (#14769) | ||||
|  | ||||
| Extractors | ||||
| * [fczenit] Fix extraction | ||||
| - [firstpost] Remove extractor | ||||
| * [freespeech] Fix extraction | ||||
| * [nexx] Extract more formats | ||||
| + [openload] Add support for openload.link (#14763) | ||||
| * [empflix] Relax URL regular expression | ||||
| * [empflix] Fix extractrion | ||||
| * [tnaflix] Don't modify download URLs (#14811) | ||||
| - [gamersyde] Remove extractor | ||||
| * [francetv:generationwhat] Fix extraction | ||||
| + [massengeschmacktv] Add support for Massengeschmack TV | ||||
| * [fox9] Fix extraction | ||||
| * [faz] Fix extraction and add support for Perform Group embeds (#14714) | ||||
| + [performgroup] Add support for performgroup.com | ||||
| + [jwplatform] Add support for iframes (#14828) | ||||
| * [culturebox] Fix extraction (#14827) | ||||
| * [youku] Fix extraction; update ccode (#14815) | ||||
| * [livestream] Make SMIL extraction non fatal (#14792) | ||||
| + [drtuber] Add support for mobile URLs (#14772) | ||||
| + [spankbang] Add support for mobile URLs (#14771) | ||||
| * [instagram] Fix description, timestamp and counters extraction (#14755) | ||||
|  | ||||
|  | ||||
| version 2017.11.15 | ||||
|  | ||||
| Core | ||||
| * [common] Skip Apple FairPlay m3u8 manifests (#14741) | ||||
| * [YoutubeDL] Fix playlist range optimization for --playlist-items (#14740) | ||||
|  | ||||
| Extractors | ||||
| * [vshare] Capture and output error message | ||||
| * [vshare] Fix extraction (#14473) | ||||
| * [crunchyroll] Extract old RTMP formats | ||||
| * [tva] Fix extraction (#14736) | ||||
| * [gamespot] Lower preference of HTTP formats (#14652) | ||||
| * [instagram:user] Fix extraction (#14699) | ||||
| * [ccma] Fix typo (#14730) | ||||
| - Remove sensitive data from logging in messages | ||||
| * [instagram:user] Fix extraction (#14699) | ||||
| + [gamespot] Add support for article URLs (#14652) | ||||
| * [gamespot] Skip Brightcove Once HTTP formats (#14652) | ||||
| * [cartoonnetwork] Update tokenizer_src (#14666) | ||||
| + [wsj] Recognize another URL pattern (#14704) | ||||
| * [pandatv] Update API URL and sign format URLs (#14693) | ||||
| * [crunchyroll] Use old login method (#11572) | ||||
|  | ||||
|  | ||||
| version 2017.11.06 | ||||
|  | ||||
| Core | ||||
| + [extractor/common] Add protocol for f4m formats | ||||
| * [f4m] Prefer baseURL for relative URLs (#14660) | ||||
| * [extractor/common] Respect URL query in _extract_wowza_formats (14645) | ||||
|  | ||||
| Extractors | ||||
| + [hotstar:playlist] Add support for playlists (#12465) | ||||
| * [hotstar] Bypass geo restriction (#14672) | ||||
| - [22tracks] Remove extractor (#11024, #14628) | ||||
| + [skysport] Sdd support ooyala videos protected with embed_token (#14641) | ||||
| * [gamespot] Extract formats referenced with new data fields (#14652) | ||||
| * [spankbang] Detect unavailable videos (#14644) | ||||
|  | ||||
|  | ||||
| version 2017.10.29 | ||||
|  | ||||
| Core | ||||
| * [extractor/common] Prefix format id for audio only HLS formats | ||||
| + [utils] Add support for zero years and months in parse_duration | ||||
|  | ||||
| Extractors | ||||
| * [egghead] Fix extraction (#14388) | ||||
| + [fxnetworks] Extract series metadata (#14603) | ||||
| + [younow] Add support for younow.com (#9255, #9432, #12436) | ||||
| * [dctptv] Fix extraction (#14599) | ||||
| * [youtube] Restrict embed regular expression (#14600) | ||||
| * [vimeo] Restrict iframe embed regular expression (#14600) | ||||
| * [soundgasm] Improve extraction (#14588) | ||||
| - [myvideo] Remove extractor (#8557) | ||||
| + [nbc] Add support for classic-tv videos (#14575) | ||||
| + [vrtnu] Add support for cookies authentication and simplify (#11873) | ||||
| + [canvas] Add support for vrt.be/vrtnu (#11873) | ||||
| * [twitch:clips] Fix title extraction (#14566) | ||||
| + [ndtv] Add support for sub-sites (#14534) | ||||
| * [dramafever] Fix login error message extraction | ||||
| + [nick] Add support for more nickelodeon sites (no, dk, se, ch, fr, es, pt, | ||||
|   ro, hu) (#14553) | ||||
|  | ||||
|  | ||||
| version 2017.10.20 | ||||
|  | ||||
| Core | ||||
| * [downloader/fragment] Report warning instead of error on inconsistent | ||||
|   download state | ||||
| * [downloader/hls] Fix total fragments count when ad fragments exist | ||||
|  | ||||
| Extractors | ||||
| * [parliamentliveuk] Fix extraction (#14524) | ||||
| * [soundcloud] Update client id (#14546) | ||||
| + [servus] Add support for servus.com (#14362) | ||||
| + [unity] Add support for unity3d.com (#14528) | ||||
| * [youtube] Replace youtube redirect URLs in description (#14517) | ||||
| * [pbs] Restrict direct video URL regular expression (#14519) | ||||
| * [drtv] Respect preference for direct HTTP formats (#14509) | ||||
| + [eporner] Add support for embed URLs (#14507) | ||||
| * [arte] Capture and output error message | ||||
| * [niconico] Improve uploader metadata extraction robustness (#14135) | ||||
|  | ||||
|  | ||||
| version 2017.10.15.1 | ||||
|  | ||||
| Core | ||||
| @@ -834,7 +1047,7 @@ version 2017.04.14 | ||||
|  | ||||
| Core | ||||
| + [downloader/hls] Add basic support for EXT-X-BYTERANGE tag (#10955) | ||||
| + [adobepass] Improve Comcast and Verison login code (#10803) | ||||
| + [adobepass] Improve Comcast and Verizon login code (#10803) | ||||
| + [adobepass] Add support for Verizon (#10803) | ||||
|  | ||||
| Extractors | ||||
|   | ||||
| @@ -1,7 +1,9 @@ | ||||
| include README.md | ||||
| include test/*.py | ||||
| include test/*.json | ||||
| include LICENSE | ||||
| include AUTHORS | ||||
| include ChangeLog | ||||
| include youtube-dl.bash-completion | ||||
| include youtube-dl.fish | ||||
| include youtube-dl.1 | ||||
| recursive-include docs Makefile conf.py *.rst | ||||
| recursive-include test * | ||||
|   | ||||
							
								
								
									
										18
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								Makefile
									
									
									
									
									
								
							| @@ -36,8 +36,17 @@ test: | ||||
|  | ||||
| ot: offlinetest | ||||
|  | ||||
| # Keep this list in sync with devscripts/run_tests.sh | ||||
| offlinetest: codetest | ||||
| 	$(PYTHON) -m nose --verbose test --exclude test_download.py --exclude test_age_restriction.py --exclude test_subtitles.py --exclude test_write_annotations.py --exclude test_youtube_lists.py --exclude test_iqiyi_sdk_interpreter.py --exclude test_socks.py | ||||
| 	$(PYTHON) -m nose --verbose test \ | ||||
| 		--exclude test_age_restriction.py \ | ||||
| 		--exclude test_download.py \ | ||||
| 		--exclude test_iqiyi_sdk_interpreter.py \ | ||||
| 		--exclude test_socks.py \ | ||||
| 		--exclude test_subtitles.py \ | ||||
| 		--exclude test_write_annotations.py \ | ||||
| 		--exclude test_youtube_lists.py \ | ||||
| 		--exclude test_youtube_signature.py | ||||
|  | ||||
| tar: youtube-dl.tar.gz | ||||
|  | ||||
| @@ -101,7 +110,7 @@ _EXTRACTOR_FILES = $(shell find youtube_dl/extractor -iname '*.py' -and -not -in | ||||
| youtube_dl/extractor/lazy_extractors.py: devscripts/make_lazy_extractors.py devscripts/lazy_load_template.py $(_EXTRACTOR_FILES) | ||||
| 	$(PYTHON) devscripts/make_lazy_extractors.py $@ | ||||
|  | ||||
| youtube-dl.tar.gz: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-completion youtube-dl.zsh youtube-dl.fish ChangeLog | ||||
| youtube-dl.tar.gz: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-completion youtube-dl.zsh youtube-dl.fish ChangeLog AUTHORS | ||||
| 	@tar -czf youtube-dl.tar.gz --transform "s|^|youtube-dl/|" --owner 0 --group 0 \ | ||||
| 		--exclude '*.DS_Store' \ | ||||
| 		--exclude '*.kate-swp' \ | ||||
| @@ -110,11 +119,10 @@ youtube-dl.tar.gz: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash- | ||||
| 		--exclude '*~' \ | ||||
| 		--exclude '__pycache__' \ | ||||
| 		--exclude '.git' \ | ||||
| 		--exclude 'testdata' \ | ||||
| 		--exclude 'docs/_build' \ | ||||
| 		-- \ | ||||
| 		bin devscripts test youtube_dl docs \ | ||||
| 		ChangeLog LICENSE README.md README.txt \ | ||||
| 		ChangeLog AUTHORS LICENSE README.md README.txt \ | ||||
| 		Makefile MANIFEST.in youtube-dl.1 youtube-dl.bash-completion \ | ||||
| 		youtube-dl.zsh youtube-dl.fish setup.py \ | ||||
| 		youtube-dl.zsh youtube-dl.fish setup.py setup.cfg \ | ||||
| 		youtube-dl | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| [](https://travis-ci.org/rg3/youtube-dl) | ||||
|  | ||||
| youtube-dl - download videos from youtube.com or other video platforms | ||||
|  | ||||
| - [INSTALLATION](#installation) | ||||
| @@ -509,6 +511,9 @@ The basic usage is not to set any template arguments when downloading a single f | ||||
|  - `average_rating` (numeric): Average rating give by users, the scale used depends on the webpage | ||||
|  - `comment_count` (numeric): Number of comments on the video | ||||
|  - `age_limit` (numeric): Age restriction for the video (years) | ||||
|  - `is_live` (boolean): Whether this video is a live stream or a fixed-length video | ||||
|  - `start_time` (numeric): Time in seconds where the reproduction should start, as specified in the URL | ||||
|  - `end_time` (numeric): Time in seconds where the reproduction should end, as specified in the URL | ||||
|  - `format` (string): A human-readable description of the format  | ||||
|  - `format_id` (string): Format code specified by `--format` | ||||
|  - `format_note` (string): Additional info about the format | ||||
| @@ -534,6 +539,8 @@ The basic usage is not to set any template arguments when downloading a single f | ||||
|  - `playlist_index` (numeric): Index of the video in the playlist padded with leading zeros according to the total length of the playlist | ||||
|  - `playlist_id` (string): Playlist identifier | ||||
|  - `playlist_title` (string): Playlist title | ||||
|  - `playlist_uploader` (string): Full name of the playlist uploader | ||||
|  - `playlist_uploader_id` (string): Nickname or id of the playlist uploader | ||||
|  | ||||
| Available for the video that belongs to some logical chapter or section: | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| DOWNLOAD_TESTS="age_restriction|download|subtitles|write_annotations|iqiyi_sdk_interpreter|youtube_lists" | ||||
| # Keep this list in sync with the `offlinetest` target in Makefile | ||||
| DOWNLOAD_TESTS="age_restriction|download|iqiyi_sdk_interpreter|socks|subtitles|write_annotations|youtube_lists|youtube_signature" | ||||
|  | ||||
| test_set="" | ||||
| multiprocess_args="" | ||||
|   | ||||
| @@ -3,8 +3,6 @@ | ||||
|  - **1up.com** | ||||
|  - **20min** | ||||
|  - **220.ro** | ||||
|  - **22tracks:genre** | ||||
|  - **22tracks:track** | ||||
|  - **24video** | ||||
|  - **3qsdn**: 3Q SDN | ||||
|  - **3sat** | ||||
| @@ -12,6 +10,7 @@ | ||||
|  - **56.com** | ||||
|  - **5min** | ||||
|  - **6play** | ||||
|  - **7plus** | ||||
|  - **8tracks** | ||||
|  - **91porn** | ||||
|  - **9c9media** | ||||
| @@ -114,16 +113,16 @@ | ||||
|  - **BokeCC** | ||||
|  - **BostonGlobe** | ||||
|  - **Bpb**: Bundeszentrale für politische Bildung | ||||
|  - **BR**: Bayerischer Rundfunk Mediathek | ||||
|  - **BR**: Bayerischer Rundfunk | ||||
|  - **BravoTV** | ||||
|  - **Break** | ||||
|  - **brightcove:legacy** | ||||
|  - **brightcove:new** | ||||
|  - **BRMediathek**: Bayerischer Rundfunk Mediathek | ||||
|  - **bt:article**: Bergens Tidende Articles | ||||
|  - **bt:vestlendingen**: Bergens Tidende - Vestlendingen | ||||
|  - **BuzzFeed** | ||||
|  - **BYUtv** | ||||
|  - **BYUtvEvent** | ||||
|  - **Camdemy** | ||||
|  - **CamdemyFolder** | ||||
|  - **CamWithHer** | ||||
| @@ -199,9 +198,8 @@ | ||||
|  - **dailymotion** | ||||
|  - **dailymotion:playlist** | ||||
|  - **dailymotion:user** | ||||
|  - **DailymotionCloud** | ||||
|  - **Daisuki** | ||||
|  - **DaisukiPlaylist** | ||||
|  - **DaisukiMotto** | ||||
|  - **DaisukiMottoPlaylist** | ||||
|  - **daum.net** | ||||
|  - **daum.net:clip** | ||||
|  - **daum.net:playlist** | ||||
| @@ -244,8 +242,9 @@ | ||||
|  - **eHow** | ||||
|  - **Einthusan** | ||||
|  - **eitb.tv** | ||||
|  - **EllenTV** | ||||
|  - **EllenTV:clips** | ||||
|  - **EllenTube** | ||||
|  - **EllenTubePlaylist** | ||||
|  - **EllenTubeVideo** | ||||
|  - **ElPais**: El País | ||||
|  - **Embedly** | ||||
|  - **EMPFlix** | ||||
| @@ -268,10 +267,8 @@ | ||||
|  - **fc2** | ||||
|  - **fc2:embed** | ||||
|  - **Fczenit** | ||||
|  - **fernsehkritik.tv** | ||||
|  - **filmon** | ||||
|  - **filmon:channel** | ||||
|  - **Firstpost** | ||||
|  - **FiveTV** | ||||
|  - **Flickr** | ||||
|  - **Flipagram** | ||||
| @@ -285,7 +282,7 @@ | ||||
|  - **foxnews:article** | ||||
|  - **foxnews:insider** | ||||
|  - **FoxSports** | ||||
|  - **france2.fr:generation-quoi** | ||||
|  - **france2.fr:generation-what** | ||||
|  - **FranceCulture** | ||||
|  - **FranceInter** | ||||
|  - **FranceTV** | ||||
| @@ -303,7 +300,6 @@ | ||||
|  - **GameInformer** | ||||
|  - **GameOne** | ||||
|  - **gameone:playlist** | ||||
|  - **Gamersyde** | ||||
|  - **GameSpot** | ||||
|  - **GameStar** | ||||
|  - **Gaskrank** | ||||
| @@ -342,6 +338,7 @@ | ||||
|  - **HornBunny** | ||||
|  - **HotNewHipHop** | ||||
|  - **HotStar** | ||||
|  - **hotstar:playlist** | ||||
|  - **Howcast** | ||||
|  - **HowStuffWorks** | ||||
|  - **HRTi** | ||||
| @@ -442,6 +439,7 @@ | ||||
|  - **mangomolo:live** | ||||
|  - **mangomolo:video** | ||||
|  - **ManyVids** | ||||
|  - **massengeschmack.tv** | ||||
|  - **MatchTV** | ||||
|  - **MDR**: MDR.DE and KiKA | ||||
|  - **media.ccc.de** | ||||
| @@ -498,7 +496,6 @@ | ||||
|  - **MySpace:album** | ||||
|  - **MySpass** | ||||
|  - **Myvi** | ||||
|  - **myvideo** (Currently broken) | ||||
|  - **MyVidster** | ||||
|  - **n-tv.de** | ||||
|  - **natgeo** | ||||
| @@ -541,6 +538,7 @@ | ||||
|  - **nhl.com:videocenter:category**: NHL videocenter category | ||||
|  - **nick.com** | ||||
|  - **nick.de** | ||||
|  - **nickelodeon:br** | ||||
|  - **nickelodeonru** | ||||
|  - **nicknight** | ||||
|  - **niconico**: ニコニコ動画 | ||||
| @@ -559,8 +557,6 @@ | ||||
|  - **nowness** | ||||
|  - **nowness:playlist** | ||||
|  - **nowness:series** | ||||
|  - **NowTV** (Currently broken) | ||||
|  - **NowTVList** | ||||
|  - **nowvideo**: NowVideo | ||||
|  - **Noz** | ||||
|  - **npo**: npo.nl, ntr.nl, omroepwnl.nl, zapp.nl and npo3.nl | ||||
| @@ -610,6 +606,7 @@ | ||||
|  - **pcmag** | ||||
|  - **PearVideo** | ||||
|  - **People** | ||||
|  - **PerformGroup** | ||||
|  - **periscope**: Periscope | ||||
|  - **periscope:user**: Periscope user videos | ||||
|  - **PhilharmonieDeParis**: Philharmonie de Paris | ||||
| @@ -664,6 +661,7 @@ | ||||
|  - **Rai** | ||||
|  - **RaiPlay** | ||||
|  - **RaiPlayLive** | ||||
|  - **RaiPlayPlaylist** | ||||
|  - **RBMARadio** | ||||
|  - **RDS**: RDS.ca | ||||
|  - **RedBullTV** | ||||
| @@ -728,8 +726,10 @@ | ||||
|  - **SenateISVP** | ||||
|  - **SendtoNews** | ||||
|  - **ServingSys** | ||||
|  - **Servus** | ||||
|  - **Sexu** | ||||
|  - **Shahid** | ||||
|  - **ShahidShow** | ||||
|  - **Shared**: shared.sx | ||||
|  - **ShowRoomLive** | ||||
|  - **Sina** | ||||
| @@ -782,6 +782,7 @@ | ||||
|  - **streamcloud.eu** | ||||
|  - **StreamCZ** | ||||
|  - **StreetVoice** | ||||
|  - **StretchInternet** | ||||
|  - **SunPorno** | ||||
|  - **SVT** | ||||
|  - **SVTPlay**: SVT Play and Öppet arkiv | ||||
| @@ -793,7 +794,7 @@ | ||||
|  - **tagesschau:player** | ||||
|  - **Tass** | ||||
|  - **TastyTrade** | ||||
|  - **TBS** (Currently broken) | ||||
|  - **TBS** | ||||
|  - **TDSLifeway** | ||||
|  - **teachertube**: teachertube.com videos | ||||
|  - **teachertube:user:collection**: teachertube.com user and collection videos | ||||
| @@ -864,6 +865,8 @@ | ||||
|  - **tvland.com** | ||||
|  - **TVN24** | ||||
|  - **TVNoe** | ||||
|  - **TVNow** | ||||
|  - **TVNowList** | ||||
|  - **tvp**: Telewizja Polska | ||||
|  - **tvp:embed**: Telewizja Polska | ||||
|  - **tvp:series** | ||||
| @@ -887,6 +890,7 @@ | ||||
|  - **UDNEmbed**: 聯合影音 | ||||
|  - **UKTVPlay** | ||||
|  - **Unistra** | ||||
|  - **Unity** | ||||
|  - **uol.com.br** | ||||
|  - **uplynk** | ||||
|  - **uplynk:preplay** | ||||
| @@ -975,6 +979,7 @@ | ||||
|  - **vpro**: npo.nl, ntr.nl, omroepwnl.nl, zapp.nl and npo3.nl | ||||
|  - **Vrak** | ||||
|  - **VRT**: deredactie.be, sporza.be, cobra.be and cobra.canvas.be | ||||
|  - **VrtNU**: VrtNU.be | ||||
|  - **vrv** | ||||
|  - **vrv:series** | ||||
|  - **VShare** | ||||
| @@ -1033,6 +1038,9 @@ | ||||
|  - **YouJizz** | ||||
|  - **youku**: 优酷 | ||||
|  - **youku:show** | ||||
|  - **YouNowChannel** | ||||
|  - **YouNowLive** | ||||
|  - **YouNowMoment** | ||||
|  - **YouPorn** | ||||
|  - **YourUpload** | ||||
|  - **youtube**: YouTube.com | ||||
|   | ||||
							
								
								
									
										1
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								setup.py
									
									
									
									
									
								
							| @@ -109,6 +109,7 @@ setup( | ||||
|     author_email='ytdl@yt-dl.org', | ||||
|     maintainer='Sergey M.', | ||||
|     maintainer_email='dstftw@gmail.com', | ||||
|     license='Unlicense', | ||||
|     packages=[ | ||||
|         'youtube_dl', | ||||
|         'youtube_dl.extractor', 'youtube_dl.downloader', | ||||
|   | ||||
| @@ -562,7 +562,89 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ | ||||
|                     'width': 1920, | ||||
|                     'height': 1080, | ||||
|                 }] | ||||
|             ), | ||||
|             ), ( | ||||
|                 # https://github.com/rg3/youtube-dl/pull/14844 | ||||
|                 'urls_only', | ||||
|                 'http://unknown/manifest.mpd', | ||||
|                 [{ | ||||
|                     'manifest_url': 'http://unknown/manifest.mpd', | ||||
|                     'ext': 'mp4', | ||||
|                     'format_id': 'h264_aac_144p_m4s', | ||||
|                     'format_note': 'DASH video', | ||||
|                     'protocol': 'http_dash_segments', | ||||
|                     'acodec': 'mp4a.40.2', | ||||
|                     'vcodec': 'avc3.42c01e', | ||||
|                     'tbr': 200, | ||||
|                     'width': 256, | ||||
|                     'height': 144, | ||||
|                 }, { | ||||
|                     'manifest_url': 'http://unknown/manifest.mpd', | ||||
|                     'ext': 'mp4', | ||||
|                     'format_id': 'h264_aac_240p_m4s', | ||||
|                     'format_note': 'DASH video', | ||||
|                     'protocol': 'http_dash_segments', | ||||
|                     'acodec': 'mp4a.40.2', | ||||
|                     'vcodec': 'avc3.42c01e', | ||||
|                     'tbr': 400, | ||||
|                     'width': 424, | ||||
|                     'height': 240, | ||||
|                 }, { | ||||
|                     'manifest_url': 'http://unknown/manifest.mpd', | ||||
|                     'ext': 'mp4', | ||||
|                     'format_id': 'h264_aac_360p_m4s', | ||||
|                     'format_note': 'DASH video', | ||||
|                     'protocol': 'http_dash_segments', | ||||
|                     'acodec': 'mp4a.40.2', | ||||
|                     'vcodec': 'avc3.42c01e', | ||||
|                     'tbr': 800, | ||||
|                     'width': 640, | ||||
|                     'height': 360, | ||||
|                 }, { | ||||
|                     'manifest_url': 'http://unknown/manifest.mpd', | ||||
|                     'ext': 'mp4', | ||||
|                     'format_id': 'h264_aac_480p_m4s', | ||||
|                     'format_note': 'DASH video', | ||||
|                     'protocol': 'http_dash_segments', | ||||
|                     'acodec': 'mp4a.40.2', | ||||
|                     'vcodec': 'avc3.42c01e', | ||||
|                     'tbr': 1200, | ||||
|                     'width': 856, | ||||
|                     'height': 480, | ||||
|                 }, { | ||||
|                     'manifest_url': 'http://unknown/manifest.mpd', | ||||
|                     'ext': 'mp4', | ||||
|                     'format_id': 'h264_aac_576p_m4s', | ||||
|                     'format_note': 'DASH video', | ||||
|                     'protocol': 'http_dash_segments', | ||||
|                     'acodec': 'mp4a.40.2', | ||||
|                     'vcodec': 'avc3.42c01e', | ||||
|                     'tbr': 1600, | ||||
|                     'width': 1024, | ||||
|                     'height': 576, | ||||
|                 }, { | ||||
|                     'manifest_url': 'http://unknown/manifest.mpd', | ||||
|                     'ext': 'mp4', | ||||
|                     'format_id': 'h264_aac_720p_m4s', | ||||
|                     'format_note': 'DASH video', | ||||
|                     'protocol': 'http_dash_segments', | ||||
|                     'acodec': 'mp4a.40.2', | ||||
|                     'vcodec': 'avc3.42c01e', | ||||
|                     'tbr': 2400, | ||||
|                     'width': 1280, | ||||
|                     'height': 720, | ||||
|                 }, { | ||||
|                     'manifest_url': 'http://unknown/manifest.mpd', | ||||
|                     'ext': 'mp4', | ||||
|                     'format_id': 'h264_aac_1080p_m4s', | ||||
|                     'format_note': 'DASH video', | ||||
|                     'protocol': 'http_dash_segments', | ||||
|                     'acodec': 'mp4a.40.2', | ||||
|                     'vcodec': 'avc3.42c01e', | ||||
|                     'tbr': 4400, | ||||
|                     'width': 1920, | ||||
|                     'height': 1080, | ||||
|                 }] | ||||
|             ) | ||||
|         ] | ||||
|  | ||||
|         for mpd_file, mpd_url, expected_formats in _TEST_CASES: | ||||
| @@ -574,6 +656,33 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ | ||||
|                 self.ie._sort_formats(formats) | ||||
|                 expect_value(self, formats, expected_formats, None) | ||||
|  | ||||
|     def test_parse_f4m_formats(self): | ||||
|         _TEST_CASES = [ | ||||
|             ( | ||||
|                 # https://github.com/rg3/youtube-dl/issues/14660 | ||||
|                 'custom_base_url', | ||||
|                 'http://api.new.livestream.com/accounts/6115179/events/6764928/videos/144884262.f4m', | ||||
|                 [{ | ||||
|                     'manifest_url': 'http://api.new.livestream.com/accounts/6115179/events/6764928/videos/144884262.f4m', | ||||
|                     'ext': 'flv', | ||||
|                     'format_id': '2148', | ||||
|                     'protocol': 'f4m', | ||||
|                     'tbr': 2148, | ||||
|                     'width': 1280, | ||||
|                     'height': 720, | ||||
|                 }] | ||||
|             ), | ||||
|         ] | ||||
|  | ||||
|         for f4m_file, f4m_url, expected_formats in _TEST_CASES: | ||||
|             with io.open('./test/testdata/f4m/%s.f4m' % f4m_file, | ||||
|                          mode='r', encoding='utf-8') as f: | ||||
|                 formats = self.ie._parse_f4m_formats( | ||||
|                     compat_etree_fromstring(f.read().encode('utf-8')), | ||||
|                     f4m_url, None) | ||||
|                 self.ie._sort_formats(formats) | ||||
|                 expect_value(self, formats, expected_formats, None) | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     unittest.main() | ||||
|   | ||||
| @@ -466,11 +466,11 @@ class TestFormatSelection(unittest.TestCase): | ||||
|         ydl = YDL({'simulate': True}) | ||||
|         self.assertEqual(ydl._default_format_spec({}), 'bestvideo+bestaudio/best') | ||||
|  | ||||
|         ydl = YDL({'is_live': True}) | ||||
|         self.assertEqual(ydl._default_format_spec({}), 'best/bestvideo+bestaudio') | ||||
|         ydl = YDL({}) | ||||
|         self.assertEqual(ydl._default_format_spec({'is_live': True}), 'best/bestvideo+bestaudio') | ||||
|  | ||||
|         ydl = YDL({'simulate': True, 'is_live': True}) | ||||
|         self.assertEqual(ydl._default_format_spec({}), 'bestvideo+bestaudio/best') | ||||
|         ydl = YDL({'simulate': True}) | ||||
|         self.assertEqual(ydl._default_format_spec({'is_live': True}), 'bestvideo+bestaudio/best') | ||||
|  | ||||
|         ydl = YDL({'outtmpl': '-'}) | ||||
|         self.assertEqual(ydl._default_format_spec({}), 'best/bestvideo+bestaudio') | ||||
|   | ||||
| @@ -343,6 +343,7 @@ class TestUtil(unittest.TestCase): | ||||
|         self.assertEqual(unified_timestamp('Feb 7, 2016 at 6:35 pm'), 1454870100) | ||||
|         self.assertEqual(unified_timestamp('2017-03-30T17:52:41Q'), 1490896361) | ||||
|         self.assertEqual(unified_timestamp('Sep 11, 2013 | 5:49 AM'), 1378878540) | ||||
|         self.assertEqual(unified_timestamp('December 15, 2017 at 7:49 am'), 1513324140) | ||||
|  | ||||
|     def test_determine_ext(self): | ||||
|         self.assertEqual(determine_ext('http://example.com/foo/bar.mp4/?download'), 'mp4') | ||||
| @@ -540,6 +541,7 @@ class TestUtil(unittest.TestCase): | ||||
|         self.assertEqual(parse_duration('87 Min.'), 5220) | ||||
|         self.assertEqual(parse_duration('PT1H0.040S'), 3600.04) | ||||
|         self.assertEqual(parse_duration('PT00H03M30SZ'), 210) | ||||
|         self.assertEqual(parse_duration('P0Y0M0DT0H4M20.880S'), 260.88) | ||||
|  | ||||
|     def test_fix_xml_ampersands(self): | ||||
|         self.assertEqual( | ||||
|   | ||||
							
								
								
									
										10
									
								
								test/testdata/f4m/custom_base_url.f4m
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								test/testdata/f4m/custom_base_url.f4m
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <manifest xmlns="http://ns.adobe.com/f4m/1.0"> | ||||
|     <streamType>recorded</streamType> | ||||
|     <baseURL>http://vod.livestream.com/events/0000000000673980/</baseURL> | ||||
|     <duration>269.293</duration> | ||||
|     <bootstrapInfo profile="named" id="bootstrap_1">AAAAm2Fic3QAAAAAAAAAAQAAAAPoAAAAAAAEG+0AAAAAAAAAAAAAAAAAAQAAABlhc3J0AAAAAAAAAAABAAAAAQAAAC4BAAAAVmFmcnQAAAAAAAAD6AAAAAAEAAAAAQAAAAAAAAAAAAAXcAAAAC0AAAAAAAQHQAAAE5UAAAAuAAAAAAAEGtUAAAEYAAAAAAAAAAAAAAAAAAAAAAA=</bootstrapInfo> | ||||
|     <media url="b90f532f-b0f6-4f4e-8289-706d490b2fd8_2292" bootstrapInfoId="bootstrap_1" bitrate="2148" width="1280" height="720" videoCodec="avc1.4d401f" audioCodec="mp4a.40.2"> | ||||
|         <metadata>AgAKb25NZXRhRGF0YQgAAAAIAAhkdXJhdGlvbgBAcNSwIMSbpgAFd2lkdGgAQJQAAAAAAAAABmhlaWdodABAhoAAAAAAAAAJZnJhbWVyYXRlAEA4/7DoLwW3AA12aWRlb2RhdGFyYXRlAECe1DLgjcobAAx2aWRlb2NvZGVjaWQAQBwAAAAAAAAADWF1ZGlvZGF0YXJhdGUAQGSimlvaPKQADGF1ZGlvY29kZWNpZABAJAAAAAAAAAAACQ==</metadata> | ||||
|     </media> | ||||
| </manifest> | ||||
							
								
								
									
										218
									
								
								test/testdata/mpd/urls_only.mpd
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								test/testdata/mpd/urls_only.mpd
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,218 @@ | ||||
| <?xml version="1.0" ?> | ||||
| <MPD maxSegmentDuration="PT0H0M10.000S" mediaPresentationDuration="PT0H4M1.728S" minBufferTime="PT1.500S" profiles="urn:mpeg:dash:profile:isoff-main:2011" type="static" xmlns="urn:mpeg:dash:schema:mpd:2011"> | ||||
|   <Period duration="PT0H4M1.728S"> | ||||
|     <AdaptationSet bitstreamSwitching="true" lang="und" maxHeight="1080" maxWidth="1920" par="16:9" segmentAlignment="true"> | ||||
|       <ContentComponent contentType="video" id="1"/> | ||||
|       <Representation audioSamplingRate="44100" bandwidth="200000" codecs="avc3.42c01e,mp4a.40.2" frameRate="25" height="144" id="h264_aac_144p_m4s" mimeType="video/mp4" sar="1:1" startWithSAP="1" width="256"> | ||||
|         <SegmentList duration="10000" timescale="1000"> | ||||
|           <Initialization sourceURL="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/init/432f65a0.mp4"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/0/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/1/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/2/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/3/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/4/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/5/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/6/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/7/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/8/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/9/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/10/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/11/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/12/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/13/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/14/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/15/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/16/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/17/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/18/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/19/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/20/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/21/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/22/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/23/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_144p_m4s/24/432f65a0.m4s"/> | ||||
|         </SegmentList> | ||||
|       </Representation> | ||||
|       <Representation audioSamplingRate="44100" bandwidth="400000" codecs="avc3.42c01e,mp4a.40.2" frameRate="25" height="240" id="h264_aac_240p_m4s" mimeType="video/mp4" sar="160:159" startWithSAP="1" width="424"> | ||||
|         <SegmentList duration="10000" timescale="1000"> | ||||
|           <Initialization sourceURL="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/init/432f65a0.mp4"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/0/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/1/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/2/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/3/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/4/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/5/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/6/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/7/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/8/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/9/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/10/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/11/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/12/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/13/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/14/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/15/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/16/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/17/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/18/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/19/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/20/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/21/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/22/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/23/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_240p_m4s/24/432f65a0.m4s"/> | ||||
|         </SegmentList> | ||||
|       </Representation> | ||||
|       <Representation audioSamplingRate="44100" bandwidth="800000" codecs="avc3.42c01e,mp4a.40.2" frameRate="25" height="360" id="h264_aac_360p_m4s" mimeType="video/mp4" sar="1:1" startWithSAP="1" width="640"> | ||||
|         <SegmentList duration="10000" timescale="1000"> | ||||
|           <Initialization sourceURL="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/init/432f65a0.mp4"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/0/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/1/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/2/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/3/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/4/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/5/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/6/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/7/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/8/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/9/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/10/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/11/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/12/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/13/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/14/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/15/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/16/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/17/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/18/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/19/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/20/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/21/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/22/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/23/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_360p_m4s/24/432f65a0.m4s"/> | ||||
|         </SegmentList> | ||||
|       </Representation> | ||||
|       <Representation audioSamplingRate="44100" bandwidth="1200000" codecs="avc3.42c01e,mp4a.40.2" frameRate="25" height="480" id="h264_aac_480p_m4s" mimeType="video/mp4" sar="320:321" startWithSAP="1" width="856"> | ||||
|         <SegmentList duration="10000" timescale="1000"> | ||||
|           <Initialization sourceURL="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/init/432f65a0.mp4"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/0/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/1/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/2/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/3/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/4/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/5/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/6/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/7/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/8/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/9/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/10/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/11/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/12/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/13/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/14/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/15/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/16/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/17/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/18/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/19/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/20/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/21/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/22/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/23/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_480p_m4s/24/432f65a0.m4s"/> | ||||
|         </SegmentList> | ||||
|       </Representation> | ||||
|       <Representation audioSamplingRate="44100" bandwidth="1600000" codecs="avc3.42c01e,mp4a.40.2" frameRate="25" height="576" id="h264_aac_576p_m4s" mimeType="video/mp4" sar="1:1" startWithSAP="1" width="1024"> | ||||
|         <SegmentList duration="10000" timescale="1000"> | ||||
|           <Initialization sourceURL="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/init/432f65a0.mp4"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/0/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/1/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/2/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/3/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/4/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/5/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/6/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/7/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/8/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/9/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/10/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/11/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/12/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/13/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/14/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/15/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/16/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/17/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/18/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/19/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/20/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/21/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/22/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/23/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_576p_m4s/24/432f65a0.m4s"/> | ||||
|         </SegmentList> | ||||
|       </Representation> | ||||
|       <Representation audioSamplingRate="44100" bandwidth="2400000" codecs="avc3.42c01e,mp4a.40.2" frameRate="25" height="720" id="h264_aac_720p_m4s" mimeType="video/mp4" sar="1:1" startWithSAP="1" width="1280"> | ||||
|         <SegmentList duration="10000" timescale="1000"> | ||||
|           <Initialization sourceURL="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/init/432f65a0.mp4"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/0/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/1/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/2/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/3/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/4/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/5/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/6/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/7/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/8/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/9/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/10/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/11/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/12/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/13/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/14/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/15/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/16/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/17/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/18/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/19/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/20/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/21/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/22/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/23/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_720p_m4s/24/432f65a0.m4s"/> | ||||
|         </SegmentList> | ||||
|       </Representation> | ||||
|       <Representation audioSamplingRate="44100" bandwidth="4400000" codecs="avc3.42c01e,mp4a.40.2" frameRate="25" height="1080" id="h264_aac_1080p_m4s" mimeType="video/mp4" sar="1:1" startWithSAP="1" width="1920"> | ||||
|         <SegmentList duration="10000" timescale="1000"> | ||||
|           <Initialization sourceURL="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/init/432f65a0.mp4"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/0/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/1/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/2/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/3/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/4/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/5/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/6/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/7/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/8/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/9/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/10/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/11/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/12/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/13/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/14/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/15/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/16/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/17/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/18/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/19/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/20/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/21/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/22/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/23/432f65a0.m4s"/> | ||||
|           <SegmentURL media="../vd_5999c902ea707c67d8e267a9_1503250723/h264_aac_1080p_m4s/24/432f65a0.m4s"/> | ||||
|         </SegmentList> | ||||
|       </Representation> | ||||
|     </AdaptationSet> | ||||
|   </Period> | ||||
| </MPD> | ||||
| @@ -948,7 +948,8 @@ class YoutubeDL(object): | ||||
|                 report_download(n_entries) | ||||
|             else:  # iterable | ||||
|                 if playlistitems: | ||||
|                     entries = make_playlistitems_entries(list(ie_entries)) | ||||
|                     entries = make_playlistitems_entries(list(itertools.islice( | ||||
|                         ie_entries, 0, max(playlistitems)))) | ||||
|                 else: | ||||
|                     entries = list(itertools.islice( | ||||
|                         ie_entries, playliststart, playlistend)) | ||||
| @@ -974,6 +975,8 @@ class YoutubeDL(object): | ||||
|                     'playlist': playlist, | ||||
|                     'playlist_id': ie_result.get('id'), | ||||
|                     'playlist_title': ie_result.get('title'), | ||||
|                     'playlist_uploader': ie_result.get('uploader'), | ||||
|                     'playlist_uploader_id': ie_result.get('uploader_id'), | ||||
|                     'playlist_index': i + playliststart, | ||||
|                     'extractor': ie_result['extractor'], | ||||
|                     'webpage_url': ie_result['webpage_url'], | ||||
|   | ||||
| @@ -243,8 +243,17 @@ def remove_encrypted_media(media): | ||||
|                        media)) | ||||
|  | ||||
|  | ||||
| def _add_ns(prop): | ||||
|     return '{http://ns.adobe.com/f4m/1.0}%s' % prop | ||||
| def _add_ns(prop, ver=1): | ||||
|     return '{http://ns.adobe.com/f4m/%d.0}%s' % (ver, prop) | ||||
|  | ||||
|  | ||||
| def get_base_url(manifest): | ||||
|     base_url = xpath_text( | ||||
|         manifest, [_add_ns('baseURL'), _add_ns('baseURL', 2)], | ||||
|         'base URL', default=None) | ||||
|     if base_url: | ||||
|         base_url = base_url.strip() | ||||
|     return base_url | ||||
|  | ||||
|  | ||||
| class F4mFD(FragmentFD): | ||||
| @@ -330,13 +339,13 @@ class F4mFD(FragmentFD): | ||||
|             rate, media = list(filter( | ||||
|                 lambda f: int(f[0]) == requested_bitrate, formats))[0] | ||||
|  | ||||
|         base_url = compat_urlparse.urljoin(man_url, media.attrib['url']) | ||||
|         # Prefer baseURL for relative URLs as per 11.2 of F4M 3.0 spec. | ||||
|         man_base_url = get_base_url(doc) or man_url | ||||
|  | ||||
|         base_url = compat_urlparse.urljoin(man_base_url, media.attrib['url']) | ||||
|         bootstrap_node = doc.find(_add_ns('bootstrapInfo')) | ||||
|         # From Adobe F4M 3.0 spec: | ||||
|         # The <baseURL> element SHALL be the base URL for all relative | ||||
|         # (HTTP-based) URLs in the manifest. If <baseURL> is not present, said | ||||
|         # URLs should be relative to the location of the containing document. | ||||
|         boot_info, bootstrap_url = self._parse_bootstrap_node(bootstrap_node, man_url) | ||||
|         boot_info, bootstrap_url = self._parse_bootstrap_node( | ||||
|             bootstrap_node, man_base_url) | ||||
|         live = boot_info['live'] | ||||
|         metadata_node = media.find(_add_ns('metadata')) | ||||
|         if metadata_node is not None: | ||||
|   | ||||
| @@ -107,11 +107,12 @@ class FragmentFD(FileDownloader): | ||||
|     def _append_fragment(self, ctx, frag_content): | ||||
|         try: | ||||
|             ctx['dest_stream'].write(frag_content) | ||||
|             ctx['dest_stream'].flush() | ||||
|         finally: | ||||
|             if self.__do_ytdl_file(ctx): | ||||
|                 self._write_ytdl_file(ctx) | ||||
|             if not self.params.get('keep_fragments', False): | ||||
|                 os.remove(ctx['fragment_filename_sanitized']) | ||||
|                 os.remove(encodeFilename(ctx['fragment_filename_sanitized'])) | ||||
|             del ctx['fragment_filename_sanitized'] | ||||
|  | ||||
|     def _prepare_frag_download(self, ctx): | ||||
| @@ -158,7 +159,7 @@ class FragmentFD(FileDownloader): | ||||
|             if os.path.isfile(encodeFilename(self.ytdl_filename(ctx['filename']))): | ||||
|                 self._read_ytdl_file(ctx) | ||||
|                 if ctx['fragment_index'] > 0 and resume_len == 0: | ||||
|                     self.report_error( | ||||
|                     self.report_warning( | ||||
|                         'Inconsistent state of incomplete fragment download. ' | ||||
|                         'Restarting from the beginning...') | ||||
|                     ctx['fragment_index'] = resume_len = 0 | ||||
|   | ||||
| @@ -88,6 +88,7 @@ class HlsFD(FragmentFD): | ||||
|             if line.startswith('#'): | ||||
|                 if anvato_ad(line): | ||||
|                     ad_frags += 1 | ||||
|                     ad_frag_next = True | ||||
|                 continue | ||||
|             if ad_frag_next: | ||||
|                 ad_frag_next = False | ||||
|   | ||||
| @@ -284,8 +284,7 @@ class HttpFD(FileDownloader): | ||||
|         while count <= retries: | ||||
|             try: | ||||
|                 establish_connection() | ||||
|                 download() | ||||
|                 return True | ||||
|                 return download() | ||||
|             except RetryDownload as e: | ||||
|                 count += 1 | ||||
|                 if count <= retries: | ||||
|   | ||||
| @@ -228,10 +228,19 @@ class AfreecaTVIE(InfoExtractor): | ||||
|                     r'^(\d{8})_', key, 'upload date', default=None) | ||||
|                 file_duration = int_or_none(file_element.get('duration')) | ||||
|                 format_id = key if key else '%s_%s' % (video_id, file_num) | ||||
|                 formats = self._extract_m3u8_formats( | ||||
|                     file_url, video_id, 'mp4', entry_protocol='m3u8_native', | ||||
|                     m3u8_id='hls', | ||||
|                     note='Downloading part %d m3u8 information' % file_num) | ||||
|                 if determine_ext(file_url) == 'm3u8': | ||||
|                     formats = self._extract_m3u8_formats( | ||||
|                         file_url, video_id, 'mp4', entry_protocol='m3u8_native', | ||||
|                         m3u8_id='hls', | ||||
|                         note='Downloading part %d m3u8 information' % file_num) | ||||
|                 else: | ||||
|                     formats = [{ | ||||
|                         'url': file_url, | ||||
|                         'format_id': 'http', | ||||
|                     }] | ||||
|                 if not formats: | ||||
|                     continue | ||||
|                 self._sort_formats(formats) | ||||
|                 file_info = common_entry.copy() | ||||
|                 file_info.update({ | ||||
|                     'id': format_id, | ||||
|   | ||||
| @@ -78,15 +78,15 @@ class AnimeOnDemandIE(InfoExtractor): | ||||
|             post_url = urljoin(self._LOGIN_URL, post_url) | ||||
|  | ||||
|         response = self._download_webpage( | ||||
|             post_url, None, 'Logging in as %s' % username, | ||||
|             post_url, None, 'Logging in', | ||||
|             data=urlencode_postdata(login_form), headers={ | ||||
|                 'Referer': self._LOGIN_URL, | ||||
|             }) | ||||
|  | ||||
|         if all(p not in response for p in ('>Logout<', 'href="/users/sign_out"')): | ||||
|             error = self._search_regex( | ||||
|                 r'<p class="alert alert-danger">(.+?)</p>', | ||||
|                 response, 'error', default=None) | ||||
|                 r'<p[^>]+\bclass=(["\'])(?:(?!\1).)*\balert\b(?:(?!\1).)*\1[^>]*>(?P<error>.+?)</p>', | ||||
|                 response, 'error', default=None, group='error') | ||||
|             if error: | ||||
|                 raise ExtractorError('Unable to login: %s' % error, expected=True) | ||||
|             raise ExtractorError('Unable to log in') | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import re | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from .generic import GenericIE | ||||
| from ..compat import compat_str | ||||
| from ..utils import ( | ||||
|     determine_ext, | ||||
|     ExtractorError, | ||||
| @@ -126,6 +127,8 @@ class ARDMediathekIE(InfoExtractor): | ||||
|                 quality = stream.get('_quality') | ||||
|                 server = stream.get('_server') | ||||
|                 for stream_url in stream_urls: | ||||
|                     if not isinstance(stream_url, compat_str) or '//' not in stream_url: | ||||
|                         continue | ||||
|                     ext = determine_ext(stream_url) | ||||
|                     if quality != 'auto' and ext in ('f4m', 'm3u8'): | ||||
|                         continue | ||||
| @@ -146,13 +149,11 @@ class ARDMediathekIE(InfoExtractor): | ||||
|                                 'play_path': stream_url, | ||||
|                                 'format_id': 'a%s-rtmp-%s' % (num, quality), | ||||
|                             } | ||||
|                         elif stream_url.startswith('http'): | ||||
|                         else: | ||||
|                             f = { | ||||
|                                 'url': stream_url, | ||||
|                                 'format_id': 'a%s-%s-%s' % (num, ext, quality) | ||||
|                             } | ||||
|                         else: | ||||
|                             continue | ||||
|                         m = re.search(r'_(?P<width>\d+)x(?P<height>\d+)\.mp4$', stream_url) | ||||
|                         if m: | ||||
|                             f.update({ | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import re | ||||
| from .common import InfoExtractor | ||||
| from ..compat import ( | ||||
|     compat_parse_qs, | ||||
|     compat_str, | ||||
|     compat_urllib_parse_urlparse, | ||||
| ) | ||||
| from ..utils import ( | ||||
| @@ -15,6 +16,7 @@ from ..utils import ( | ||||
|     int_or_none, | ||||
|     NO_DEFAULT, | ||||
|     qualities, | ||||
|     try_get, | ||||
|     unified_strdate, | ||||
| ) | ||||
|  | ||||
| @@ -80,12 +82,15 @@ class ArteTVBaseIE(InfoExtractor): | ||||
|         info = self._download_json(json_url, video_id) | ||||
|         player_info = info['videoJsonPlayer'] | ||||
|  | ||||
|         vsr = player_info['VSR'] | ||||
|  | ||||
|         vsr = try_get(player_info, lambda x: x['VSR'], dict) | ||||
|         if not vsr: | ||||
|             raise ExtractorError( | ||||
|                 'Video %s is not available' % player_info.get('VID') or video_id, | ||||
|                 expected=True) | ||||
|             error = None | ||||
|             if try_get(player_info, lambda x: x['custom_msg']['type']) == 'error': | ||||
|                 error = try_get( | ||||
|                     player_info, lambda x: x['custom_msg']['msg'], compat_str) | ||||
|             if not error: | ||||
|                 error = 'Video %s is not available' % player_info.get('VID') or video_id | ||||
|             raise ExtractorError(error, expected=True) | ||||
|  | ||||
|         upload_date_str = player_info.get('shootingDate') | ||||
|         if not upload_date_str: | ||||
|   | ||||
| @@ -87,7 +87,7 @@ class AtresPlayerIE(InfoExtractor): | ||||
|             self._LOGIN_URL, urlencode_postdata(login_form)) | ||||
|         request.add_header('Content-Type', 'application/x-www-form-urlencoded') | ||||
|         response = self._download_webpage( | ||||
|             request, None, 'Logging in as %s' % username) | ||||
|             request, None, 'Logging in') | ||||
|  | ||||
|         error = self._html_search_regex( | ||||
|             r'(?s)<ul[^>]+class="[^"]*\blist_error\b[^"]*">(.+?)</ul>', | ||||
|   | ||||
							
								
								
									
										78
									
								
								youtube_dl/extractor/aws.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								youtube_dl/extractor/aws.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| # coding: utf-8 | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import datetime | ||||
| import hashlib | ||||
| import hmac | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..compat import compat_urllib_parse_urlencode | ||||
|  | ||||
|  | ||||
| class AWSIE(InfoExtractor): | ||||
|     _AWS_ALGORITHM = 'AWS4-HMAC-SHA256' | ||||
|     _AWS_REGION = 'us-east-1' | ||||
|  | ||||
|     def _aws_execute_api(self, aws_dict, video_id, query=None): | ||||
|         query = query or {} | ||||
|         amz_date = datetime.datetime.utcnow().strftime('%Y%m%dT%H%M%SZ') | ||||
|         date = amz_date[:8] | ||||
|         headers = { | ||||
|             'Accept': 'application/json', | ||||
|             'Host': self._AWS_PROXY_HOST, | ||||
|             'X-Amz-Date': amz_date, | ||||
|         } | ||||
|         session_token = aws_dict.get('session_token') | ||||
|         if session_token: | ||||
|             headers['X-Amz-Security-Token'] = session_token | ||||
|         headers['X-Api-Key'] = self._AWS_API_KEY | ||||
|  | ||||
|         def aws_hash(s): | ||||
|             return hashlib.sha256(s.encode('utf-8')).hexdigest() | ||||
|  | ||||
|         # Task 1: http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html | ||||
|         canonical_querystring = compat_urllib_parse_urlencode(query) | ||||
|         canonical_headers = '' | ||||
|         for header_name, header_value in headers.items(): | ||||
|             canonical_headers += '%s:%s\n' % (header_name.lower(), header_value) | ||||
|         signed_headers = ';'.join([header.lower() for header in headers.keys()]) | ||||
|         canonical_request = '\n'.join([ | ||||
|             'GET', | ||||
|             aws_dict['uri'], | ||||
|             canonical_querystring, | ||||
|             canonical_headers, | ||||
|             signed_headers, | ||||
|             aws_hash('') | ||||
|         ]) | ||||
|  | ||||
|         # Task 2: http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html | ||||
|         credential_scope_list = [date, self._AWS_REGION, 'execute-api', 'aws4_request'] | ||||
|         credential_scope = '/'.join(credential_scope_list) | ||||
|         string_to_sign = '\n'.join([self._AWS_ALGORITHM, amz_date, credential_scope, aws_hash(canonical_request)]) | ||||
|  | ||||
|         # Task 3: http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html | ||||
|         def aws_hmac(key, msg): | ||||
|             return hmac.new(key, msg.encode('utf-8'), hashlib.sha256) | ||||
|  | ||||
|         def aws_hmac_digest(key, msg): | ||||
|             return aws_hmac(key, msg).digest() | ||||
|  | ||||
|         def aws_hmac_hexdigest(key, msg): | ||||
|             return aws_hmac(key, msg).hexdigest() | ||||
|  | ||||
|         k_signing = ('AWS4' + aws_dict['secret_key']).encode('utf-8') | ||||
|         for value in credential_scope_list: | ||||
|             k_signing = aws_hmac_digest(k_signing, value) | ||||
|  | ||||
|         signature = aws_hmac_hexdigest(k_signing, string_to_sign) | ||||
|  | ||||
|         # Task 4: http://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html | ||||
|         headers['Authorization'] = ', '.join([ | ||||
|             '%s Credential=%s/%s' % (self._AWS_ALGORITHM, aws_dict['access_key'], credential_scope), | ||||
|             'SignedHeaders=%s' % signed_headers, | ||||
|             'Signature=%s' % signature, | ||||
|         ]) | ||||
|  | ||||
|         return self._download_json( | ||||
|             'https://%s%s%s' % (self._AWS_PROXY_HOST, aws_dict['uri'], '?' + canonical_querystring if canonical_querystring else ''), | ||||
|             video_id, headers=headers) | ||||
| @@ -47,7 +47,7 @@ class AZMedienIE(AZMedienBaseIE): | ||||
|         'url': 'http://www.telezueri.ch/62-show-zuerinews/13772-episode-sonntag-18-dezember-2016/32419-segment-massenabweisungen-beim-hiltl-club-wegen-pelzboom', | ||||
|         'info_dict': { | ||||
|             'id': '1_2444peh4', | ||||
|             'ext': 'mov', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Massenabweisungen beim Hiltl Club wegen Pelzboom', | ||||
|             'description': 'md5:9ea9dd1b159ad65b36ddcf7f0d7c76a8', | ||||
|             'uploader_id': 'TeleZ?ri', | ||||
|   | ||||
| @@ -59,7 +59,7 @@ class BambuserIE(InfoExtractor): | ||||
|             self._LOGIN_URL, urlencode_postdata(login_form)) | ||||
|         request.add_header('Referer', self._LOGIN_URL) | ||||
|         response = self._download_webpage( | ||||
|             request, None, 'Logging in as %s' % username) | ||||
|             request, None, 'Logging in') | ||||
|  | ||||
|         login_error = self._html_search_regex( | ||||
|             r'(?s)<div class="messages error">(.+?)</div>', | ||||
|   | ||||
| @@ -386,7 +386,7 @@ class BBCCoUkIE(InfoExtractor): | ||||
|                             m3u8_id=format_id, fatal=False)) | ||||
|                         if re.search(self._USP_RE, href): | ||||
|                             usp_formats = self._extract_m3u8_formats( | ||||
|                                 re.sub(self._USP_RE, r'/\1\.ism/\1\.m3u8', href), | ||||
|                                 re.sub(self._USP_RE, r'/\1.ism/\1.m3u8', href), | ||||
|                                 programme_id, ext='mp4', entry_protocol='m3u8_native', | ||||
|                                 m3u8_id=format_id, fatal=False) | ||||
|                             for f in usp_formats: | ||||
|   | ||||
| @@ -1,20 +1,23 @@ | ||||
| # coding: utf-8 | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import json | ||||
| import re | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..utils import ( | ||||
|     determine_ext, | ||||
|     ExtractorError, | ||||
|     int_or_none, | ||||
|     parse_duration, | ||||
|     parse_iso8601, | ||||
|     xpath_element, | ||||
|     xpath_text, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class BRIE(InfoExtractor): | ||||
|     IE_DESC = 'Bayerischer Rundfunk Mediathek' | ||||
|     IE_DESC = 'Bayerischer Rundfunk' | ||||
|     _VALID_URL = r'(?P<base_url>https?://(?:www\.)?br(?:-klassik)?\.de)/(?:[a-z0-9\-_]+/)+(?P<id>[a-z0-9\-_]+)\.html' | ||||
|  | ||||
|     _TESTS = [ | ||||
| @@ -123,10 +126,10 @@ class BRIE(InfoExtractor): | ||||
|         for asset in assets.findall('asset'): | ||||
|             format_url = xpath_text(asset, ['downloadUrl', 'url']) | ||||
|             asset_type = asset.get('type') | ||||
|             if asset_type == 'HDS': | ||||
|             if asset_type.startswith('HDS'): | ||||
|                 formats.extend(self._extract_f4m_formats( | ||||
|                     format_url + '?hdcore=3.2.0', media_id, f4m_id='hds', fatal=False)) | ||||
|             elif asset_type == 'HLS': | ||||
|             elif asset_type.startswith('HLS'): | ||||
|                 formats.extend(self._extract_m3u8_formats( | ||||
|                     format_url, media_id, 'mp4', 'm3u8_native', m3u8_id='hds', fatal=False)) | ||||
|             else: | ||||
| @@ -169,3 +172,140 @@ class BRIE(InfoExtractor): | ||||
|         } for variant in variants.findall('variant') if xpath_text(variant, 'url')] | ||||
|         thumbnails.sort(key=lambda x: x['width'] * x['height'], reverse=True) | ||||
|         return thumbnails | ||||
|  | ||||
|  | ||||
| class BRMediathekIE(InfoExtractor): | ||||
|     IE_DESC = 'Bayerischer Rundfunk Mediathek' | ||||
|     _VALID_URL = r'https?://(?:www\.)?br\.de/mediathek/video/[^/?&#]*?-(?P<id>av:[0-9a-f]{24})' | ||||
|  | ||||
|     _TESTS = [{ | ||||
|         'url': 'https://www.br.de/mediathek/video/gesundheit-die-sendung-vom-28112017-av:5a1e6a6e8fce6d001871cc8e', | ||||
|         'md5': 'fdc3d485835966d1622587d08ba632ec', | ||||
|         'info_dict': { | ||||
|             'id': 'av:5a1e6a6e8fce6d001871cc8e', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Die Sendung vom 28.11.2017', | ||||
|             'description': 'md5:6000cdca5912ab2277e5b7339f201ccc', | ||||
|             'timestamp': 1511942766, | ||||
|             'upload_date': '20171129', | ||||
|         } | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         clip_id = self._match_id(url) | ||||
|  | ||||
|         clip = self._download_json( | ||||
|             'https://proxy-base.master.mango.express/graphql', | ||||
|             clip_id, data=json.dumps({ | ||||
|                 "query": """{ | ||||
|   viewer { | ||||
|     clip(id: "%s") { | ||||
|       title | ||||
|       description | ||||
|       duration | ||||
|       createdAt | ||||
|       ageRestriction | ||||
|       videoFiles { | ||||
|         edges { | ||||
|           node { | ||||
|             publicLocation | ||||
|             fileSize | ||||
|             videoProfile { | ||||
|               width | ||||
|               height | ||||
|               bitrate | ||||
|               encoding | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       captionFiles { | ||||
|         edges { | ||||
|           node { | ||||
|             publicLocation | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       teaserImages { | ||||
|         edges { | ||||
|           node { | ||||
|             imageFiles { | ||||
|               edges { | ||||
|                 node { | ||||
|                   publicLocation | ||||
|                   width | ||||
|                   height | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| }""" % clip_id}).encode(), headers={ | ||||
|                 'Content-Type': 'application/json', | ||||
|             })['data']['viewer']['clip'] | ||||
|         title = clip['title'] | ||||
|  | ||||
|         formats = [] | ||||
|         for edge in clip.get('videoFiles', {}).get('edges', []): | ||||
|             node = edge.get('node', {}) | ||||
|             n_url = node.get('publicLocation') | ||||
|             if not n_url: | ||||
|                 continue | ||||
|             ext = determine_ext(n_url) | ||||
|             if ext == 'm3u8': | ||||
|                 formats.extend(self._extract_m3u8_formats( | ||||
|                     n_url, clip_id, 'mp4', 'm3u8_native', | ||||
|                     m3u8_id='hls', fatal=False)) | ||||
|             else: | ||||
|                 video_profile = node.get('videoProfile', {}) | ||||
|                 tbr = int_or_none(video_profile.get('bitrate')) | ||||
|                 format_id = 'http' | ||||
|                 if tbr: | ||||
|                     format_id += '-%d' % tbr | ||||
|                 formats.append({ | ||||
|                     'format_id': format_id, | ||||
|                     'url': n_url, | ||||
|                     'width': int_or_none(video_profile.get('width')), | ||||
|                     'height': int_or_none(video_profile.get('height')), | ||||
|                     'tbr': tbr, | ||||
|                     'filesize': int_or_none(node.get('fileSize')), | ||||
|                 }) | ||||
|         self._sort_formats(formats) | ||||
|  | ||||
|         subtitles = {} | ||||
|         for edge in clip.get('captionFiles', {}).get('edges', []): | ||||
|             node = edge.get('node', {}) | ||||
|             n_url = node.get('publicLocation') | ||||
|             if not n_url: | ||||
|                 continue | ||||
|             subtitles.setdefault('de', []).append({ | ||||
|                 'url': n_url, | ||||
|             }) | ||||
|  | ||||
|         thumbnails = [] | ||||
|         for edge in clip.get('teaserImages', {}).get('edges', []): | ||||
|             for image_edge in edge.get('node', {}).get('imageFiles', {}).get('edges', []): | ||||
|                 node = image_edge.get('node', {}) | ||||
|                 n_url = node.get('publicLocation') | ||||
|                 if not n_url: | ||||
|                     continue | ||||
|                 thumbnails.append({ | ||||
|                     'url': n_url, | ||||
|                     'width': int_or_none(node.get('width')), | ||||
|                     'height': int_or_none(node.get('height')), | ||||
|                 }) | ||||
|  | ||||
|         return { | ||||
|             'id': clip_id, | ||||
|             'title': title, | ||||
|             'description': clip.get('description'), | ||||
|             'duration': int_or_none(clip.get('duration')), | ||||
|             'timestamp': parse_iso8601(clip.get('createdAt')), | ||||
|             'age_limit': int_or_none(clip.get('ageRestriction')), | ||||
|             'formats': formats, | ||||
|             'subtitles': subtitles, | ||||
|             'thumbnails': thumbnails, | ||||
|         } | ||||
|   | ||||
| @@ -464,7 +464,7 @@ class BrightcoveNewIE(AdobePassIE): | ||||
|             'timestamp': 1441391203, | ||||
|             'upload_date': '20150904', | ||||
|             'uploader_id': '929656772001', | ||||
|             'formats': 'mincount:22', | ||||
|             'formats': 'mincount:20', | ||||
|         }, | ||||
|     }, { | ||||
|         # with rtmp streams | ||||
| @@ -478,7 +478,7 @@ class BrightcoveNewIE(AdobePassIE): | ||||
|             'timestamp': 1433556729, | ||||
|             'upload_date': '20150606', | ||||
|             'uploader_id': '4036320279001', | ||||
|             'formats': 'mincount:41', | ||||
|             'formats': 'mincount:39', | ||||
|         }, | ||||
|         'params': { | ||||
|             # m3u8 download | ||||
| @@ -564,59 +564,7 @@ class BrightcoveNewIE(AdobePassIE): | ||||
|  | ||||
|         return entries | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         url, smuggled_data = unsmuggle_url(url, {}) | ||||
|         self._initialize_geo_bypass(smuggled_data.get('geo_countries')) | ||||
|  | ||||
|         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') | ||||
|  | ||||
|         api_url = 'https://edge.api.brightcove.com/playback/v1/accounts/%s/videos/%s' % (account_id, video_id) | ||||
|         try: | ||||
|             json_data = self._download_json(api_url, video_id, headers={ | ||||
|                 'Accept': 'application/json;pk=%s' % policy_key | ||||
|             }) | ||||
|         except ExtractorError as e: | ||||
|             if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403: | ||||
|                 json_data = self._parse_json(e.cause.read().decode(), video_id)[0] | ||||
|                 message = json_data.get('message') or json_data['error_code'] | ||||
|                 if json_data.get('error_subcode') == 'CLIENT_GEO': | ||||
|                     self.raise_geo_restricted(msg=message) | ||||
|                 raise ExtractorError(message, expected=True) | ||||
|             raise | ||||
|  | ||||
|         errors = json_data.get('errors') | ||||
|         if errors and errors[0].get('error_subcode') == 'TVE_AUTH': | ||||
|             custom_fields = json_data['custom_fields'] | ||||
|             tve_token = self._extract_mvpd_auth( | ||||
|                 smuggled_data['source_url'], video_id, | ||||
|                 custom_fields['bcadobepassrequestorid'], | ||||
|                 custom_fields['bcadobepassresourceid']) | ||||
|             json_data = self._download_json( | ||||
|                 api_url, video_id, headers={ | ||||
|                     'Accept': 'application/json;pk=%s' % policy_key | ||||
|                 }, query={ | ||||
|                     'tveToken': tve_token, | ||||
|                 }) | ||||
|  | ||||
|     def _parse_brightcove_metadata(self, json_data, video_id): | ||||
|         title = json_data['name'].strip() | ||||
|  | ||||
|         formats = [] | ||||
| @@ -682,6 +630,7 @@ class BrightcoveNewIE(AdobePassIE): | ||||
|                     }) | ||||
|                 formats.append(f) | ||||
|  | ||||
|         errors = json_data.get('errors') | ||||
|         if not formats and errors: | ||||
|             error = errors[0] | ||||
|             raise ExtractorError( | ||||
| @@ -708,9 +657,64 @@ class BrightcoveNewIE(AdobePassIE): | ||||
|             'thumbnail': json_data.get('thumbnail') or json_data.get('poster'), | ||||
|             'duration': duration, | ||||
|             'timestamp': parse_iso8601(json_data.get('published_at')), | ||||
|             'uploader_id': account_id, | ||||
|             'uploader_id': json_data.get('account_id'), | ||||
|             'formats': formats, | ||||
|             'subtitles': subtitles, | ||||
|             'tags': json_data.get('tags', []), | ||||
|             'is_live': is_live, | ||||
|         } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         url, smuggled_data = unsmuggle_url(url, {}) | ||||
|         self._initialize_geo_bypass(smuggled_data.get('geo_countries')) | ||||
|  | ||||
|         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') | ||||
|  | ||||
|         api_url = 'https://edge.api.brightcove.com/playback/v1/accounts/%s/videos/%s' % (account_id, video_id) | ||||
|         try: | ||||
|             json_data = self._download_json(api_url, video_id, headers={ | ||||
|                 'Accept': 'application/json;pk=%s' % policy_key | ||||
|             }) | ||||
|         except ExtractorError as e: | ||||
|             if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403: | ||||
|                 json_data = self._parse_json(e.cause.read().decode(), video_id)[0] | ||||
|                 message = json_data.get('message') or json_data['error_code'] | ||||
|                 if json_data.get('error_subcode') == 'CLIENT_GEO': | ||||
|                     self.raise_geo_restricted(msg=message) | ||||
|                 raise ExtractorError(message, expected=True) | ||||
|             raise | ||||
|  | ||||
|         errors = json_data.get('errors') | ||||
|         if errors and errors[0].get('error_subcode') == 'TVE_AUTH': | ||||
|             custom_fields = json_data['custom_fields'] | ||||
|             tve_token = self._extract_mvpd_auth( | ||||
|                 smuggled_data['source_url'], video_id, | ||||
|                 custom_fields['bcadobepassrequestorid'], | ||||
|                 custom_fields['bcadobepassresourceid']) | ||||
|             json_data = self._download_json( | ||||
|                 api_url, video_id, headers={ | ||||
|                     'Accept': 'application/json;pk=%s' % policy_key | ||||
|                 }, query={ | ||||
|                     'tveToken': tve_token, | ||||
|                 }) | ||||
|  | ||||
|         return self._parse_brightcove_metadata(json_data, video_id) | ||||
|   | ||||
| @@ -3,20 +3,19 @@ from __future__ import unicode_literals | ||||
| import re | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..utils import ExtractorError | ||||
|  | ||||
|  | ||||
| class BYUtvIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:www\.)?byutv\.org/watch/(?!event/)(?P<id>[0-9a-f-]+)(?:/(?P<display_id>[^/?#&]+))?' | ||||
|     _VALID_URL = r'https?://(?:www\.)?byutv\.org/(?:watch|player)/(?!event/)(?P<id>[0-9a-f-]+)(?:/(?P<display_id>[^/?#&]+))?' | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.byutv.org/watch/6587b9a3-89d2-42a6-a7f7-fd2f81840a7d/studio-c-season-5-episode-5', | ||||
|         'info_dict': { | ||||
|             'id': '6587b9a3-89d2-42a6-a7f7-fd2f81840a7d', | ||||
|             'id': 'ZvanRocTpW-G5_yZFeltTAMv6jxOU9KH', | ||||
|             'display_id': 'studio-c-season-5-episode-5', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Season 5 Episode 5', | ||||
|             'description': 'md5:e07269172baff037f8e8bf9956bc9747', | ||||
|             'thumbnail': r're:^https?://.*\.jpg$', | ||||
|             'description': 'md5:1d31dc18ef4f075b28f6a65937d22c65', | ||||
|             'thumbnail': r're:^https?://.*', | ||||
|             'duration': 1486.486, | ||||
|         }, | ||||
|         'params': { | ||||
| @@ -26,6 +25,9 @@ class BYUtvIE(InfoExtractor): | ||||
|     }, { | ||||
|         'url': 'http://www.byutv.org/watch/6587b9a3-89d2-42a6-a7f7-fd2f81840a7d', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'https://www.byutv.org/player/27741493-dc83-40b0-8420-e7ae38a2ae98/byu-football-toledo-vs-byu-93016?listid=4fe0fee5-0d3c-4a29-b725-e4948627f472&listindex=0&q=toledo', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
| @@ -33,16 +35,16 @@ class BYUtvIE(InfoExtractor): | ||||
|         video_id = mobj.group('id') | ||||
|         display_id = mobj.group('display_id') or video_id | ||||
|  | ||||
|         webpage = self._download_webpage(url, display_id) | ||||
|         episode_code = self._search_regex( | ||||
|             r'(?s)episode:(.*?\}),\s*\n', webpage, 'episode information') | ||||
|  | ||||
|         ep = self._parse_json( | ||||
|             episode_code, display_id, transform_source=lambda s: | ||||
|             re.sub(r'(\n\s+)([a-zA-Z]+):\s+\'(.*?)\'', r'\1"\2": "\3"', s)) | ||||
|  | ||||
|         if ep['providerType'] != 'Ooyala': | ||||
|             raise ExtractorError('Unsupported provider %s' % ep['provider']) | ||||
|         ep = self._download_json( | ||||
|             'https://api.byutv.org/api3/catalog/getvideosforcontent', video_id, | ||||
|             query={ | ||||
|                 'contentid': video_id, | ||||
|                 'channel': 'byutv', | ||||
|                 'x-byutv-context': 'web$US', | ||||
|             }, headers={ | ||||
|                 'x-byutv-context': 'web$US', | ||||
|                 'x-byutv-platformkey': 'xsaaw9c7y5', | ||||
|             })['ooyalaVOD'] | ||||
|  | ||||
|         return { | ||||
|             '_type': 'url_transparent', | ||||
| @@ -50,44 +52,7 @@ class BYUtvIE(InfoExtractor): | ||||
|             'url': 'ooyala:%s' % ep['providerId'], | ||||
|             'id': video_id, | ||||
|             'display_id': display_id, | ||||
|             'title': ep['title'], | ||||
|             'title': ep.get('title'), | ||||
|             'description': ep.get('description'), | ||||
|             'thumbnail': ep.get('imageThumbnail'), | ||||
|         } | ||||
|  | ||||
|  | ||||
| class BYUtvEventIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:www\.)?byutv\.org/watch/event/(?P<id>[0-9a-f-]+)' | ||||
|     _TEST = { | ||||
|         'url': 'http://www.byutv.org/watch/event/29941b9b-8bf6-48d2-aebf-7a87add9e34b', | ||||
|         'info_dict': { | ||||
|             'id': '29941b9b-8bf6-48d2-aebf-7a87add9e34b', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Toledo vs. BYU (9/30/16)', | ||||
|         }, | ||||
|         'params': { | ||||
|             'skip_download': True, | ||||
|         }, | ||||
|         'add_ie': ['Ooyala'], | ||||
|     } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         video_id = self._match_id(url) | ||||
|  | ||||
|         webpage = self._download_webpage(url, video_id) | ||||
|  | ||||
|         ooyala_id = self._search_regex( | ||||
|             r'providerId\s*:\s*(["\'])(?P<id>(?:(?!\1).)+)\1', | ||||
|             webpage, 'ooyala id', group='id') | ||||
|  | ||||
|         title = self._search_regex( | ||||
|             r'class=["\']description["\'][^>]*>\s*<h1>([^<]+)</h1>', webpage, | ||||
|             'title').strip() | ||||
|  | ||||
|         return { | ||||
|             '_type': 'url_transparent', | ||||
|             'ie_key': 'Ooyala', | ||||
|             'url': 'ooyala:%s' % ooyala_id, | ||||
|             'id': video_id, | ||||
|             'title': title, | ||||
|         } | ||||
|   | ||||
| @@ -1,16 +1,22 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import re | ||||
| import json | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from .gigya import GigyaBaseIE | ||||
| from ..compat import compat_HTTPError | ||||
| from ..utils import ( | ||||
|     float_or_none, | ||||
|     ExtractorError, | ||||
|     strip_or_none, | ||||
|     float_or_none, | ||||
|     int_or_none, | ||||
|     parse_iso8601, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class CanvasIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://mediazone\.vrt\.be/api/v1/(?P<site_id>canvas|een|ketnet)/assets/(?P<id>m[dz]-ast-[^/?#&]+)' | ||||
|     _VALID_URL = r'https?://mediazone\.vrt\.be/api/v1/(?P<site_id>canvas|een|ketnet|vrtvideo)/assets/(?P<id>[^/?#&]+)' | ||||
|     _TESTS = [{ | ||||
|         'url': 'https://mediazone.vrt.be/api/v1/ketnet/assets/md-ast-4ac54990-ce66-4d00-a8ca-9eac86f4c475', | ||||
|         'md5': '90139b746a0a9bd7bb631283f6e2a64e', | ||||
| @@ -166,3 +172,139 @@ class CanvasEenIE(InfoExtractor): | ||||
|             'title': title, | ||||
|             'description': self._og_search_description(webpage), | ||||
|         } | ||||
|  | ||||
|  | ||||
| class VrtNUIE(GigyaBaseIE): | ||||
|     IE_DESC = 'VrtNU.be' | ||||
|     _VALID_URL = r'https?://(?:www\.)?vrt\.be/(?P<site_id>vrtnu)/(?:[^/]+/)*(?P<id>[^/?#&]+)' | ||||
|     _TESTS = [{ | ||||
|         'url': 'https://www.vrt.be/vrtnu/a-z/postbus-x/1/postbus-x-s1a1/', | ||||
|         'info_dict': { | ||||
|             'id': 'pbs-pub-2e2d8c27-df26-45c9-9dc6-90c78153044d$vid-90c932b1-e21d-4fb8-99b1-db7b49cf74de', | ||||
|             'ext': 'flv', | ||||
|             'title': 'De zwarte weduwe', | ||||
|             'description': 'md5:d90c21dced7db869a85db89a623998d4', | ||||
|             'duration': 1457.04, | ||||
|             'thumbnail': r're:^https?://.*\.jpg$', | ||||
|             'season': '1', | ||||
|             'season_number': 1, | ||||
|             'episode_number': 1, | ||||
|         }, | ||||
|         'skip': 'This video is only available for registered users' | ||||
|     }] | ||||
|     _NETRC_MACHINE = 'vrtnu' | ||||
|     _APIKEY = '3_0Z2HujMtiWq_pkAjgnS2Md2E11a1AwZjYiBETtwNE-EoEHDINgtnvcAOpNgmrVGy' | ||||
|     _CONTEXT_ID = 'R3595707040' | ||||
|  | ||||
|     def _real_initialize(self): | ||||
|         self._login() | ||||
|  | ||||
|     def _login(self): | ||||
|         username, password = self._get_login_info() | ||||
|         if username is None: | ||||
|             return | ||||
|  | ||||
|         auth_data = { | ||||
|             'APIKey': self._APIKEY, | ||||
|             'targetEnv': 'jssdk', | ||||
|             'loginID': username, | ||||
|             'password': password, | ||||
|             'authMode': 'cookie', | ||||
|         } | ||||
|  | ||||
|         auth_info = self._gigya_login(auth_data) | ||||
|  | ||||
|         # Sometimes authentication fails for no good reason, retry | ||||
|         login_attempt = 1 | ||||
|         while login_attempt <= 3: | ||||
|             try: | ||||
|                 # When requesting a token, no actual token is returned, but the | ||||
|                 # necessary cookies are set. | ||||
|                 self._request_webpage( | ||||
|                     'https://token.vrt.be', | ||||
|                     None, note='Requesting a token', errnote='Could not get a token', | ||||
|                     headers={ | ||||
|                         'Content-Type': 'application/json', | ||||
|                         'Referer': 'https://www.vrt.be/vrtnu/', | ||||
|                     }, | ||||
|                     data=json.dumps({ | ||||
|                         'uid': auth_info['UID'], | ||||
|                         'uidsig': auth_info['UIDSignature'], | ||||
|                         'ts': auth_info['signatureTimestamp'], | ||||
|                         'email': auth_info['profile']['email'], | ||||
|                     }).encode('utf-8')) | ||||
|             except ExtractorError as e: | ||||
|                 if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401: | ||||
|                     login_attempt += 1 | ||||
|                     self.report_warning('Authentication failed') | ||||
|                     self._sleep(1, None, msg_template='Waiting for %(timeout)s seconds before trying again') | ||||
|                 else: | ||||
|                     raise e | ||||
|             else: | ||||
|                 break | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         display_id = self._match_id(url) | ||||
|  | ||||
|         webpage = self._download_webpage(url, display_id) | ||||
|  | ||||
|         title = self._html_search_regex( | ||||
|             r'(?ms)<h1 class="content__heading">(.+?)</h1>', | ||||
|             webpage, 'title').strip() | ||||
|  | ||||
|         description = self._html_search_regex( | ||||
|             r'(?ms)<div class="content__description">(.+?)</div>', | ||||
|             webpage, 'description', default=None) | ||||
|  | ||||
|         season = self._html_search_regex( | ||||
|             [r'''(?xms)<div\ class="tabs__tab\ tabs__tab--active">\s* | ||||
|                     <span>seizoen\ (.+?)</span>\s* | ||||
|                 </div>''', | ||||
|              r'<option value="seizoen (\d{1,3})" data-href="[^"]+?" selected>'], | ||||
|             webpage, 'season', default=None) | ||||
|  | ||||
|         season_number = int_or_none(season) | ||||
|  | ||||
|         episode_number = int_or_none(self._html_search_regex( | ||||
|             r'''(?xms)<div\ class="content__episode">\s* | ||||
|                     <abbr\ title="aflevering">afl</abbr>\s*<span>(\d+)</span> | ||||
|                 </div>''', | ||||
|             webpage, 'episode_number', default=None)) | ||||
|  | ||||
|         release_date = parse_iso8601(self._html_search_regex( | ||||
|             r'(?ms)<div class="content__broadcastdate">\s*<time\ datetime="(.+?)"', | ||||
|             webpage, 'release_date', default=None)) | ||||
|  | ||||
|         # If there's a ? or a # in the URL, remove them and everything after | ||||
|         clean_url = url.split('?')[0].split('#')[0].strip('/') | ||||
|         securevideo_url = clean_url + '.mssecurevideo.json' | ||||
|  | ||||
|         try: | ||||
|             video = self._download_json(securevideo_url, display_id) | ||||
|         except ExtractorError as e: | ||||
|             if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401: | ||||
|                 self.raise_login_required() | ||||
|             raise | ||||
|  | ||||
|         # We are dealing with a '../<show>.relevant' URL | ||||
|         redirect_url = video.get('url') | ||||
|         if redirect_url: | ||||
|             return self.url_result(self._proto_relative_url(redirect_url, 'https:')) | ||||
|  | ||||
|         # There is only one entry, but with an unknown key, so just get | ||||
|         # the first one | ||||
|         video_id = list(video.values())[0].get('videoid') | ||||
|  | ||||
|         return { | ||||
|             '_type': 'url_transparent', | ||||
|             'url': 'https://mediazone.vrt.be/api/v1/vrtvideo/assets/%s' % video_id, | ||||
|             'ie_key': CanvasIE.ie_key(), | ||||
|             'id': video_id, | ||||
|             'display_id': display_id, | ||||
|             'title': title, | ||||
|             'description': description, | ||||
|             'season': season, | ||||
|             'season_number': season_number, | ||||
|             'episode_number': episode_number, | ||||
|             'release_date': release_date, | ||||
|         } | ||||
|   | ||||
| @@ -31,7 +31,7 @@ class CartoonNetworkIE(TurnerBaseIE): | ||||
|             'http://www.cartoonnetwork.com/video-seo-svc/episodeservices/getCvpPlaylist?networkName=CN2&' + query, video_id, { | ||||
|                 'secure': { | ||||
|                     'media_src': 'http://androidhls-secure.cdn.turner.com/toon/big', | ||||
|                     'tokenizer_src': 'http://www.cartoonnetwork.com/cntv/mvpd/processors/services/token_ipadAdobe.do', | ||||
|                     'tokenizer_src': 'https://token.vgtf.net/token/token_mobile', | ||||
|                 }, | ||||
|             }, { | ||||
|                 'url': url, | ||||
|   | ||||
| @@ -91,12 +91,10 @@ class CBSLocalIE(AnvatoIE): | ||||
|  | ||||
|         info_dict = self._extract_anvato_videos(webpage, display_id) | ||||
|  | ||||
|         time_str = self._html_search_regex( | ||||
|             r'class="entry-date">([^<]+)<', webpage, 'released date', default=None) | ||||
|         if time_str: | ||||
|             timestamp = unified_timestamp(time_str) | ||||
|         else: | ||||
|             timestamp = parse_iso8601(self._html_search_meta('uploadDate', webpage)) | ||||
|         timestamp = unified_timestamp(self._html_search_regex( | ||||
|             r'class="(?:entry|post)-date"[^>]*>([^<]+)', webpage, | ||||
|             'released date', default=None)) or parse_iso8601( | ||||
|             self._html_search_meta('uploadDate', webpage)) | ||||
|  | ||||
|         info_dict.update({ | ||||
|             'display_id': display_id, | ||||
|   | ||||
| @@ -93,7 +93,7 @@ class CCMAIE(InfoExtractor): | ||||
|             'description': clean_html(informacio.get('descripcio')), | ||||
|             'duration': duration, | ||||
|             'timestamp': timestamp, | ||||
|             'thumnails': thumbnails, | ||||
|             'thumbnails': thumbnails, | ||||
|             'subtitles': subtitles, | ||||
|             'formats': formats, | ||||
|         } | ||||
|   | ||||
| @@ -29,7 +29,10 @@ from ..compat import ( | ||||
|     compat_urlparse, | ||||
|     compat_xml_parse_error, | ||||
| ) | ||||
| from ..downloader.f4m import remove_encrypted_media | ||||
| from ..downloader.f4m import ( | ||||
|     get_base_url, | ||||
|     remove_encrypted_media, | ||||
| ) | ||||
| from ..utils import ( | ||||
|     NO_DEFAULT, | ||||
|     age_restricted, | ||||
| @@ -298,8 +301,9 @@ class InfoExtractor(object): | ||||
|     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. | ||||
|  | ||||
|     Additionally, playlists can have "title", "description" and "id" attributes | ||||
|     with the same semantics as videos (see above). | ||||
|     Additionally, playlists can have "id", "title", "description", "uploader", | ||||
|     "uploader_id", "uploader_url" attributes with the same semantics as videos | ||||
|     (see above). | ||||
|  | ||||
|  | ||||
|     _type "multi_video" indicates that there are multiple videos that | ||||
| @@ -491,6 +495,16 @@ class InfoExtractor(object): | ||||
|                 self.to_screen('%s' % (note,)) | ||||
|             else: | ||||
|                 self.to_screen('%s: %s' % (video_id, note)) | ||||
|  | ||||
|         # Some sites check X-Forwarded-For HTTP header in order to figure out | ||||
|         # the origin of the client behind proxy. This allows bypassing geo | ||||
|         # restriction by faking this header's value to IP that belongs to some | ||||
|         # geo unrestricted country. We will do so once we encounter any | ||||
|         # geo restriction error. | ||||
|         if self._x_forwarded_for_ip: | ||||
|             if 'X-Forwarded-For' not in headers: | ||||
|                 headers['X-Forwarded-For'] = self._x_forwarded_for_ip | ||||
|  | ||||
|         if isinstance(url_or_request, compat_urllib_request.Request): | ||||
|             url_or_request = update_Request( | ||||
|                 url_or_request, data=data, headers=headers, query=query) | ||||
| @@ -520,15 +534,6 @@ class InfoExtractor(object): | ||||
|         if isinstance(url_or_request, (compat_str, str)): | ||||
|             url_or_request = url_or_request.partition('#')[0] | ||||
|  | ||||
|         # Some sites check X-Forwarded-For HTTP header in order to figure out | ||||
|         # the origin of the client behind proxy. This allows bypassing geo | ||||
|         # restriction by faking this header's value to IP that belongs to some | ||||
|         # geo unrestricted country. We will do so once we encounter any | ||||
|         # geo restriction error. | ||||
|         if self._x_forwarded_for_ip: | ||||
|             if 'X-Forwarded-For' not in headers: | ||||
|                 headers['X-Forwarded-For'] = self._x_forwarded_for_ip | ||||
|  | ||||
|         urlh = self._request_webpage(url_or_request, video_id, note, errnote, fatal, data=data, headers=headers, query=query) | ||||
|         if urlh is False: | ||||
|             assert not fatal | ||||
| @@ -589,19 +594,11 @@ class InfoExtractor(object): | ||||
|         if not encoding: | ||||
|             encoding = self._guess_encoding_from_content(content_type, webpage_bytes) | ||||
|         if self._downloader.params.get('dump_intermediate_pages', False): | ||||
|             try: | ||||
|                 url = url_or_request.get_full_url() | ||||
|             except AttributeError: | ||||
|                 url = url_or_request | ||||
|             self.to_screen('Dumping request to ' + url) | ||||
|             self.to_screen('Dumping request to ' + urlh.geturl()) | ||||
|             dump = base64.b64encode(webpage_bytes).decode('ascii') | ||||
|             self._downloader.to_screen(dump) | ||||
|         if self._downloader.params.get('write_pages', False): | ||||
|             try: | ||||
|                 url = url_or_request.get_full_url() | ||||
|             except AttributeError: | ||||
|                 url = url_or_request | ||||
|             basen = '%s_%s' % (video_id, url) | ||||
|             basen = '%s_%s' % (video_id, urlh.geturl()) | ||||
|             if len(basen) > 240: | ||||
|                 h = '___' + hashlib.md5(basen.encode('utf-8')).hexdigest() | ||||
|                 basen = basen[:240 - len(h)] + h | ||||
| @@ -1239,11 +1236,8 @@ class InfoExtractor(object): | ||||
|         media_nodes = remove_encrypted_media(media_nodes) | ||||
|         if not media_nodes: | ||||
|             return formats | ||||
|         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() | ||||
|  | ||||
|         manifest_base_url = get_base_url(manifest) | ||||
|  | ||||
|         bootstrap_info = xpath_element( | ||||
|             manifest, ['{http://ns.adobe.com/f4m/1.0}bootstrapInfo', '{http://ns.adobe.com/f4m/2.0}bootstrapInfo'], | ||||
| @@ -1275,7 +1269,7 @@ class InfoExtractor(object): | ||||
|                     continue | ||||
|                 manifest_url = ( | ||||
|                     media_url if media_url.startswith('http://') or media_url.startswith('https://') | ||||
|                     else ((base_url or '/'.join(manifest_url.split('/')[:-1])) + '/' + media_url)) | ||||
|                     else ((manifest_base_url or '/'.join(manifest_url.split('/')[:-1])) + '/' + media_url)) | ||||
|                 # If media_url is itself a f4m manifest do the recursive extraction | ||||
|                 # since bitrates in parent manifest (this one) and media_url manifest | ||||
|                 # may differ leading to inability to resolve the format by requested | ||||
| @@ -1310,6 +1304,7 @@ class InfoExtractor(object): | ||||
|                 'url': manifest_url, | ||||
|                 'manifest_url': manifest_url, | ||||
|                 'ext': 'flv' if bootstrap_info is not None else None, | ||||
|                 'protocol': 'f4m', | ||||
|                 'tbr': tbr, | ||||
|                 'width': width, | ||||
|                 'height': height, | ||||
| @@ -1355,6 +1350,9 @@ class InfoExtractor(object): | ||||
|         if '#EXT-X-FAXS-CM:' in m3u8_doc:  # Adobe Flash Access | ||||
|             return [] | ||||
|  | ||||
|         if re.search(r'#EXT-X-SESSION-KEY:.*?URI="skd://', m3u8_doc):  # Apple FairPlay | ||||
|             return [] | ||||
|  | ||||
|         formats = [] | ||||
|  | ||||
|         format_url = lambda u: ( | ||||
| @@ -1401,7 +1399,7 @@ class InfoExtractor(object): | ||||
|             media_url = media.get('URI') | ||||
|             if media_url: | ||||
|                 format_id = [] | ||||
|                 for v in (group_id, name): | ||||
|                 for v in (m3u8_id, group_id, name): | ||||
|                     if v: | ||||
|                         format_id.append(v) | ||||
|                 f = { | ||||
| @@ -1979,6 +1977,22 @@ class InfoExtractor(object): | ||||
|                                     }) | ||||
|                                     segment_index += 1 | ||||
|                             representation_ms_info['fragments'] = fragments | ||||
|                         elif 'segment_urls' in representation_ms_info: | ||||
|                             # Segment URLs with no SegmentTimeline | ||||
|                             # Example: https://www.seznam.cz/zpravy/clanek/cesko-zasahne-vitr-o-sile-vichrice-muze-byt-i-zivotu-nebezpecny-39091 | ||||
|                             # https://github.com/rg3/youtube-dl/pull/14844 | ||||
|                             fragments = [] | ||||
|                             segment_duration = float_or_none( | ||||
|                                 representation_ms_info['segment_duration'], | ||||
|                                 representation_ms_info['timescale']) if 'segment_duration' in representation_ms_info else None | ||||
|                             for segment_url in representation_ms_info['segment_urls']: | ||||
|                                 fragment = { | ||||
|                                     location_key(segment_url): segment_url, | ||||
|                                 } | ||||
|                                 if segment_duration: | ||||
|                                     fragment['duration'] = segment_duration | ||||
|                                 fragments.append(fragment) | ||||
|                             representation_ms_info['fragments'] = fragments | ||||
|                         # NB: MPD manifest may contain direct URLs to unfragmented media. | ||||
|                         # No fragments key is present in this case. | ||||
|                         if 'fragments' in representation_ms_info: | ||||
| @@ -2233,27 +2247,35 @@ class InfoExtractor(object): | ||||
|         return formats | ||||
|  | ||||
|     def _extract_wowza_formats(self, url, video_id, m3u8_entry_protocol='m3u8_native', skip_protocols=[]): | ||||
|         query = compat_urlparse.urlparse(url).query | ||||
|         url = re.sub(r'/(?:manifest|playlist|jwplayer)\.(?:m3u8|f4m|mpd|smil)', '', url) | ||||
|         url_base = self._search_regex( | ||||
|             r'(?:(?:https?|rtmp|rtsp):)?(//[^?]+)', url, 'format url') | ||||
|         http_base_url = '%s:%s' % ('http', url_base) | ||||
|         formats = [] | ||||
|  | ||||
|         def manifest_url(manifest): | ||||
|             m_url = '%s/%s' % (http_base_url, manifest) | ||||
|             if query: | ||||
|                 m_url += '?%s' % query | ||||
|             return m_url | ||||
|  | ||||
|         if 'm3u8' not in skip_protocols: | ||||
|             formats.extend(self._extract_m3u8_formats( | ||||
|                 http_base_url + '/playlist.m3u8', video_id, 'mp4', | ||||
|                 manifest_url('playlist.m3u8'), video_id, 'mp4', | ||||
|                 m3u8_entry_protocol, m3u8_id='hls', fatal=False)) | ||||
|         if 'f4m' not in skip_protocols: | ||||
|             formats.extend(self._extract_f4m_formats( | ||||
|                 http_base_url + '/manifest.f4m', | ||||
|                 manifest_url('manifest.f4m'), | ||||
|                 video_id, f4m_id='hds', fatal=False)) | ||||
|         if 'dash' not in skip_protocols: | ||||
|             formats.extend(self._extract_mpd_formats( | ||||
|                 http_base_url + '/manifest.mpd', | ||||
|                 manifest_url('manifest.mpd'), | ||||
|                 video_id, mpd_id='dash', fatal=False)) | ||||
|         if re.search(r'(?:/smil:|\.smil)', url_base): | ||||
|             if 'smil' not in skip_protocols: | ||||
|                 rtmp_formats = self._extract_smil_formats( | ||||
|                     http_base_url + '/jwplayer.smil', | ||||
|                     manifest_url('jwplayer.smil'), | ||||
|                     video_id, fatal=False) | ||||
|                 for rtmp_format in rtmp_formats: | ||||
|                     rtsp_format = rtmp_format.copy() | ||||
|   | ||||
| @@ -38,11 +38,32 @@ class CrunchyrollBaseIE(InfoExtractor): | ||||
|     _LOGIN_FORM = 'login_form' | ||||
|     _NETRC_MACHINE = 'crunchyroll' | ||||
|  | ||||
|     def _call_rpc_api(self, method, video_id, note=None, data=None): | ||||
|         data = data or {} | ||||
|         data['req'] = 'RpcApi' + method | ||||
|         data = compat_urllib_parse_urlencode(data).encode('utf-8') | ||||
|         return self._download_xml( | ||||
|             'http://www.crunchyroll.com/xml/', | ||||
|             video_id, note, fatal=False, data=data, headers={ | ||||
|                 'Content-Type': 'application/x-www-form-urlencoded', | ||||
|             }) | ||||
|  | ||||
|     def _login(self): | ||||
|         (username, password) = self._get_login_info() | ||||
|         if username is None: | ||||
|             return | ||||
|  | ||||
|         self._download_webpage( | ||||
|             'https://www.crunchyroll.com/?a=formhandler', | ||||
|             None, 'Logging in', 'Wrong login info', | ||||
|             data=urlencode_postdata({ | ||||
|                 'formname': 'RpcApiUser_Login', | ||||
|                 'next_url': 'https://www.crunchyroll.com/acct/membership', | ||||
|                 'name': username, | ||||
|                 'password': password, | ||||
|             })) | ||||
|  | ||||
|         ''' | ||||
|         login_page = self._download_webpage( | ||||
|             self._LOGIN_URL, None, 'Downloading login page') | ||||
|  | ||||
| @@ -86,6 +107,7 @@ class CrunchyrollBaseIE(InfoExtractor): | ||||
|             raise ExtractorError('Unable to login: %s' % error, expected=True) | ||||
|  | ||||
|         raise ExtractorError('Unable to log in') | ||||
|         ''' | ||||
|  | ||||
|     def _real_initialize(self): | ||||
|         self._login() | ||||
| @@ -365,15 +387,19 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text | ||||
|     def _get_subtitles(self, video_id, webpage): | ||||
|         subtitles = {} | ||||
|         for sub_id, sub_name in re.findall(r'\bssid=([0-9]+)"[^>]+?\btitle="([^"]+)', webpage): | ||||
|             sub_page = self._download_webpage( | ||||
|                 'http://www.crunchyroll.com/xml/?req=RpcApiSubtitle_GetXml&subtitle_script_id=' + sub_id, | ||||
|                 video_id, note='Downloading subtitles for ' + sub_name) | ||||
|             id = self._search_regex(r'id=\'([0-9]+)', sub_page, 'subtitle_id', fatal=False) | ||||
|             iv = self._search_regex(r'<iv>([^<]+)', sub_page, 'subtitle_iv', fatal=False) | ||||
|             data = self._search_regex(r'<data>([^<]+)', sub_page, 'subtitle_data', fatal=False) | ||||
|             if not id or not iv or not data: | ||||
|             sub_doc = self._call_rpc_api( | ||||
|                 'Subtitle_GetXml', video_id, | ||||
|                 'Downloading subtitles for ' + sub_name, data={ | ||||
|                     'subtitle_script_id': sub_id, | ||||
|                 }) | ||||
|             if sub_doc is None: | ||||
|                 continue | ||||
|             subtitle = self._decrypt_subtitles(data, iv, id).decode('utf-8') | ||||
|             sid = sub_doc.get('id') | ||||
|             iv = xpath_text(sub_doc, 'iv', 'subtitle iv') | ||||
|             data = xpath_text(sub_doc, 'data', 'subtitle data') | ||||
|             if not sid or not iv or not data: | ||||
|                 continue | ||||
|             subtitle = self._decrypt_subtitles(data, iv, sid).decode('utf-8') | ||||
|             lang_code = self._search_regex(r'lang_code=["\']([^"\']+)', subtitle, 'subtitle_lang_code', fatal=False) | ||||
|             if not lang_code: | ||||
|                 continue | ||||
| @@ -444,65 +470,79 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text | ||||
|         for fmt in available_fmts: | ||||
|             stream_quality, stream_format = self._FORMAT_IDS[fmt] | ||||
|             video_format = fmt + 'p' | ||||
|             streamdata_req = sanitized_Request( | ||||
|                 'http://www.crunchyroll.com/xml/?req=RpcApiVideoPlayer_GetStandardConfig&media_id=%s&video_format=%s&video_quality=%s' | ||||
|                 % (video_id, stream_format, stream_quality), | ||||
|                 compat_urllib_parse_urlencode({'current_page': url}).encode('utf-8')) | ||||
|             streamdata_req.add_header('Content-Type', 'application/x-www-form-urlencoded') | ||||
|             streamdata = self._download_xml( | ||||
|                 streamdata_req, video_id, | ||||
|                 note='Downloading media info for %s' % video_format) | ||||
|             stream_info = streamdata.find('./{default}preload/stream_info') | ||||
|             video_encode_id = xpath_text(stream_info, './video_encode_id') | ||||
|             if video_encode_id in video_encode_ids: | ||||
|                 continue | ||||
|             video_encode_ids.append(video_encode_id) | ||||
|             stream_infos = [] | ||||
|             streamdata = self._call_rpc_api( | ||||
|                 'VideoPlayer_GetStandardConfig', video_id, | ||||
|                 'Downloading media info for %s' % video_format, data={ | ||||
|                     'media_id': video_id, | ||||
|                     'video_format': stream_format, | ||||
|                     'video_quality': stream_quality, | ||||
|                     'current_page': url, | ||||
|                 }) | ||||
|             if streamdata is not None: | ||||
|                 stream_info = streamdata.find('./{default}preload/stream_info') | ||||
|                 if stream_info is not None: | ||||
|                     stream_infos.append(stream_info) | ||||
|             stream_info = self._call_rpc_api( | ||||
|                 'VideoEncode_GetStreamInfo', video_id, | ||||
|                 'Downloading stream info for %s' % video_format, data={ | ||||
|                     'media_id': video_id, | ||||
|                     'video_format': stream_format, | ||||
|                     'video_encode_quality': stream_quality, | ||||
|                 }) | ||||
|             if stream_info is not None: | ||||
|                 stream_infos.append(stream_info) | ||||
|             for stream_info in stream_infos: | ||||
|                 video_encode_id = xpath_text(stream_info, './video_encode_id') | ||||
|                 if video_encode_id in video_encode_ids: | ||||
|                     continue | ||||
|                 video_encode_ids.append(video_encode_id) | ||||
|  | ||||
|             video_file = xpath_text(stream_info, './file') | ||||
|             if not video_file: | ||||
|                 continue | ||||
|             if video_file.startswith('http'): | ||||
|                 formats.extend(self._extract_m3u8_formats( | ||||
|                     video_file, video_id, 'mp4', entry_protocol='m3u8_native', | ||||
|                     m3u8_id='hls', fatal=False)) | ||||
|                 continue | ||||
|  | ||||
|             video_url = xpath_text(stream_info, './host') | ||||
|             if not video_url: | ||||
|                 continue | ||||
|             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_file.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) | ||||
|                 video_file = xpath_text(stream_info, './file') | ||||
|                 if not video_file: | ||||
|                     continue | ||||
|                 if video_file.startswith('http'): | ||||
|                     formats.extend(self._extract_m3u8_formats( | ||||
|                         video_file, video_id, 'mp4', entry_protocol='m3u8_native', | ||||
|                         m3u8_id='hls', fatal=False)) | ||||
|                     continue | ||||
|  | ||||
|             format_info.update({ | ||||
|                 'url': video_url, | ||||
|                 'play_path': video_file, | ||||
|                 'ext': 'flv', | ||||
|             }) | ||||
|             formats.append(format_info) | ||||
|         self._sort_formats(formats) | ||||
|                 video_url = xpath_text(stream_info, './host') | ||||
|                 if not video_url: | ||||
|                     continue | ||||
|                 metadata = stream_info.find('./metadata') | ||||
|                 format_info = { | ||||
|                     'format': video_format, | ||||
|                     'height': int_or_none(xpath_text(metadata, './height')), | ||||
|                     'width': int_or_none(xpath_text(metadata, './width')), | ||||
|                 } | ||||
|  | ||||
|         metadata = self._download_xml( | ||||
|             'http://www.crunchyroll.com/xml', video_id, | ||||
|             note='Downloading media info', query={ | ||||
|                 'req': 'RpcApiVideoPlayer_GetMediaMetadata', | ||||
|                 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_file.split(':')[-1]))) | ||||
|                     if self._is_valid_url(direct_video_url, video_id, video_format): | ||||
|                         format_info.update({ | ||||
|                             'format_id': 'http-' + video_format, | ||||
|                             'url': direct_video_url, | ||||
|                         }) | ||||
|                         formats.append(format_info) | ||||
|                         continue | ||||
|  | ||||
|                 format_info.update({ | ||||
|                     'format_id': 'rtmp-' + video_format, | ||||
|                     'url': video_url, | ||||
|                     'play_path': video_file, | ||||
|                     'ext': 'flv', | ||||
|                 }) | ||||
|                 formats.append(format_info) | ||||
|         self._sort_formats(formats, ('height', 'width', 'tbr', 'fps')) | ||||
|  | ||||
|         metadata = self._call_rpc_api( | ||||
|             'VideoPlayer_GetMediaMetadata', video_id, | ||||
|             note='Downloading media info', data={ | ||||
|                 'media_id': video_id, | ||||
|             }) | ||||
|  | ||||
|   | ||||
| @@ -4,13 +4,14 @@ import re | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..utils import ( | ||||
|     int_or_none, | ||||
|     unescapeHTML, | ||||
|     find_xpath_attr, | ||||
|     smuggle_url, | ||||
|     determine_ext, | ||||
|     ExtractorError, | ||||
|     extract_attributes, | ||||
|     find_xpath_attr, | ||||
|     get_element_by_class, | ||||
|     int_or_none, | ||||
|     smuggle_url, | ||||
|     unescapeHTML, | ||||
| ) | ||||
| from .senateisvp import SenateISVPIE | ||||
| from .ustream import UstreamIE | ||||
| @@ -68,6 +69,10 @@ class CSpanIE(InfoExtractor): | ||||
|             'uploader': 'HouseCommittee', | ||||
|             'uploader_id': '12987475', | ||||
|         }, | ||||
|     }, { | ||||
|         # Audio Only | ||||
|         'url': 'https://www.c-span.org/video/?437336-1/judiciary-antitrust-competition-policy-consumer-rights', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|     BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/%s/%s_%s/index.html?videoId=%s' | ||||
|  | ||||
| @@ -111,7 +116,15 @@ class CSpanIE(InfoExtractor): | ||||
|                     title = self._og_search_title(webpage) | ||||
|                     surl = smuggle_url(senate_isvp_url, {'force_title': title}) | ||||
|                     return self.url_result(surl, 'SenateISVP', video_id, title) | ||||
|                 video_id = self._search_regex( | ||||
|                     r'jwsetup\.clipprog\s*=\s*(\d+);', | ||||
|                     webpage, 'jwsetup program id', default=None) | ||||
|                 if video_id: | ||||
|                     video_type = 'program' | ||||
|         if video_type is None or video_id is None: | ||||
|             error_message = get_element_by_class('VLplayer-error-message', webpage) | ||||
|             if error_message: | ||||
|                 raise ExtractorError(error_message) | ||||
|             raise ExtractorError('unable to find video id and type') | ||||
|  | ||||
|         def get_text_attr(d, attr): | ||||
| @@ -138,7 +151,7 @@ class CSpanIE(InfoExtractor): | ||||
|         entries = [] | ||||
|         for partnum, f in enumerate(files): | ||||
|             formats = [] | ||||
|             for quality in f['qualities']: | ||||
|             for quality in f.get('qualities', []): | ||||
|                 formats.append({ | ||||
|                     'format_id': '%s-%sp' % (get_text_attr(quality, 'bitrate'), get_text_attr(quality, 'height')), | ||||
|                     'url': unescapeHTML(get_text_attr(quality, 'file')), | ||||
|   | ||||
| @@ -413,52 +413,3 @@ class DailymotionUserIE(DailymotionPlaylistIE): | ||||
|             'title': full_user, | ||||
|             'entries': self._extract_entries(user), | ||||
|         } | ||||
|  | ||||
|  | ||||
| class DailymotionCloudIE(DailymotionBaseInfoExtractor): | ||||
|     _VALID_URL_PREFIX = r'https?://api\.dmcloud\.net/(?:player/)?embed/' | ||||
|     _VALID_URL = r'%s[^/]+/(?P<id>[^/?]+)' % _VALID_URL_PREFIX | ||||
|     _VALID_EMBED_URL = r'%s[^/]+/[^\'"]+' % _VALID_URL_PREFIX | ||||
|  | ||||
|     _TESTS = [{ | ||||
|         # From http://www.francetvinfo.fr/economie/entreprises/les-entreprises-familiales-le-secret-de-la-reussite_933271.html | ||||
|         # Tested at FranceTvInfo_2 | ||||
|         'url': 'http://api.dmcloud.net/embed/4e7343f894a6f677b10006b4/556e03339473995ee145930c?auth=1464865870-0-jyhsm84b-ead4c701fb750cf9367bf4447167a3db&autoplay=1', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         # http://www.francetvinfo.fr/societe/larguez-les-amarres-le-cobaturage-se-developpe_980101.html | ||||
|         'url': 'http://api.dmcloud.net/player/embed/4e7343f894a6f677b10006b4/559545469473996d31429f06?auth=1467430263-0-90tglw2l-a3a4b64ed41efe48d7fccad85b8b8fda&autoplay=1', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     @classmethod | ||||
|     def _extract_dmcloud_url(cls, webpage): | ||||
|         mobj = re.search(r'<iframe[^>]+src=[\'"](%s)[\'"]' % cls._VALID_EMBED_URL, webpage) | ||||
|         if mobj: | ||||
|             return mobj.group(1) | ||||
|  | ||||
|         mobj = re.search( | ||||
|             r'<input[^>]+id=[\'"]dmcloudUrlEmissionSelect[\'"][^>]+value=[\'"](%s)[\'"]' % cls._VALID_EMBED_URL, | ||||
|             webpage) | ||||
|         if mobj: | ||||
|             return mobj.group(1) | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         video_id = self._match_id(url) | ||||
|  | ||||
|         webpage = self._download_webpage_no_ff(url, video_id) | ||||
|  | ||||
|         title = self._html_search_regex(r'<title>([^>]+)</title>', webpage, 'title') | ||||
|  | ||||
|         video_info = self._parse_json(self._search_regex( | ||||
|             r'var\s+info\s*=\s*([^;]+);', webpage, 'video info'), video_id) | ||||
|  | ||||
|         # TODO: parse ios_url, which is in fact a manifest | ||||
|         video_url = video_info['mp4_url'] | ||||
|  | ||||
|         return { | ||||
|             'id': video_id, | ||||
|             'url': video_url, | ||||
|             'title': title, | ||||
|             'thumbnail': video_info.get('thumbnail_url'), | ||||
|         } | ||||
|   | ||||
| @@ -13,33 +13,30 @@ from ..aes import ( | ||||
| from ..utils import ( | ||||
|     bytes_to_intlist, | ||||
|     bytes_to_long, | ||||
|     clean_html, | ||||
|     extract_attributes, | ||||
|     ExtractorError, | ||||
|     intlist_to_bytes, | ||||
|     get_element_by_id, | ||||
|     js_to_json, | ||||
|     int_or_none, | ||||
|     long_to_bytes, | ||||
|     pkcs1pad, | ||||
|     remove_end, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class DaisukiIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:www\.)?daisuki\.net/[^/]+/[^/]+/[^/]+/watch\.[^.]+\.(?P<id>\d+)\.html' | ||||
| class DaisukiMottoIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://motto\.daisuki\.net/framewatch/embed/[^/]+/(?P<id>[0-9a-zA-Z]{3})' | ||||
|  | ||||
|     _TEST = { | ||||
|         'url': 'http://www.daisuki.net/tw/en/anime/watch.TheIdolMasterCG.11213.html', | ||||
|         'url': 'http://motto.daisuki.net/framewatch/embed/embedDRAGONBALLSUPERUniverseSurvivalsaga/V2e/760/428', | ||||
|         'info_dict': { | ||||
|             'id': '11213', | ||||
|             'id': 'V2e', | ||||
|             'ext': 'mp4', | ||||
|             'title': '#01 Who is in the pumpkin carriage? - THE IDOLM@STER CINDERELLA GIRLS', | ||||
|             'title': '#117 SHOWDOWN OF LOVE! ANDROIDS VS UNIVERSE 2!!', | ||||
|             'subtitles': { | ||||
|                 'mul': [{ | ||||
|                     'ext': 'ttml', | ||||
|                 }], | ||||
|             }, | ||||
|             'creator': 'BANDAI NAMCO Entertainment', | ||||
|         }, | ||||
|         'params': { | ||||
|             'skip_download': True,  # AES-encrypted HLS stream | ||||
| @@ -73,15 +70,17 @@ class DaisukiIE(InfoExtractor): | ||||
|  | ||||
|             n, e = self._RSA_KEY | ||||
|             encrypted_aeskey = long_to_bytes(pow(bytes_to_long(padded_aeskey), e, n)) | ||||
|             init_data = self._download_json('http://www.daisuki.net/bin/bgn/init', video_id, query={ | ||||
|                 's': flashvars.get('s', ''), | ||||
|                 'c': flashvars.get('ss3_prm', ''), | ||||
|                 'e': url, | ||||
|                 'd': base64.b64encode(intlist_to_bytes(aes_cbc_encrypt( | ||||
|                     bytes_to_intlist(json.dumps(data)), | ||||
|                     aes_key, iv))).decode('ascii'), | ||||
|                 'a': base64.b64encode(encrypted_aeskey).decode('ascii'), | ||||
|             }, note='Downloading JSON metadata' + (' (try #%d)' % (idx + 1) if idx > 0 else '')) | ||||
|             init_data = self._download_json( | ||||
|                 'http://motto.daisuki.net/fastAPI/bgn/init/', | ||||
|                 video_id, query={ | ||||
|                     's': flashvars.get('s', ''), | ||||
|                     'c': flashvars.get('ss3_prm', ''), | ||||
|                     'e': url, | ||||
|                     'd': base64.b64encode(intlist_to_bytes(aes_cbc_encrypt( | ||||
|                         bytes_to_intlist(json.dumps(data)), | ||||
|                         aes_key, iv))).decode('ascii'), | ||||
|                     'a': base64.b64encode(encrypted_aeskey).decode('ascii'), | ||||
|                 }, note='Downloading JSON metadata' + (' (try #%d)' % (idx + 1) if idx > 0 else '')) | ||||
|  | ||||
|             if 'rtn' in init_data: | ||||
|                 encrypted_rtn = init_data['rtn'] | ||||
| @@ -98,14 +97,11 @@ class DaisukiIE(InfoExtractor): | ||||
|                 aes_key, iv)).decode('utf-8').rstrip('\0'), | ||||
|             video_id) | ||||
|  | ||||
|         title = rtn['title_str'] | ||||
|  | ||||
|         formats = self._extract_m3u8_formats( | ||||
|             rtn['play_url'], video_id, ext='mp4', entry_protocol='m3u8_native') | ||||
|  | ||||
|         title = remove_end(self._og_search_title(webpage), ' - DAISUKI') | ||||
|  | ||||
|         creator = self._html_search_regex( | ||||
|             r'Creator\s*:\s*([^<]+)', webpage, 'creator', fatal=False) | ||||
|  | ||||
|         subtitles = {} | ||||
|         caption_url = rtn.get('caption_url') | ||||
|         if caption_url: | ||||
| @@ -120,21 +116,18 @@ class DaisukiIE(InfoExtractor): | ||||
|             'title': title, | ||||
|             'formats': formats, | ||||
|             'subtitles': subtitles, | ||||
|             'creator': creator, | ||||
|         } | ||||
|  | ||||
|  | ||||
| class DaisukiPlaylistIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:www\.)daisuki\.net/[^/]+/[^/]+/[^/]+/detail\.(?P<id>[a-zA-Z0-9]+)\.html' | ||||
| class DaisukiMottoPlaylistIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://motto\.daisuki\.net/(?P<id>information)/' | ||||
|  | ||||
|     _TEST = { | ||||
|         'url': 'http://www.daisuki.net/tw/en/anime/detail.TheIdolMasterCG.html', | ||||
|         'url': 'http://motto.daisuki.net/information/', | ||||
|         'info_dict': { | ||||
|             'id': 'TheIdolMasterCG', | ||||
|             'title': 'THE IDOLM@STER CINDERELLA GIRLS', | ||||
|             'description': 'md5:0f2c028a9339f7a2c7fbf839edc5c5d8', | ||||
|             'title': 'DRAGON BALL SUPER', | ||||
|         }, | ||||
|         'playlist_count': 26, | ||||
|         'playlist_mincount': 117, | ||||
|     } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
| @@ -142,18 +135,19 @@ class DaisukiPlaylistIE(InfoExtractor): | ||||
|  | ||||
|         webpage = self._download_webpage(url, playlist_id) | ||||
|  | ||||
|         episode_pattern = r'''(?sx) | ||||
|             <img[^>]+delay="[^"]+/(\d+)/movie\.jpg".+? | ||||
|             <p[^>]+class=".*?\bepisodeNumber\b.*?">(?:<a[^>]+>)?([^<]+)''' | ||||
|         entries = [{ | ||||
|             '_type': 'url_transparent', | ||||
|             'url': url.replace('detail', 'watch').replace('.html', '.' + movie_id + '.html'), | ||||
|             'episode_id': episode_id, | ||||
|             'episode_number': int_or_none(episode_id), | ||||
|         } for movie_id, episode_id in re.findall(episode_pattern, webpage)] | ||||
|         entries = [] | ||||
|         for li in re.findall(r'(<li[^>]+?data-product_id="[a-zA-Z0-9]{3}"[^>]+>)', webpage): | ||||
|             attr = extract_attributes(li) | ||||
|             ad_id = attr.get('data-ad_id') | ||||
|             product_id = attr.get('data-product_id') | ||||
|             if ad_id and product_id: | ||||
|                 episode_id = attr.get('data-chapter') | ||||
|                 entries.append({ | ||||
|                     '_type': 'url_transparent', | ||||
|                     'url': 'http://motto.daisuki.net/framewatch/embed/%s/%s/760/428' % (ad_id, product_id), | ||||
|                     'episode_id': episode_id, | ||||
|                     'episode_number': int_or_none(episode_id), | ||||
|                     'ie_key': 'DaisukiMotto', | ||||
|                 }) | ||||
|  | ||||
|         playlist_title = remove_end( | ||||
|             self._og_search_title(webpage, fatal=False), ' - Anime - DAISUKI') | ||||
|         playlist_description = clean_html(get_element_by_id('synopsisTxt', webpage)) | ||||
|  | ||||
|         return self.playlist_result(entries, playlist_id, playlist_title, playlist_description) | ||||
|         return self.playlist_result(entries, playlist_title='DRAGON BALL SUPER') | ||||
|   | ||||
| @@ -2,53 +2,85 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..utils import unified_strdate | ||||
| from ..compat import compat_str | ||||
| from ..utils import ( | ||||
|     float_or_none, | ||||
|     unified_strdate, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class DctpTvIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:www\.)?dctp\.tv/(#/)?filme/(?P<id>.+?)/$' | ||||
|     _VALID_URL = r'https?://(?:www\.)?dctp\.tv/(?:#/)?filme/(?P<id>[^/?#&]+)' | ||||
|     _TEST = { | ||||
|         'url': 'http://www.dctp.tv/filme/videoinstallation-fuer-eine-kaufhausfassade/', | ||||
|         'md5': '174dd4a8a6225cf5655952f969cfbe24', | ||||
|         'info_dict': { | ||||
|             'id': '95eaa4f33dad413aa17b4ee613cccc6c', | ||||
|             'display_id': 'videoinstallation-fuer-eine-kaufhausfassade', | ||||
|             'ext': 'mp4', | ||||
|             'ext': 'flv', | ||||
|             'title': 'Videoinstallation für eine Kaufhausfassade', | ||||
|             'description': 'Kurzfilm', | ||||
|             'upload_date': '20110407', | ||||
|             'thumbnail': r're:^https?://.*\.jpg$', | ||||
|             'duration': 71.24, | ||||
|         }, | ||||
|         'params': { | ||||
|             # rtmp download | ||||
|             'skip_download': True, | ||||
|         }, | ||||
|     } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         video_id = self._match_id(url) | ||||
|         webpage = self._download_webpage(url, video_id) | ||||
|         display_id = self._match_id(url) | ||||
|  | ||||
|         object_id = self._html_search_meta('DC.identifier', webpage) | ||||
|         webpage = self._download_webpage(url, display_id) | ||||
|  | ||||
|         servers_json = self._download_json( | ||||
|             'http://www.dctp.tv/elastic_streaming_client/get_streaming_server/', | ||||
|             video_id, note='Downloading server list') | ||||
|         server = servers_json[0]['server'] | ||||
|         m3u8_path = self._search_regex( | ||||
|             r'\'([^\'"]+/playlist\.m3u8)"', webpage, 'm3u8 path') | ||||
|         formats = self._extract_m3u8_formats( | ||||
|             'http://%s%s' % (server, m3u8_path), video_id, ext='mp4', | ||||
|             entry_protocol='m3u8_native') | ||||
|         video_id = self._html_search_meta( | ||||
|             'DC.identifier', webpage, 'video id', | ||||
|             default=None) or self._search_regex( | ||||
|             r'id=["\']uuid[^>]+>([^<]+)<', webpage, 'video id') | ||||
|  | ||||
|         title = self._og_search_title(webpage) | ||||
|  | ||||
|         servers = self._download_json( | ||||
|             'http://www.dctp.tv/streaming_servers/', display_id, | ||||
|             note='Downloading server list', fatal=False) | ||||
|  | ||||
|         if servers: | ||||
|             endpoint = next( | ||||
|                 server['endpoint'] | ||||
|                 for server in servers | ||||
|                 if isinstance(server.get('endpoint'), compat_str) and | ||||
|                 'cloudfront' in server['endpoint']) | ||||
|         else: | ||||
|             endpoint = 'rtmpe://s2pqqn4u96e4j8.cloudfront.net/cfx/st/' | ||||
|  | ||||
|         app = self._search_regex( | ||||
|             r'^rtmpe?://[^/]+/(?P<app>.*)$', endpoint, 'app') | ||||
|  | ||||
|         formats = [{ | ||||
|             'url': endpoint, | ||||
|             'app': app, | ||||
|             'play_path': 'mp4:%s_dctp_0500_4x3.m4v' % video_id, | ||||
|             'page_url': url, | ||||
|             'player_url': 'http://svm-prod-dctptv-static.s3.amazonaws.com/dctptv-relaunch2012-109.swf', | ||||
|             'ext': 'flv', | ||||
|         }] | ||||
|  | ||||
|         description = self._html_search_meta('DC.description', webpage) | ||||
|         upload_date = unified_strdate( | ||||
|             self._html_search_meta('DC.date.created', webpage)) | ||||
|         thumbnail = self._og_search_thumbnail(webpage) | ||||
|         duration = float_or_none(self._search_regex( | ||||
|             r'id=["\']duration_in_ms[^+]>(\d+)', webpage, 'duration', | ||||
|             default=None), scale=1000) | ||||
|  | ||||
|         return { | ||||
|             'id': object_id, | ||||
|             'id': video_id, | ||||
|             'title': title, | ||||
|             'formats': formats, | ||||
|             'display_id': video_id, | ||||
|             'display_id': display_id, | ||||
|             'description': description, | ||||
|             'upload_date': upload_date, | ||||
|             'thumbnail': thumbnail, | ||||
|             'duration': duration, | ||||
|         } | ||||
|   | ||||
| @@ -1,14 +1,18 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| import random | ||||
| import re | ||||
| import string | ||||
|  | ||||
| from .discoverygo import DiscoveryGoBaseIE | ||||
| from ..utils import ( | ||||
|     parse_duration, | ||||
|     parse_iso8601, | ||||
|     ExtractorError, | ||||
|     update_url_query, | ||||
| ) | ||||
| from ..compat import compat_str | ||||
| from ..compat import compat_HTTPError | ||||
|  | ||||
|  | ||||
| class DiscoveryIE(InfoExtractor): | ||||
| class DiscoveryIE(DiscoveryGoBaseIE): | ||||
|     _VALID_URL = r'''(?x)https?://(?:www\.)?(?: | ||||
|             discovery| | ||||
|             investigationdiscovery| | ||||
| @@ -19,79 +23,65 @@ class DiscoveryIE(InfoExtractor): | ||||
|             sciencechannel| | ||||
|             tlc| | ||||
|             velocity | ||||
|         )\.com/(?:[^/]+/)*(?P<id>[^./?#]+)''' | ||||
|         )\.com(?P<path>/tv-shows/[^/]+/(?:video|full-episode)s/(?P<id>[^./?#]+))''' | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.discovery.com/tv-shows/mythbusters/videos/mission-impossible-outtakes.htm', | ||||
|         'url': 'https://www.discovery.com/tv-shows/cash-cab/videos/dave-foley', | ||||
|         'info_dict': { | ||||
|             'id': '20769', | ||||
|             'id': '5a2d9b4d6b66d17a5026e1fd', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Mission Impossible Outtakes', | ||||
|             'description': ('Watch Jamie Hyneman and Adam Savage practice being' | ||||
|                             ' each other -- to the point of confusing Jamie\'s dog -- and ' | ||||
|                             'don\'t miss Adam moon-walking as Jamie ... behind Jamie\'s' | ||||
|                             ' back.'), | ||||
|             'duration': 156, | ||||
|             'timestamp': 1302032462, | ||||
|             'upload_date': '20110405', | ||||
|             'uploader_id': '103207', | ||||
|             'title': 'Dave Foley', | ||||
|             'description': 'md5:4b39bcafccf9167ca42810eb5f28b01f', | ||||
|             'duration': 608, | ||||
|         }, | ||||
|         'params': { | ||||
|             'skip_download': True,  # requires ffmpeg | ||||
|         } | ||||
|     }, { | ||||
|         'url': 'http://www.discovery.com/tv-shows/mythbusters/videos/mythbusters-the-simpsons', | ||||
|         'info_dict': { | ||||
|             'id': 'mythbusters-the-simpsons', | ||||
|             'title': 'MythBusters: The Simpsons', | ||||
|         }, | ||||
|         'playlist_mincount': 10, | ||||
|     }, { | ||||
|         'url': 'http://www.animalplanet.com/longfin-eels-maneaters/', | ||||
|         'info_dict': { | ||||
|             'id': '78326', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Longfin Eels: Maneaters?', | ||||
|             'description': 'Jeremy Wade tests whether or not New Zealand\'s longfin eels are man-eaters by covering himself in fish guts and getting in the water with them.', | ||||
|             'upload_date': '20140725', | ||||
|             'timestamp': 1406246400, | ||||
|             'duration': 116, | ||||
|             'uploader_id': '103207', | ||||
|         }, | ||||
|         'params': { | ||||
|             'skip_download': True,  # requires ffmpeg | ||||
|         } | ||||
|         'url': 'https://www.investigationdiscovery.com/tv-shows/final-vision/full-episodes/final-vision', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|     _GEO_COUNTRIES = ['US'] | ||||
|     _GEO_BYPASS = False | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         display_id = self._match_id(url) | ||||
|         info = self._download_json(url + '?flat=1', display_id) | ||||
|         path, display_id = re.match(self._VALID_URL, url).groups() | ||||
|         webpage = self._download_webpage(url, display_id) | ||||
|  | ||||
|         video_title = info.get('playlist_title') or info.get('video_title') | ||||
|         react_data = self._parse_json(self._search_regex( | ||||
|             r'window\.__reactTransmitPacket\s*=\s*({.+?});', | ||||
|             webpage, 'react data'), display_id) | ||||
|         content_blocks = react_data['layout'][path]['contentBlocks'] | ||||
|         video = next(cb for cb in content_blocks if cb.get('type') == 'video')['content']['items'][0] | ||||
|         video_id = video['id'] | ||||
|  | ||||
|         entries = [] | ||||
|         access_token = self._download_json( | ||||
|             'https://www.discovery.com/anonymous', display_id, query={ | ||||
|                 'authLink': update_url_query( | ||||
|                     'https://login.discovery.com/v1/oauth2/authorize', { | ||||
|                         'client_id': react_data['application']['apiClientId'], | ||||
|                         'redirect_uri': 'https://fusion.ddmcdn.com/app/mercury-sdk/180/redirectHandler.html', | ||||
|                         'response_type': 'anonymous', | ||||
|                         'state': 'nonce,' + ''.join([random.choice(string.ascii_letters) for _ in range(32)]), | ||||
|                     }) | ||||
|             })['access_token'] | ||||
|  | ||||
|         for idx, video_info in enumerate(info['playlist']): | ||||
|             subtitles = {} | ||||
|             caption_url = video_info.get('captionsUrl') | ||||
|             if caption_url: | ||||
|                 subtitles = { | ||||
|                     'en': [{ | ||||
|                         'url': caption_url, | ||||
|                     }] | ||||
|                 } | ||||
|         try: | ||||
|             stream = self._download_json( | ||||
|                 'https://api.discovery.com/v1/streaming/video/' + video_id, | ||||
|                 display_id, headers={ | ||||
|                     'Authorization': 'Bearer ' + access_token, | ||||
|                 }) | ||||
|         except ExtractorError as e: | ||||
|             if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403: | ||||
|                 e_description = self._parse_json( | ||||
|                     e.cause.read().decode(), display_id)['description'] | ||||
|                 if 'resource not available for country' in e_description: | ||||
|                     self.raise_geo_restricted(countries=self._GEO_COUNTRIES) | ||||
|                 if 'Authorized Networks' in e_description: | ||||
|                     raise ExtractorError( | ||||
|                         'This video is only available via cable service provider subscription that' | ||||
|                         ' is not currently supported. You may want to use --cookies.', expected=True) | ||||
|                 raise ExtractorError(e_description) | ||||
|             raise | ||||
|  | ||||
|             entries.append({ | ||||
|                 '_type': 'url_transparent', | ||||
|                 'url': 'http://players.brightcove.net/103207/default_default/index.html?videoId=ref:%s' % video_info['referenceId'], | ||||
|                 'id': compat_str(video_info['id']), | ||||
|                 'title': video_info['title'], | ||||
|                 'description': video_info.get('description'), | ||||
|                 'duration': parse_duration(video_info.get('video_length')), | ||||
|                 'webpage_url': video_info.get('href') or video_info.get('url'), | ||||
|                 'thumbnail': video_info.get('thumbnailURL'), | ||||
|                 'alt_title': video_info.get('secondary_title'), | ||||
|                 'timestamp': parse_iso8601(video_info.get('publishedDate')), | ||||
|                 'subtitles': subtitles, | ||||
|             }) | ||||
|  | ||||
|         return self.playlist_result(entries, display_id, video_title) | ||||
|         return self._extract_video_info(video, stream, display_id) | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import re | ||||
| from .common import InfoExtractor | ||||
| from ..compat import compat_str | ||||
| from ..utils import ( | ||||
|     determine_ext, | ||||
|     extract_attributes, | ||||
|     ExtractorError, | ||||
|     int_or_none, | ||||
| @@ -27,42 +28,9 @@ class DiscoveryGoBaseIE(InfoExtractor): | ||||
|             velocitychannel | ||||
|         )go\.com/%s(?P<id>[^/?#&]+)''' | ||||
|  | ||||
|  | ||||
| class DiscoveryGoIE(DiscoveryGoBaseIE): | ||||
|     _VALID_URL = DiscoveryGoBaseIE._VALID_URL_TEMPLATE % r'(?:[^/]+/)+' | ||||
|     _GEO_COUNTRIES = ['US'] | ||||
|     _TEST = { | ||||
|         'url': 'https://www.discoverygo.com/bering-sea-gold/reaper-madness/', | ||||
|         'info_dict': { | ||||
|             'id': '58c167d86b66d12f2addeb01', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Reaper Madness', | ||||
|             'description': 'md5:09f2c625c99afb8946ed4fb7865f6e78', | ||||
|             'duration': 2519, | ||||
|             'series': 'Bering Sea Gold', | ||||
|             'season_number': 8, | ||||
|             'episode_number': 6, | ||||
|             'age_limit': 14, | ||||
|         }, | ||||
|     } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         display_id = self._match_id(url) | ||||
|  | ||||
|         webpage = self._download_webpage(url, display_id) | ||||
|  | ||||
|         container = extract_attributes( | ||||
|             self._search_regex( | ||||
|                 r'(<div[^>]+class=["\']video-player-container[^>]+>)', | ||||
|                 webpage, 'video container')) | ||||
|  | ||||
|         video = self._parse_json( | ||||
|             container.get('data-video') or container.get('data-json'), | ||||
|             display_id) | ||||
|  | ||||
|     def _extract_video_info(self, video, stream, display_id): | ||||
|         title = video['name'] | ||||
|  | ||||
|         stream = video.get('stream') | ||||
|         if not stream: | ||||
|             if video.get('authenticated') is True: | ||||
|                 raise ExtractorError( | ||||
| @@ -106,7 +74,11 @@ class DiscoveryGoIE(DiscoveryGoBaseIE): | ||||
|                         not subtitle_url.startswith('http')): | ||||
|                     continue | ||||
|                 lang = caption.get('fileLang', 'en') | ||||
|                 subtitles.setdefault(lang, []).append({'url': subtitle_url}) | ||||
|                 ext = determine_ext(subtitle_url) | ||||
|                 subtitles.setdefault(lang, []).append({ | ||||
|                     'url': subtitle_url, | ||||
|                     'ext': 'ttml' if ext == 'xml' else ext, | ||||
|                 }) | ||||
|  | ||||
|         return { | ||||
|             'id': video_id, | ||||
| @@ -124,6 +96,43 @@ class DiscoveryGoIE(DiscoveryGoBaseIE): | ||||
|         } | ||||
|  | ||||
|  | ||||
| class DiscoveryGoIE(DiscoveryGoBaseIE): | ||||
|     _VALID_URL = DiscoveryGoBaseIE._VALID_URL_TEMPLATE % r'(?:[^/]+/)+' | ||||
|     _GEO_COUNTRIES = ['US'] | ||||
|     _TEST = { | ||||
|         'url': 'https://www.discoverygo.com/bering-sea-gold/reaper-madness/', | ||||
|         'info_dict': { | ||||
|             'id': '58c167d86b66d12f2addeb01', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Reaper Madness', | ||||
|             'description': 'md5:09f2c625c99afb8946ed4fb7865f6e78', | ||||
|             'duration': 2519, | ||||
|             'series': 'Bering Sea Gold', | ||||
|             'season_number': 8, | ||||
|             'episode_number': 6, | ||||
|             'age_limit': 14, | ||||
|         }, | ||||
|     } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         display_id = self._match_id(url) | ||||
|  | ||||
|         webpage = self._download_webpage(url, display_id) | ||||
|  | ||||
|         container = extract_attributes( | ||||
|             self._search_regex( | ||||
|                 r'(<div[^>]+class=["\']video-player-container[^>]+>)', | ||||
|                 webpage, 'video container')) | ||||
|  | ||||
|         video = self._parse_json( | ||||
|             container.get('data-video') or container.get('data-json'), | ||||
|             display_id) | ||||
|  | ||||
|         stream = video.get('stream') | ||||
|  | ||||
|         return self._extract_video_info(video, stream, display_id) | ||||
|  | ||||
|  | ||||
| class DiscoveryGoPlaylistIE(DiscoveryGoBaseIE): | ||||
|     _VALID_URL = DiscoveryGoBaseIE._VALID_URL_TEMPLATE % '' | ||||
|     _TEST = { | ||||
|   | ||||
| @@ -10,6 +10,7 @@ from ..utils import ( | ||||
|     compat_str, | ||||
|     determine_ext, | ||||
|     ExtractorError, | ||||
|     update_url_query, | ||||
| ) | ||||
|  | ||||
|  | ||||
| @@ -108,9 +109,16 @@ class DisneyIE(InfoExtractor): | ||||
|                 continue | ||||
|             tbr = int_or_none(flavor.get('bitrate')) | ||||
|             if tbr == 99999: | ||||
|                 formats.extend(self._extract_m3u8_formats( | ||||
|                 # wrong ks(Kaltura Signature) causes 404 Error | ||||
|                 flavor_url = update_url_query(flavor_url, {'ks': ''}) | ||||
|                 m3u8_formats = self._extract_m3u8_formats( | ||||
|                     flavor_url, video_id, 'mp4', | ||||
|                     m3u8_id=flavor_format, fatal=False)) | ||||
|                     m3u8_id=flavor_format, fatal=False) | ||||
|                 for f in m3u8_formats: | ||||
|                     # Apple FairPlay | ||||
|                     if '/fpshls/' in f['url']: | ||||
|                         continue | ||||
|                     formats.append(f) | ||||
|                 continue | ||||
|             format_id = [] | ||||
|             if flavor_format: | ||||
|   | ||||
| @@ -54,12 +54,12 @@ class DramaFeverBaseIE(AMPIE): | ||||
|         request = sanitized_Request( | ||||
|             self._LOGIN_URL, urlencode_postdata(login_form)) | ||||
|         response = self._download_webpage( | ||||
|             request, None, 'Logging in as %s' % username) | ||||
|             request, None, 'Logging in') | ||||
|  | ||||
|         if all(logout_pattern not in response | ||||
|                for logout_pattern in ['href="/accounts/logout/"', '>Log out<']): | ||||
|             error = self._html_search_regex( | ||||
|                 r'(?s)class="hidden-xs prompt"[^>]*>(.+?)<', | ||||
|                 r'(?s)<h\d[^>]+\bclass="hidden-xs prompt"[^>]*>(.+?)</h\d', | ||||
|                 response, 'error message', default=None) | ||||
|             if error: | ||||
|                 raise ExtractorError('Unable to login: %s' % error, expected=True) | ||||
|   | ||||
| @@ -10,7 +10,7 @@ from ..utils import ( | ||||
|  | ||||
|  | ||||
| class DrTuberIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:www\.)?drtuber\.com/(?:video|embed)/(?P<id>\d+)(?:/(?P<display_id>[\w-]+))?' | ||||
|     _VALID_URL = r'https?://(?:(?:www|m)\.)?drtuber\.com/(?:video|embed)/(?P<id>\d+)(?:/(?P<display_id>[\w-]+))?' | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.drtuber.com/video/1740434/hot-perky-blonde-naked-golf', | ||||
|         'md5': '93e680cf2536ad0dfb7e74d94a89facd', | ||||
| @@ -28,6 +28,9 @@ class DrTuberIE(InfoExtractor): | ||||
|     }, { | ||||
|         'url': 'http://www.drtuber.com/embed/489939', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://m.drtuber.com/video/3893529/lingerie-blowjob-from-beautiful-teen', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     @staticmethod | ||||
|   | ||||
| @@ -138,6 +138,7 @@ class DRTVIE(InfoExtractor): | ||||
|                             'tbr': int_or_none(bitrate), | ||||
|                             'ext': link.get('FileFormat'), | ||||
|                             'vcodec': 'none' if kind == 'AudioResource' else None, | ||||
|                             'preference': preference, | ||||
|                         }) | ||||
|                 subtitles_list = asset.get('SubtitlesList') | ||||
|                 if isinstance(subtitles_list, list): | ||||
|   | ||||
| @@ -2,7 +2,9 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..compat import compat_str | ||||
| from ..utils import ( | ||||
|     determine_ext, | ||||
|     int_or_none, | ||||
|     try_get, | ||||
|     unified_timestamp, | ||||
| @@ -17,7 +19,7 @@ class EggheadCourseIE(InfoExtractor): | ||||
|         'url': 'https://egghead.io/courses/professor-frisby-introduces-composable-functional-javascript', | ||||
|         'playlist_count': 29, | ||||
|         'info_dict': { | ||||
|             'id': 'professor-frisby-introduces-composable-functional-javascript', | ||||
|             'id': '72', | ||||
|             'title': 'Professor Frisby Introduces Composable Functional JavaScript', | ||||
|             'description': 're:(?s)^This course teaches the ubiquitous.*You\'ll start composing functionality before you know it.$', | ||||
|         }, | ||||
| @@ -26,14 +28,28 @@ class EggheadCourseIE(InfoExtractor): | ||||
|     def _real_extract(self, url): | ||||
|         playlist_id = self._match_id(url) | ||||
|  | ||||
|         course = self._download_json( | ||||
|             'https://egghead.io/api/v1/series/%s' % playlist_id, playlist_id) | ||||
|         lessons = self._download_json( | ||||
|             'https://egghead.io/api/v1/series/%s/lessons' % playlist_id, | ||||
|             playlist_id, 'Downloading course lessons JSON') | ||||
|  | ||||
|         entries = [ | ||||
|             self.url_result( | ||||
|                 'wistia:%s' % lesson['wistia_id'], ie='Wistia', | ||||
|                 video_id=lesson['wistia_id'], video_title=lesson.get('title')) | ||||
|             for lesson in course['lessons'] if lesson.get('wistia_id')] | ||||
|         entries = [] | ||||
|         for lesson in lessons: | ||||
|             lesson_url = lesson.get('http_url') | ||||
|             if not lesson_url or not isinstance(lesson_url, compat_str): | ||||
|                 continue | ||||
|             lesson_id = lesson.get('id') | ||||
|             if lesson_id: | ||||
|                 lesson_id = compat_str(lesson_id) | ||||
|             entries.append(self.url_result( | ||||
|                 lesson_url, ie=EggheadLessonIE.ie_key(), video_id=lesson_id)) | ||||
|  | ||||
|         course = self._download_json( | ||||
|             'https://egghead.io/api/v1/series/%s' % playlist_id, | ||||
|             playlist_id, 'Downloading course JSON', fatal=False) or {} | ||||
|  | ||||
|         playlist_id = course.get('id') | ||||
|         if playlist_id: | ||||
|             playlist_id = compat_str(playlist_id) | ||||
|  | ||||
|         return self.playlist_result( | ||||
|             entries, playlist_id, course.get('title'), | ||||
| @@ -43,11 +59,12 @@ class EggheadCourseIE(InfoExtractor): | ||||
| class EggheadLessonIE(InfoExtractor): | ||||
|     IE_DESC = 'egghead.io lesson' | ||||
|     IE_NAME = 'egghead:lesson' | ||||
|     _VALID_URL = r'https://egghead\.io/lessons/(?P<id>[^/?#&]+)' | ||||
|     _TEST = { | ||||
|     _VALID_URL = r'https://egghead\.io/(?:api/v1/)?lessons/(?P<id>[^/?#&]+)' | ||||
|     _TESTS = [{ | ||||
|         'url': 'https://egghead.io/lessons/javascript-linear-data-flow-with-container-style-types-box', | ||||
|         'info_dict': { | ||||
|             'id': 'fv5yotjxcg', | ||||
|             'id': '1196', | ||||
|             'display_id': 'javascript-linear-data-flow-with-container-style-types-box', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Create linear data flow with container style types (Box)', | ||||
|             'description': 'md5:9aa2cdb6f9878ed4c39ec09e85a8150e', | ||||
| @@ -60,25 +77,51 @@ class EggheadLessonIE(InfoExtractor): | ||||
|         }, | ||||
|         'params': { | ||||
|             'skip_download': True, | ||||
|             'format': 'bestvideo', | ||||
|         }, | ||||
|     } | ||||
|     }, { | ||||
|         'url': 'https://egghead.io/api/v1/lessons/react-add-redux-to-a-react-application', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         lesson_id = self._match_id(url) | ||||
|         display_id = self._match_id(url) | ||||
|  | ||||
|         lesson = self._download_json( | ||||
|             'https://egghead.io/api/v1/lessons/%s' % lesson_id, lesson_id) | ||||
|             'https://egghead.io/api/v1/lessons/%s' % display_id, display_id) | ||||
|  | ||||
|         lesson_id = compat_str(lesson['id']) | ||||
|         title = lesson['title'] | ||||
|  | ||||
|         formats = [] | ||||
|         for _, format_url in lesson['media_urls'].items(): | ||||
|             if not format_url or not isinstance(format_url, compat_str): | ||||
|                 continue | ||||
|             ext = determine_ext(format_url) | ||||
|             if ext == 'm3u8': | ||||
|                 formats.extend(self._extract_m3u8_formats( | ||||
|                     format_url, lesson_id, 'mp4', entry_protocol='m3u8', | ||||
|                     m3u8_id='hls', fatal=False)) | ||||
|             elif ext == 'mpd': | ||||
|                 formats.extend(self._extract_mpd_formats( | ||||
|                     format_url, lesson_id, mpd_id='dash', fatal=False)) | ||||
|             else: | ||||
|                 formats.append({ | ||||
|                     'url': format_url, | ||||
|                 }) | ||||
|         self._sort_formats(formats) | ||||
|  | ||||
|         return { | ||||
|             '_type': 'url_transparent', | ||||
|             'ie_key': 'Wistia', | ||||
|             'url': 'wistia:%s' % lesson['wistia_id'], | ||||
|             'id': lesson['wistia_id'], | ||||
|             'title': lesson.get('title'), | ||||
|             'id': lesson_id, | ||||
|             'display_id': display_id, | ||||
|             'title': title, | ||||
|             'description': lesson.get('summary'), | ||||
|             'thumbnail': lesson.get('thumb_nail'), | ||||
|             'timestamp': unified_timestamp(lesson.get('published_at')), | ||||
|             'duration': int_or_none(lesson.get('duration')), | ||||
|             'view_count': int_or_none(lesson.get('plays_count')), | ||||
|             'tags': try_get(lesson, lambda x: x['tag_list'], list), | ||||
|             'series': try_get( | ||||
|                 lesson, lambda x: x['series']['title'], compat_str), | ||||
|             'formats': formats, | ||||
|         } | ||||
|   | ||||
							
								
								
									
										133
									
								
								youtube_dl/extractor/ellentube.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								youtube_dl/extractor/ellentube.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| # coding: utf-8 | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..utils import ( | ||||
|     clean_html, | ||||
|     extract_attributes, | ||||
|     float_or_none, | ||||
|     int_or_none, | ||||
|     try_get, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class EllenTubeBaseIE(InfoExtractor): | ||||
|     def _extract_data_config(self, webpage, video_id): | ||||
|         details = self._search_regex( | ||||
|             r'(<[^>]+\bdata-component=(["\'])[Dd]etails.+?></div>)', webpage, | ||||
|             'details') | ||||
|         return self._parse_json( | ||||
|             extract_attributes(details)['data-config'], video_id) | ||||
|  | ||||
|     def _extract_video(self, data, video_id): | ||||
|         title = data['title'] | ||||
|  | ||||
|         formats = [] | ||||
|         duration = None | ||||
|         for entry in data.get('media'): | ||||
|             if entry.get('id') == 'm3u8': | ||||
|                 formats = self._extract_m3u8_formats( | ||||
|                     entry['url'], video_id, 'mp4', | ||||
|                     entry_protocol='m3u8_native', m3u8_id='hls') | ||||
|                 duration = int_or_none(entry.get('duration')) | ||||
|                 break | ||||
|         self._sort_formats(formats) | ||||
|  | ||||
|         def get_insight(kind): | ||||
|             return int_or_none(try_get( | ||||
|                 data, lambda x: x['insight']['%ss' % kind])) | ||||
|  | ||||
|         return { | ||||
|             'extractor_key': EllenTubeIE.ie_key(), | ||||
|             'id': video_id, | ||||
|             'title': title, | ||||
|             'description': data.get('description'), | ||||
|             'duration': duration, | ||||
|             'thumbnail': data.get('thumbnail'), | ||||
|             'timestamp': float_or_none(data.get('publishTime'), scale=1000), | ||||
|             'view_count': get_insight('view'), | ||||
|             'like_count': get_insight('like'), | ||||
|             'formats': formats, | ||||
|         } | ||||
|  | ||||
|  | ||||
| class EllenTubeIE(EllenTubeBaseIE): | ||||
|     _VALID_URL = r'''(?x) | ||||
|                         (?: | ||||
|                             ellentube:| | ||||
|                             https://api-prod\.ellentube\.com/ellenapi/api/item/ | ||||
|                         ) | ||||
|                         (?P<id>[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}) | ||||
|                     ''' | ||||
|     _TESTS = [{ | ||||
|         'url': 'https://api-prod.ellentube.com/ellenapi/api/item/0822171c-3829-43bf-b99f-d77358ae75e3', | ||||
|         'md5': '2fabc277131bddafdd120e0fc0f974c9', | ||||
|         'info_dict': { | ||||
|             'id': '0822171c-3829-43bf-b99f-d77358ae75e3', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Ellen Meets Las Vegas Survivors Jesus Campos and Stephen Schuck', | ||||
|             'description': 'md5:76e3355e2242a78ad9e3858e5616923f', | ||||
|             'thumbnail': r're:^https?://.+?', | ||||
|             'duration': 514, | ||||
|             'timestamp': 1508505120, | ||||
|             'upload_date': '20171020', | ||||
|             'view_count': int, | ||||
|             'like_count': int, | ||||
|         } | ||||
|     }, { | ||||
|         'url': 'ellentube:734a3353-f697-4e79-9ca9-bfc3002dc1e0', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         video_id = self._match_id(url) | ||||
|         data = self._download_json( | ||||
|             'https://api-prod.ellentube.com/ellenapi/api/item/%s' % video_id, | ||||
|             video_id) | ||||
|         return self._extract_video(data, video_id) | ||||
|  | ||||
|  | ||||
| class EllenTubeVideoIE(EllenTubeBaseIE): | ||||
|     _VALID_URL = r'https?://(?:www\.)?ellentube\.com/video/(?P<id>.+?)\.html' | ||||
|     _TEST = { | ||||
|         'url': 'https://www.ellentube.com/video/ellen-meets-las-vegas-survivors-jesus-campos-and-stephen-schuck.html', | ||||
|         'only_matching': True, | ||||
|     } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         display_id = self._match_id(url) | ||||
|         webpage = self._download_webpage(url, display_id) | ||||
|         video_id = self._extract_data_config(webpage, display_id)['id'] | ||||
|         return self.url_result( | ||||
|             'ellentube:%s' % video_id, ie=EllenTubeIE.ie_key(), | ||||
|             video_id=video_id) | ||||
|  | ||||
|  | ||||
| class EllenTubePlaylistIE(EllenTubeBaseIE): | ||||
|     _VALID_URL = r'https?://(?:www\.)?ellentube\.com/(?:episode|studios)/(?P<id>.+?)\.html' | ||||
|     _TESTS = [{ | ||||
|         'url': 'https://www.ellentube.com/episode/dax-shepard-jordan-fisher-haim.html', | ||||
|         'info_dict': { | ||||
|             'id': 'dax-shepard-jordan-fisher-haim', | ||||
|             'title': "Dax Shepard, 'DWTS' Team Jordan Fisher & Lindsay Arnold, HAIM", | ||||
|             'description': 'md5:bfc982194dabb3f4e325e43aa6b2e21c', | ||||
|         }, | ||||
|         'playlist_count': 6, | ||||
|     }, { | ||||
|         'url': 'https://www.ellentube.com/studios/macey-goes-rving0.html', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         display_id = self._match_id(url) | ||||
|         webpage = self._download_webpage(url, display_id) | ||||
|         data = self._extract_data_config(webpage, display_id)['data'] | ||||
|         feed = self._download_json( | ||||
|             'https://api-prod.ellentube.com/ellenapi/api/feed/?%s' | ||||
|             % data['filter'], display_id) | ||||
|         entries = [ | ||||
|             self._extract_video(elem, elem['id']) | ||||
|             for elem in feed if elem.get('type') == 'VIDEO' and elem.get('id')] | ||||
|         return self.playlist_result( | ||||
|             entries, display_id, data.get('title'), | ||||
|             clean_html(data.get('description'))) | ||||
| @@ -1,101 +0,0 @@ | ||||
| # coding: utf-8 | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from .kaltura import KalturaIE | ||||
| from ..utils import NO_DEFAULT | ||||
|  | ||||
|  | ||||
| class EllenTVIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:www\.)?(?:ellentv|ellentube)\.com/videos/(?P<id>[a-z0-9_-]+)' | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.ellentv.com/videos/0-ipq1gsai/', | ||||
|         'md5': '4294cf98bc165f218aaa0b89e0fd8042', | ||||
|         'info_dict': { | ||||
|             'id': '0_ipq1gsai', | ||||
|             'ext': 'mov', | ||||
|             'title': 'Fast Fingers of Fate', | ||||
|             'description': 'md5:3539013ddcbfa64b2a6d1b38d910868a', | ||||
|             'timestamp': 1428035648, | ||||
|             'upload_date': '20150403', | ||||
|             'uploader_id': 'batchUser', | ||||
|         }, | ||||
|     }, { | ||||
|         # not available via http://widgets.ellentube.com/ | ||||
|         'url': 'http://www.ellentv.com/videos/1-szkgu2m2/', | ||||
|         'info_dict': { | ||||
|             'id': '1_szkgu2m2', | ||||
|             'ext': 'flv', | ||||
|             'title': "Ellen's Amazingly Talented Audience", | ||||
|             'description': 'md5:86ff1e376ff0d717d7171590e273f0a5', | ||||
|             'timestamp': 1255140900, | ||||
|             'upload_date': '20091010', | ||||
|             'uploader_id': 'ellenkaltura@gmail.com', | ||||
|         }, | ||||
|         'params': { | ||||
|             'skip_download': True, | ||||
|         }, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         video_id = self._match_id(url) | ||||
|  | ||||
|         URLS = ('http://widgets.ellentube.com/videos/%s' % video_id, url) | ||||
|  | ||||
|         for num, url_ in enumerate(URLS, 1): | ||||
|             webpage = self._download_webpage( | ||||
|                 url_, video_id, fatal=num == len(URLS)) | ||||
|  | ||||
|             default = NO_DEFAULT if num == len(URLS) else None | ||||
|  | ||||
|             partner_id = self._search_regex( | ||||
|                 r"var\s+partnerId\s*=\s*'([^']+)", webpage, 'partner id', | ||||
|                 default=default) | ||||
|  | ||||
|             kaltura_id = self._search_regex( | ||||
|                 [r'id="kaltura_player_([^"]+)"', | ||||
|                  r"_wb_entry_id\s*:\s*'([^']+)", | ||||
|                  r'data-kaltura-entry-id="([^"]+)'], | ||||
|                 webpage, 'kaltura id', default=default) | ||||
|  | ||||
|             if partner_id and kaltura_id: | ||||
|                 break | ||||
|  | ||||
|         return self.url_result('kaltura:%s:%s' % (partner_id, kaltura_id), KalturaIE.ie_key()) | ||||
|  | ||||
|  | ||||
| class EllenTVClipsIE(InfoExtractor): | ||||
|     IE_NAME = 'EllenTV:clips' | ||||
|     _VALID_URL = r'https?://(?:www\.)?ellentv\.com/episodes/(?P<id>[a-z0-9_-]+)' | ||||
|     _TEST = { | ||||
|         'url': 'http://www.ellentv.com/episodes/meryl-streep-vanessa-hudgens/', | ||||
|         'info_dict': { | ||||
|             'id': 'meryl-streep-vanessa-hudgens', | ||||
|             'title': 'Meryl Streep, Vanessa Hudgens', | ||||
|         }, | ||||
|         'playlist_mincount': 5, | ||||
|     } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         playlist_id = self._match_id(url) | ||||
|  | ||||
|         webpage = self._download_webpage(url, playlist_id) | ||||
|         playlist = self._extract_playlist(webpage, playlist_id) | ||||
|  | ||||
|         return { | ||||
|             '_type': 'playlist', | ||||
|             'id': playlist_id, | ||||
|             'title': self._og_search_title(webpage), | ||||
|             'entries': self._extract_entries(playlist) | ||||
|         } | ||||
|  | ||||
|     def _extract_playlist(self, webpage, playlist_id): | ||||
|         json_string = self._search_regex(r'playerView.addClips\(\[\{(.*?)\}\]\);', webpage, 'json') | ||||
|         return self._parse_json('[{' + json_string + '}]', playlist_id) | ||||
|  | ||||
|     def _extract_entries(self, playlist): | ||||
|         return [ | ||||
|             self.url_result( | ||||
|                 'kaltura:%s:%s' % (item['kaltura_partner_id'], item['kaltura_entry_id']), | ||||
|                 KalturaIE.ie_key(), video_id=item['kaltura_entry_id']) | ||||
|             for item in playlist] | ||||
| @@ -15,7 +15,7 @@ from ..utils import ( | ||||
|  | ||||
|  | ||||
| class EpornerIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:www\.)?eporner\.com/hd-porn/(?P<id>\w+)(?:/(?P<display_id>[\w-]+))?' | ||||
|     _VALID_URL = r'https?://(?:www\.)?eporner\.com/(?:hd-porn|embed)/(?P<id>\w+)(?:/(?P<display_id>[\w-]+))?' | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.eporner.com/hd-porn/95008/Infamous-Tiffany-Teen-Strip-Tease-Video/', | ||||
|         'md5': '39d486f046212d8e1b911c52ab4691f8', | ||||
| @@ -35,6 +35,9 @@ class EpornerIE(InfoExtractor): | ||||
|     }, { | ||||
|         'url': 'http://www.eporner.com/hd-porn/3YRUtzMcWn0', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.eporner.com/hd-porn/3YRUtzMcWn0', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|   | ||||
| @@ -127,7 +127,10 @@ from .bloomberg import BloombergIE | ||||
| from .bokecc import BokeCCIE | ||||
| from .bostonglobe import BostonGlobeIE | ||||
| from .bpb import BpbIE | ||||
| from .br import BRIE | ||||
| from .br import ( | ||||
|     BRIE, | ||||
|     BRMediathekIE, | ||||
| ) | ||||
| from .bravotv import BravoTVIE | ||||
| from .breakcom import BreakIE | ||||
| from .brightcove import ( | ||||
| @@ -135,10 +138,7 @@ from .brightcove import ( | ||||
|     BrightcoveNewIE, | ||||
| ) | ||||
| from .buzzfeed import BuzzFeedIE | ||||
| from .byutv import ( | ||||
|     BYUtvIE, | ||||
|     BYUtvEventIE, | ||||
| ) | ||||
| from .byutv import BYUtvIE | ||||
| from .c56 import C56IE | ||||
| from .camdemy import ( | ||||
|     CamdemyIE, | ||||
| @@ -150,6 +150,7 @@ from .canalc2 import Canalc2IE | ||||
| from .canvas import ( | ||||
|     CanvasIE, | ||||
|     CanvasEenIE, | ||||
|     VrtNUIE, | ||||
| ) | ||||
| from .carambatv import ( | ||||
|     CarambaTVIE, | ||||
| @@ -242,11 +243,10 @@ from .dailymotion import ( | ||||
|     DailymotionIE, | ||||
|     DailymotionPlaylistIE, | ||||
|     DailymotionUserIE, | ||||
|     DailymotionCloudIE, | ||||
| ) | ||||
| from .daisuki import ( | ||||
|     DaisukiIE, | ||||
|     DaisukiPlaylistIE, | ||||
|     DaisukiMottoIE, | ||||
|     DaisukiMottoPlaylistIE, | ||||
| ) | ||||
| from .daum import ( | ||||
|     DaumIE, | ||||
| @@ -308,9 +308,10 @@ from .ehow import EHowIE | ||||
| from .eighttracks import EightTracksIE | ||||
| from .einthusan import EinthusanIE | ||||
| from .eitb import EitbIE | ||||
| from .ellentv import ( | ||||
|     EllenTVIE, | ||||
|     EllenTVClipsIE, | ||||
| from .ellentube import ( | ||||
|     EllenTubeIE, | ||||
|     EllenTubeVideoIE, | ||||
|     EllenTubePlaylistIE, | ||||
| ) | ||||
| from .elpais import ElPaisIE | ||||
| from .embedly import EmbedlyIE | ||||
| @@ -343,11 +344,9 @@ from .filmon import ( | ||||
|     FilmOnIE, | ||||
|     FilmOnChannelIE, | ||||
| ) | ||||
| from .firstpost import FirstpostIE | ||||
| from .firsttv import FirstTVIE | ||||
| from .fivemin import FiveMinIE | ||||
| from .fivetv import FiveTVIE | ||||
| from .fktv import FKTVIE | ||||
| from .flickr import FlickrIE | ||||
| from .flipagram import FlipagramIE | ||||
| from .folketinget import FolketingetIE | ||||
| @@ -374,7 +373,7 @@ from .francetv import ( | ||||
|     FranceTVIE, | ||||
|     FranceTVEmbedIE, | ||||
|     FranceTVInfoIE, | ||||
|     GenerationQuoiIE, | ||||
|     GenerationWhatIE, | ||||
|     CultureboxIE, | ||||
| ) | ||||
| from .freesound import FreesoundIE | ||||
| @@ -390,7 +389,6 @@ from .gameone import ( | ||||
|     GameOneIE, | ||||
|     GameOnePlaylistIE, | ||||
| ) | ||||
| from .gamersyde import GamersydeIE | ||||
| from .gamespot import GameSpotIE | ||||
| from .gamestar import GameStarIE | ||||
| from .gaskrank import GaskrankIE | ||||
| @@ -431,7 +429,10 @@ from .hitbox import HitboxIE, HitboxLiveIE | ||||
| from .hitrecord import HitRecordIE | ||||
| from .hornbunny import HornBunnyIE | ||||
| from .hotnewhiphop import HotNewHipHopIE | ||||
| from .hotstar import HotStarIE | ||||
| from .hotstar import ( | ||||
|     HotStarIE, | ||||
|     HotStarPlaylistIE, | ||||
| ) | ||||
| from .howcast import HowcastIE | ||||
| from .howstuffworks import HowStuffWorksIE | ||||
| from .hrti import ( | ||||
| @@ -568,6 +569,7 @@ from .mangomolo import ( | ||||
|     MangomoloLiveIE, | ||||
| ) | ||||
| from .manyvids import ManyVidsIE | ||||
| from .massengeschmacktv import MassengeschmackTVIE | ||||
| from .matchtv import MatchTVIE | ||||
| from .mdr import MDRIE | ||||
| from .mediaset import MediasetIE | ||||
| @@ -623,7 +625,6 @@ from .mwave import MwaveIE, MwaveMeetGreetIE | ||||
| from .myspace import MySpaceIE, MySpaceAlbumIE | ||||
| from .myspass import MySpassIE | ||||
| from .myvi import MyviIE | ||||
| from .myvideo import MyVideoIE | ||||
| from .myvidster import MyVidsterIE | ||||
| from .nationalgeographic import ( | ||||
|     NationalGeographicVideoIE, | ||||
| @@ -685,6 +686,7 @@ from .nhl import ( | ||||
| ) | ||||
| from .nick import ( | ||||
|     NickIE, | ||||
|     NickBrIE, | ||||
|     NickDeIE, | ||||
|     NickNightIE, | ||||
|     NickRuIE, | ||||
| @@ -717,10 +719,6 @@ from .nowness import ( | ||||
|     NownessPlaylistIE, | ||||
|     NownessSeriesIE, | ||||
| ) | ||||
| from .nowtv import ( | ||||
|     NowTVIE, | ||||
|     NowTVListIE, | ||||
| ) | ||||
| from .noz import NozIE | ||||
| from .npo import ( | ||||
|     AndereTijdenIE, | ||||
| @@ -786,6 +784,7 @@ from .patreon import PatreonIE | ||||
| from .pbs import PBSIE | ||||
| from .pearvideo import PearVideoIE | ||||
| from .people import PeopleIE | ||||
| from .performgroup import PerformGroupIE | ||||
| from .periscope import ( | ||||
|     PeriscopeIE, | ||||
|     PeriscopeUserIE, | ||||
| @@ -852,6 +851,7 @@ from .radiofrance import RadioFranceIE | ||||
| from .rai import ( | ||||
|     RaiPlayIE, | ||||
|     RaiPlayLiveIE, | ||||
|     RaiPlayPlaylistIE, | ||||
|     RaiIE, | ||||
| ) | ||||
| from .rbmaradio import RBMARadioIE | ||||
| @@ -925,8 +925,13 @@ from .seeker import SeekerIE | ||||
| from .senateisvp import SenateISVPIE | ||||
| from .sendtonews import SendtoNewsIE | ||||
| from .servingsys import ServingSysIE | ||||
| from .servus import ServusIE | ||||
| from .sevenplus import SevenPlusIE | ||||
| from .sexu import SexuIE | ||||
| from .shahid import ShahidIE | ||||
| from .shahid import ( | ||||
|     ShahidIE, | ||||
|     ShahidShowIE, | ||||
| ) | ||||
| from .shared import ( | ||||
|     SharedIE, | ||||
|     VivoIE, | ||||
| @@ -994,6 +999,7 @@ from .streamango import StreamangoIE | ||||
| from .streamcloud import StreamcloudIE | ||||
| from .streamcz import StreamCZIE | ||||
| from .streetvoice import StreetVoiceIE | ||||
| from .stretchinternet import StretchInternetIE | ||||
| from .sunporno import SunPornoIE | ||||
| from .svt import ( | ||||
|     SVTIE, | ||||
| @@ -1096,6 +1102,10 @@ from .tvigle import TvigleIE | ||||
| from .tvland import TVLandIE | ||||
| from .tvn24 import TVN24IE | ||||
| from .tvnoe import TVNoeIE | ||||
| from .tvnow import ( | ||||
|     TVNowIE, | ||||
|     TVNowListIE, | ||||
| ) | ||||
| from .tvp import ( | ||||
|     TVPEmbedIE, | ||||
|     TVPIE, | ||||
| @@ -1109,10 +1119,6 @@ from .tvplayer import TVPlayerIE | ||||
| from .tweakers import TweakersIE | ||||
| from .twentyfourvideo import TwentyFourVideoIE | ||||
| from .twentymin import TwentyMinutenIE | ||||
| from .twentytwotracks import ( | ||||
|     TwentyTwoTracksIE, | ||||
|     TwentyTwoTracksGenreIE | ||||
| ) | ||||
| from .twitch import ( | ||||
|     TwitchVideoIE, | ||||
|     TwitchChapterIE, | ||||
| @@ -1138,6 +1144,7 @@ from .udn import UDNEmbedIE | ||||
| from .uktvplay import UKTVPlayIE | ||||
| from .digiteka import DigitekaIE | ||||
| from .unistra import UnistraIE | ||||
| from .unity import UnityIE | ||||
| from .uol import UOLIE | ||||
| from .uplynk import ( | ||||
|     UplynkIE, | ||||
| @@ -1333,6 +1340,11 @@ from .youku import ( | ||||
|     YoukuIE, | ||||
|     YoukuShowIE, | ||||
| ) | ||||
| from .younow import ( | ||||
|     YouNowLiveIE, | ||||
|     YouNowChannelIE, | ||||
|     YouNowMomentIE, | ||||
| ) | ||||
| from .youporn import YouPornIE | ||||
| from .yourupload import YourUploadIE | ||||
| from .youtube import ( | ||||
|   | ||||
| @@ -1,7 +1,10 @@ | ||||
| # coding: utf-8 | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import re | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..compat import compat_etree_fromstring | ||||
| from ..utils import ( | ||||
|     xpath_element, | ||||
|     xpath_text, | ||||
| @@ -43,10 +46,15 @@ class FazIE(InfoExtractor): | ||||
|  | ||||
|         webpage = self._download_webpage(url, video_id) | ||||
|         description = self._og_search_description(webpage) | ||||
|         config_xml_url = self._search_regex( | ||||
|             r'videoXMLURL\s*=\s*"([^"]+)', webpage, 'config xml url') | ||||
|         config = self._download_xml( | ||||
|             config_xml_url, video_id, 'Downloading config xml') | ||||
|         media = self._html_search_regex( | ||||
|             r"data-videojs-media='([^']+)", | ||||
|             webpage, 'media') | ||||
|         if media == 'extern': | ||||
|             perform_url = self._search_regex( | ||||
|                 r"<iframe[^>]+?src='((?:http:)?//player\.performgroup\.com/eplayer/eplayer\.html#/?[0-9a-f]{26}\.[0-9a-z]{26})", | ||||
|                 webpage, 'perform url') | ||||
|             return self.url_result(perform_url) | ||||
|         config = compat_etree_fromstring(media) | ||||
|  | ||||
|         encodings = xpath_element(config, 'ENCODINGS', 'encodings', True) | ||||
|         formats = [] | ||||
| @@ -55,12 +63,24 @@ class FazIE(InfoExtractor): | ||||
|             if encoding is not None: | ||||
|                 encoding_url = xpath_text(encoding, 'FILENAME') | ||||
|                 if encoding_url: | ||||
|                     formats.append({ | ||||
|                     tbr = xpath_text(encoding, 'AVERAGEBITRATE', 1000) | ||||
|                     if tbr: | ||||
|                         tbr = int_or_none(tbr.replace(',', '.')) | ||||
|                     f = { | ||||
|                         'url': encoding_url, | ||||
|                         'format_id': code.lower(), | ||||
|                         'quality': pref, | ||||
|                         'tbr': int_or_none(xpath_text(encoding, 'AVERAGEBITRATE')), | ||||
|                     }) | ||||
|                         'tbr': tbr, | ||||
|                         'vcodec': xpath_text(encoding, 'CODEC'), | ||||
|                     } | ||||
|                     mobj = re.search(r'(\d+)x(\d+)_(\d+)\.mp4', encoding_url) | ||||
|                     if mobj: | ||||
|                         f.update({ | ||||
|                             'width': int(mobj.group(1)), | ||||
|                             'height': int(mobj.group(2)), | ||||
|                             'tbr': tbr or int(mobj.group(3)), | ||||
|                         }) | ||||
|                     formats.append(f) | ||||
|         self._sort_formats(formats) | ||||
|  | ||||
|         return { | ||||
|   | ||||
| @@ -2,7 +2,10 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..compat import compat_urlparse | ||||
| from ..utils import ( | ||||
|     int_or_none, | ||||
|     float_or_none, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class FczenitIE(InfoExtractor): | ||||
| @@ -14,6 +17,8 @@ class FczenitIE(InfoExtractor): | ||||
|             'id': '41044', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Так пишется история: казанский разгром ЦСКА на «Зенит-ТВ»', | ||||
|             'timestamp': 1462283735, | ||||
|             'upload_date': '20160503', | ||||
|         }, | ||||
|     } | ||||
|  | ||||
| @@ -21,28 +26,31 @@ class FczenitIE(InfoExtractor): | ||||
|         video_id = self._match_id(url) | ||||
|         webpage = self._download_webpage(url, video_id) | ||||
|  | ||||
|         video_title = self._html_search_regex( | ||||
|             r'<[^>]+class=\"photoalbum__title\">([^<]+)', webpage, 'title') | ||||
|         msi_id = self._search_regex( | ||||
|             r"(?s)config\s*=\s*{.+?video_id\s*:\s*'([^']+)'", webpage, 'msi id') | ||||
|  | ||||
|         video_items = self._parse_json(self._search_regex( | ||||
|             r'arrPath\s*=\s*JSON\.parse\(\'(.+)\'\)', webpage, 'video items'), | ||||
|             video_id) | ||||
|  | ||||
|         def merge_dicts(*dicts): | ||||
|             ret = {} | ||||
|             for a_dict in dicts: | ||||
|                 ret.update(a_dict) | ||||
|             return ret | ||||
|         msi_data = self._download_json( | ||||
|             'http://player.fc-zenit.ru/msi/video', msi_id, query={ | ||||
|                 'video': msi_id, | ||||
|             })['data'] | ||||
|         title = msi_data['name'] | ||||
|  | ||||
|         formats = [{ | ||||
|             'url': compat_urlparse.urljoin(url, video_url), | ||||
|             'tbr': int(tbr), | ||||
|         } for tbr, video_url in merge_dicts(*video_items).items()] | ||||
|             'format_id': q.get('label'), | ||||
|             'url': q['url'], | ||||
|             'height': int_or_none(q.get('label')), | ||||
|         } for q in msi_data['qualities'] if q.get('url')] | ||||
|  | ||||
|         self._sort_formats(formats) | ||||
|  | ||||
|         tags = [tag['label'] for tag in msi_data.get('tags', []) if tag.get('label')] | ||||
|  | ||||
|         return { | ||||
|             'id': video_id, | ||||
|             'title': video_title, | ||||
|             'title': title, | ||||
|             'thumbnail': msi_data.get('preview'), | ||||
|             'formats': formats, | ||||
|             'duration': float_or_none(msi_data.get('duration')), | ||||
|             'timestamp': int_or_none(msi_data.get('date')), | ||||
|             'tags': tags, | ||||
|         } | ||||
|   | ||||
| @@ -1,50 +0,0 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| from .common import InfoExtractor | ||||
|  | ||||
|  | ||||
| class FirstpostIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:www\.)?firstpost\.com/[^/]+/.*-(?P<id>[0-9]+)\.html' | ||||
|  | ||||
|     _TEST = { | ||||
|         'url': 'http://www.firstpost.com/india/india-to-launch-indigenous-aircraft-carrier-monday-1025403.html', | ||||
|         'md5': 'ee9114957692f01fb1263ed87039112a', | ||||
|         'info_dict': { | ||||
|             'id': '1025403', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'India to launch indigenous aircraft carrier INS Vikrant today', | ||||
|             'description': 'md5:feef3041cb09724e0bdc02843348f5f4', | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         video_id = self._match_id(url) | ||||
|         page = self._download_webpage(url, video_id) | ||||
|  | ||||
|         title = self._html_search_meta('twitter:title', page, 'title', fatal=True) | ||||
|         description = self._html_search_meta('twitter:description', page, 'title') | ||||
|  | ||||
|         data = self._download_xml( | ||||
|             'http://www.firstpost.com/getvideoxml-%s.xml' % video_id, video_id, | ||||
|             'Downloading video XML') | ||||
|  | ||||
|         item = data.find('./playlist/item') | ||||
|         thumbnail = item.find('./image').text | ||||
|  | ||||
|         formats = [ | ||||
|             { | ||||
|                 'url': details.find('./file').text, | ||||
|                 'format_id': details.find('./label').text.strip(), | ||||
|                 'width': int(details.find('./width').text.strip()), | ||||
|                 'height': int(details.find('./height').text.strip()), | ||||
|             } for details in item.findall('./source/file_details') if details.find('./file').text | ||||
|         ] | ||||
|         self._sort_formats(formats) | ||||
|  | ||||
|         return { | ||||
|             'id': video_id, | ||||
|             'title': title, | ||||
|             'description': description, | ||||
|             'thumbnail': thumbnail, | ||||
|             'formats': formats, | ||||
|         } | ||||
| @@ -1,51 +0,0 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..utils import ( | ||||
|     clean_html, | ||||
|     determine_ext, | ||||
|     js_to_json, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class FKTVIE(InfoExtractor): | ||||
|     IE_NAME = 'fernsehkritik.tv' | ||||
|     _VALID_URL = r'https?://(?:www\.)?fernsehkritik\.tv/folge-(?P<id>[0-9]+)(?:/.*)?' | ||||
|  | ||||
|     _TEST = { | ||||
|         'url': 'http://fernsehkritik.tv/folge-1', | ||||
|         'md5': '21f0b0c99bce7d5b524eb1b17b1c6d79', | ||||
|         'info_dict': { | ||||
|             'id': '1', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Folge 1 vom 10. April 2007', | ||||
|             'thumbnail': r're:^https?://.*\.jpg$', | ||||
|         }, | ||||
|     } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         episode = self._match_id(url) | ||||
|  | ||||
|         webpage = self._download_webpage( | ||||
|             'http://fernsehkritik.tv/folge-%s/play' % episode, episode) | ||||
|         title = clean_html(self._html_search_regex( | ||||
|             '<h3>([^<]+)</h3>', webpage, 'title')) | ||||
|         thumbnail = self._search_regex(r'POSTER\s*=\s*"([^"]+)', webpage, 'thumbnail', fatal=False) | ||||
|         sources = self._parse_json(self._search_regex(r'(?s)MEDIA\s*=\s*(\[.+?\]);', webpage, 'media'), episode, js_to_json) | ||||
|  | ||||
|         formats = [] | ||||
|         for source in sources: | ||||
|             furl = source.get('src') | ||||
|             if furl: | ||||
|                 formats.append({ | ||||
|                     'url': furl, | ||||
|                     'format_id': determine_ext(furl), | ||||
|                 }) | ||||
|         self._sort_formats(formats) | ||||
|  | ||||
|         return { | ||||
|             'id': episode, | ||||
|             'title': title, | ||||
|             'formats': formats, | ||||
|             'thumbnail': thumbnail, | ||||
|         } | ||||
| @@ -11,6 +11,7 @@ from ..utils import ( | ||||
|     parse_duration, | ||||
|     try_get, | ||||
|     unified_timestamp, | ||||
|     update_url_query, | ||||
| ) | ||||
|  | ||||
|  | ||||
| @@ -62,7 +63,8 @@ class FOXIE(AdobePassIE): | ||||
|         duration = int_or_none(video.get('durationInSeconds')) or int_or_none( | ||||
|             video.get('duration')) or parse_duration(video.get('duration')) | ||||
|         timestamp = unified_timestamp(video.get('datePublished')) | ||||
|         age_limit = parse_age_limit(video.get('contentRating')) | ||||
|         rating = video.get('contentRating') | ||||
|         age_limit = parse_age_limit(rating) | ||||
|  | ||||
|         data = try_get( | ||||
|             video, lambda x: x['trackingData']['properties'], dict) or {} | ||||
| @@ -77,8 +79,24 @@ class FOXIE(AdobePassIE): | ||||
|         release_year = int_or_none(video.get('releaseYear')) | ||||
|  | ||||
|         if data.get('authRequired'): | ||||
|             # TODO: AP | ||||
|             pass | ||||
|             resource = self._get_mvpd_resource( | ||||
|                 'fbc-fox', title, video.get('guid'), rating) | ||||
|             release_url = update_url_query( | ||||
|                 release_url, { | ||||
|                     'auth': self._extract_mvpd_auth( | ||||
|                         url, video_id, 'fbc-fox', resource) | ||||
|                 }) | ||||
|  | ||||
|         subtitles = {} | ||||
|         for doc_rel in video.get('documentReleases', []): | ||||
|             rel_url = doc_rel.get('url') | ||||
|             if not url or doc_rel.get('format') != 'SCC': | ||||
|                 continue | ||||
|             subtitles['en'] = [{ | ||||
|                 'url': rel_url, | ||||
|                 'ext': 'scc', | ||||
|             }] | ||||
|             break | ||||
|  | ||||
|         info = { | ||||
|             'id': video_id, | ||||
| @@ -93,6 +111,7 @@ class FOXIE(AdobePassIE): | ||||
|             'episode': episode, | ||||
|             'episode_number': episode_number, | ||||
|             'release_year': release_year, | ||||
|             'subtitles': subtitles, | ||||
|         } | ||||
|  | ||||
|         urlh = self._request_webpage(HEADRequest(release_url), video_id) | ||||
|   | ||||
| @@ -2,7 +2,6 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| from .anvato import AnvatoIE | ||||
| from ..utils import js_to_json | ||||
|  | ||||
|  | ||||
| class FOX9IE(AnvatoIE): | ||||
| @@ -34,9 +33,9 @@ class FOX9IE(AnvatoIE): | ||||
|  | ||||
|         video_id = self._parse_json( | ||||
|             self._search_regex( | ||||
|                 r'AnvatoPlaylist\s*\(\s*(\[.+?\])\s*\)\s*;', | ||||
|                 r"this\.videosJson\s*=\s*'(\[.+?\])';", | ||||
|                 webpage, 'anvato playlist'), | ||||
|             video_id, transform_source=js_to_json)[0]['video'] | ||||
|             video_id)[0]['video'] | ||||
|  | ||||
|         return self._get_anvato_videos( | ||||
|             'anvato_epfox_app_web_prod_b3373168e12f423f41504f207000188daf88251b', | ||||
|   | ||||
| @@ -3,7 +3,6 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import re | ||||
| import json | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..compat import compat_urlparse | ||||
| @@ -14,10 +13,7 @@ from ..utils import ( | ||||
|     parse_duration, | ||||
|     determine_ext, | ||||
| ) | ||||
| from .dailymotion import ( | ||||
|     DailymotionIE, | ||||
|     DailymotionCloudIE, | ||||
| ) | ||||
| from .dailymotion import DailymotionIE | ||||
|  | ||||
|  | ||||
| class FranceTVBaseInfoExtractor(InfoExtractor): | ||||
| @@ -291,10 +287,6 @@ class FranceTVInfoIE(FranceTVBaseInfoExtractor): | ||||
|         page_title = mobj.group('title') | ||||
|         webpage = self._download_webpage(url, page_title) | ||||
|  | ||||
|         dmcloud_url = DailymotionCloudIE._extract_dmcloud_url(webpage) | ||||
|         if dmcloud_url: | ||||
|             return self.url_result(dmcloud_url, DailymotionCloudIE.ie_key()) | ||||
|  | ||||
|         dailymotion_urls = DailymotionIE._extract_urls(webpage) | ||||
|         if dailymotion_urls: | ||||
|             return self.playlist_result([ | ||||
| @@ -308,31 +300,32 @@ class FranceTVInfoIE(FranceTVBaseInfoExtractor): | ||||
|         return self._extract_video(video_id, catalogue) | ||||
|  | ||||
|  | ||||
| class GenerationQuoiIE(InfoExtractor): | ||||
|     IE_NAME = 'france2.fr:generation-quoi' | ||||
|     _VALID_URL = r'https?://generation-quoi\.france2\.fr/portrait/(?P<id>[^/?#]+)' | ||||
| class GenerationWhatIE(InfoExtractor): | ||||
|     IE_NAME = 'france2.fr:generation-what' | ||||
|     _VALID_URL = r'https?://generation-what\.francetv\.fr/[^/]+/video/(?P<id>[^/?#]+)' | ||||
|  | ||||
|     _TEST = { | ||||
|         'url': 'http://generation-quoi.france2.fr/portrait/garde-a-vous', | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://generation-what.francetv.fr/portrait/video/present-arms', | ||||
|         'info_dict': { | ||||
|             'id': 'k7FJX8VBcvvLmX4wA5Q', | ||||
|             'id': 'wtvKYUG45iw', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Génération Quoi - Garde à Vous', | ||||
|             'uploader': 'Génération Quoi', | ||||
|             'title': 'Generation What - Garde à vous - FRA', | ||||
|             'uploader': 'Generation What', | ||||
|             'uploader_id': 'UCHH9p1eetWCgt4kXBYCb3_w', | ||||
|             'upload_date': '20160411', | ||||
|         }, | ||||
|         'params': { | ||||
|             # It uses Dailymotion | ||||
|             'skip_download': True, | ||||
|         }, | ||||
|     } | ||||
|     }, { | ||||
|         'url': 'http://generation-what.francetv.fr/europe/video/present-arms', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         display_id = self._match_id(url) | ||||
|         info_url = compat_urlparse.urljoin(url, '/medias/video/%s.json' % display_id) | ||||
|         info_json = self._download_webpage(info_url, display_id) | ||||
|         info = json.loads(info_json) | ||||
|         return self.url_result('http://www.dailymotion.com/video/%s' % info['id'], | ||||
|                                ie='Dailymotion') | ||||
|         webpage = self._download_webpage(url, display_id) | ||||
|         youtube_id = self._search_regex( | ||||
|             r"window\.videoURL\s*=\s*'([0-9A-Za-z_-]{11})';", | ||||
|             webpage, 'youtube id') | ||||
|         return self.url_result(youtube_id, 'Youtube', youtube_id) | ||||
|  | ||||
|  | ||||
| class CultureboxIE(FranceTVBaseInfoExtractor): | ||||
| @@ -363,6 +356,7 @@ class CultureboxIE(FranceTVBaseInfoExtractor): | ||||
|             raise ExtractorError('Video %s is not available' % name, expected=True) | ||||
|  | ||||
|         video_id, catalogue = self._search_regex( | ||||
|             r'"http://videos\.francetv\.fr/video/([^@]+@[^"]+)"', webpage, 'video id').split('@') | ||||
|             r'["\'>]https?://videos\.francetv\.fr/video/([^@]+@.+?)["\'<]', | ||||
|             webpage, 'video id').split('@') | ||||
|  | ||||
|         return self._extract_video(video_id, catalogue) | ||||
|   | ||||
| @@ -1,37 +1,34 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import re | ||||
| import json | ||||
|  | ||||
| from .common import InfoExtractor | ||||
|  | ||||
|  | ||||
| class FreespeechIE(InfoExtractor): | ||||
|     IE_NAME = 'freespeech.org' | ||||
|     _VALID_URL = r'https?://(?:www\.)?freespeech\.org/video/(?P<title>.+)' | ||||
|     _VALID_URL = r'https?://(?:www\.)?freespeech\.org/stories/(?P<id>.+)' | ||||
|     _TEST = { | ||||
|         'add_ie': ['Youtube'], | ||||
|         'url': 'https://www.freespeech.org/video/obama-romney-campaign-colorado-ahead-debate-0', | ||||
|         'url': 'http://www.freespeech.org/stories/fcc-announces-net-neutrality-rollback-whats-stake/', | ||||
|         'info_dict': { | ||||
|             'id': 'poKsVCZ64uU', | ||||
|             'ext': 'webm', | ||||
|             'title': 'Obama, Romney Campaign in Colorado Ahead of Debate', | ||||
|             'description': 'Obama, Romney Campaign in Colorado Ahead of Debate', | ||||
|             'uploader': 'freespeechtv', | ||||
|             'id': 'waRk6IPqyWM', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'What\'s At Stake - Net Neutrality Special', | ||||
|             'description': 'Presented by MNN and FSTV', | ||||
|             'upload_date': '20170728', | ||||
|             'uploader_id': 'freespeechtv', | ||||
|             'upload_date': '20121002', | ||||
|             'uploader': 'freespeechtv', | ||||
|         }, | ||||
|     } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         mobj = re.match(self._VALID_URL, url) | ||||
|         title = mobj.group('title') | ||||
|         webpage = self._download_webpage(url, title) | ||||
|         info_json = self._search_regex(r'jQuery\.extend\(Drupal\.settings, ({.*?})\);', webpage, 'info') | ||||
|         info = json.loads(info_json) | ||||
|         display_id = self._match_id(url) | ||||
|         webpage = self._download_webpage(url, display_id) | ||||
|         youtube_url = self._search_regex( | ||||
|             r'data-video-url="([^"]+)"', | ||||
|             webpage, 'youtube url') | ||||
|  | ||||
|         return { | ||||
|             '_type': 'url', | ||||
|             'url': info['jw_player']['basic_video_node_player']['file'], | ||||
|             'url': youtube_url, | ||||
|             'ie_key': 'Youtube', | ||||
|         } | ||||
|   | ||||
| @@ -57,7 +57,7 @@ class FunimationIE(InfoExtractor): | ||||
|         try: | ||||
|             data = self._download_json( | ||||
|                 'https://prod-api-funimationnow.dadcdigital.com/api/auth/login/', | ||||
|                 None, 'Logging in as %s' % username, data=urlencode_postdata({ | ||||
|                 None, 'Logging in', data=urlencode_postdata({ | ||||
|                     'username': username, | ||||
|                     'password': password, | ||||
|                 })) | ||||
|   | ||||
| @@ -3,27 +3,31 @@ from __future__ import unicode_literals | ||||
|  | ||||
| from .adobepass import AdobePassIE | ||||
| from ..utils import ( | ||||
|     update_url_query, | ||||
|     extract_attributes, | ||||
|     int_or_none, | ||||
|     parse_age_limit, | ||||
|     smuggle_url, | ||||
|     update_url_query, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class FXNetworksIE(AdobePassIE): | ||||
|     _VALID_URL = r'https?://(?:www\.)?(?:fxnetworks|simpsonsworld)\.com/video/(?P<id>\d+)' | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.fxnetworks.com/video/719841347694', | ||||
|         'md5': '1447d4722e42ebca19e5232ab93abb22', | ||||
|         'url': 'http://www.fxnetworks.com/video/1032565827847', | ||||
|         'md5': '8d99b97b4aa7a202f55b6ed47ea7e703', | ||||
|         'info_dict': { | ||||
|             'id': '719841347694', | ||||
|             'id': 'dRzwHC_MMqIv', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Vanpage', | ||||
|             'description': 'F*ck settling down. You\'re the Worst returns for an all new season August 31st on FXX.', | ||||
|             'title': 'First Look: Better Things - Season 2', | ||||
|             'description': 'Because real life is like a fart. Watch this FIRST LOOK to see what inspired the new season of Better Things.', | ||||
|             'age_limit': 14, | ||||
|             'uploader': 'NEWA-FNG-FX', | ||||
|             'upload_date': '20160706', | ||||
|             'timestamp': 1467844741, | ||||
|             'upload_date': '20170825', | ||||
|             'timestamp': 1503686274, | ||||
|             'episode_number': 0, | ||||
|             'season_number': 2, | ||||
|             'series': 'Better Things', | ||||
|         }, | ||||
|         'add_ie': ['ThePlatform'], | ||||
|     }, { | ||||
| @@ -64,6 +68,9 @@ class FXNetworksIE(AdobePassIE): | ||||
|             'id': video_id, | ||||
|             'title': title, | ||||
|             'url': smuggle_url(update_url_query(release_url, query), {'force_smil_url': True}), | ||||
|             'series': video_data.get('data-show-title'), | ||||
|             'episode_number': int_or_none(video_data.get('data-episode')), | ||||
|             'season_number': int_or_none(video_data.get('data-season')), | ||||
|             'thumbnail': video_data.get('data-large-thumb'), | ||||
|             'age_limit': parse_age_limit(rating), | ||||
|             'ie_key': 'ThePlatform', | ||||
|   | ||||
| @@ -1,70 +0,0 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import re | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..utils import ( | ||||
|     js_to_json, | ||||
|     parse_duration, | ||||
|     remove_start, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class GamersydeIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:www\.)?gamersyde\.com/hqstream_(?P<display_id>[\da-z_]+)-(?P<id>\d+)_[a-z]{2}\.html' | ||||
|     _TEST = { | ||||
|         'url': 'http://www.gamersyde.com/hqstream_bloodborne_birth_of_a_hero-34371_en.html', | ||||
|         'md5': 'f38d400d32f19724570040d5ce3a505f', | ||||
|         'info_dict': { | ||||
|             'id': '34371', | ||||
|             'ext': 'mp4', | ||||
|             'duration': 372, | ||||
|             'title': 'Bloodborne - Birth of a hero', | ||||
|             'thumbnail': r're:^https?://.*\.jpg$', | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         mobj = re.match(self._VALID_URL, url) | ||||
|         video_id = mobj.group('id') | ||||
|         display_id = mobj.group('display_id') | ||||
|  | ||||
|         webpage = self._download_webpage(url, display_id) | ||||
|  | ||||
|         playlist = self._parse_json( | ||||
|             self._search_regex( | ||||
|                 r'(?s)playlist: \[({.+?})\]\s*}\);', webpage, 'files'), | ||||
|             display_id, transform_source=js_to_json) | ||||
|  | ||||
|         formats = [] | ||||
|         for source in playlist['sources']: | ||||
|             video_url = source.get('file') | ||||
|             if not video_url: | ||||
|                 continue | ||||
|             format_id = source.get('label') | ||||
|             f = { | ||||
|                 'url': video_url, | ||||
|                 'format_id': format_id, | ||||
|             } | ||||
|             m = re.search(r'^(?P<height>\d+)[pP](?P<fps>\d+)fps', format_id) | ||||
|             if m: | ||||
|                 f.update({ | ||||
|                     'height': int(m.group('height')), | ||||
|                     'fps': int(m.group('fps')), | ||||
|                 }) | ||||
|             formats.append(f) | ||||
|         self._sort_formats(formats) | ||||
|  | ||||
|         title = remove_start(playlist['title'], '%s - ' % video_id) | ||||
|         thumbnail = playlist.get('image') | ||||
|         duration = parse_duration(self._search_regex( | ||||
|             r'Length:</label>([^<]+)<', webpage, 'duration', fatal=False)) | ||||
|  | ||||
|         return { | ||||
|             'id': video_id, | ||||
|             'display_id': display_id, | ||||
|             'title': title, | ||||
|             'thumbnail': thumbnail, | ||||
|             'duration': duration, | ||||
|             'formats': formats, | ||||
|         } | ||||
| @@ -14,7 +14,7 @@ from ..utils import ( | ||||
|  | ||||
|  | ||||
| class GameSpotIE(OnceIE): | ||||
|     _VALID_URL = r'https?://(?:www\.)?gamespot\.com/.*-(?P<id>\d+)/?' | ||||
|     _VALID_URL = r'https?://(?:www\.)?gamespot\.com/(?:video|article)s/(?:[^/]+/\d+-|embed/)(?P<id>\d+)' | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.gamespot.com/videos/arma-3-community-guide-sitrep-i/2300-6410818/', | ||||
|         'md5': 'b2a30deaa8654fcccd43713a6b6a4825', | ||||
| @@ -35,6 +35,12 @@ class GameSpotIE(OnceIE): | ||||
|         'params': { | ||||
|             'skip_download': True,  # m3u8 downloads | ||||
|         }, | ||||
|     }, { | ||||
|         'url': 'https://www.gamespot.com/videos/embed/6439218/', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'https://www.gamespot.com/articles/the-last-of-us-2-receives-new-ps4-trailer/1100-6454469/', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
| @@ -52,7 +58,7 @@ class GameSpotIE(OnceIE): | ||||
|             manifest_url = f4m_url | ||||
|             formats.extend(self._extract_f4m_formats( | ||||
|                 f4m_url + '?hdcore=3.7.0', page_id, f4m_id='hds', fatal=False)) | ||||
|         m3u8_url = streams.get('m3u8_stream') | ||||
|         m3u8_url = dict_get(streams, ('m3u8_stream', 'adaptive_stream')) | ||||
|         if m3u8_url: | ||||
|             manifest_url = m3u8_url | ||||
|             m3u8_formats = self._extract_m3u8_formats( | ||||
| @@ -60,7 +66,7 @@ class GameSpotIE(OnceIE): | ||||
|                 m3u8_id='hls', fatal=False) | ||||
|             formats.extend(m3u8_formats) | ||||
|         progressive_url = dict_get( | ||||
|             streams, ('progressive_hd', 'progressive_high', 'progressive_low')) | ||||
|             streams, ('progressive_hd', 'progressive_high', 'progressive_low', 'other_lr')) | ||||
|         if progressive_url and manifest_url: | ||||
|             qualities_basename = self._search_regex( | ||||
|                 r'/([^/]+)\.csmil/', | ||||
| @@ -105,7 +111,8 @@ class GameSpotIE(OnceIE): | ||||
|             onceux_url = self._parse_json(unescapeHTML(onceux_json), page_id).get('metadataUri') | ||||
|             if onceux_url: | ||||
|                 formats.extend(self._extract_once_formats(re.sub( | ||||
|                     r'https?://[^/]+', 'http://once.unicornmedia.com', onceux_url))) | ||||
|                     r'https?://[^/]+', 'http://once.unicornmedia.com', onceux_url), | ||||
|                     http_formats_preference=-1)) | ||||
|  | ||||
|         if not formats: | ||||
|             for quality in ['sd', 'hd']: | ||||
|   | ||||
| @@ -59,10 +59,7 @@ from .tnaflix import TNAFlixNetworkEmbedIE | ||||
| from .drtuber import DrTuberIE | ||||
| from .redtube import RedTubeIE | ||||
| from .vimeo import VimeoIE | ||||
| from .dailymotion import ( | ||||
|     DailymotionIE, | ||||
|     DailymotionCloudIE, | ||||
| ) | ||||
| from .dailymotion import DailymotionIE | ||||
| from .dailymail import DailyMailIE | ||||
| from .onionstudios import OnionStudiosIE | ||||
| from .viewlift import ViewLiftEmbedIE | ||||
| @@ -102,6 +99,7 @@ from .joj import JojIE | ||||
| from .megaphone import MegaphoneIE | ||||
| from .vzaar import VzaarIE | ||||
| from .channel9 import Channel9IE | ||||
| from .vshare import VShareIE | ||||
|  | ||||
|  | ||||
| class GenericIE(InfoExtractor): | ||||
| @@ -1098,9 +1096,9 @@ class GenericIE(InfoExtractor): | ||||
|         }, | ||||
|         # jwplayer rtmp | ||||
|         { | ||||
|             'url': 'http://www.suffolk.edu/sjc/', | ||||
|             'url': 'http://www.suffolk.edu/sjc/live.php', | ||||
|             'info_dict': { | ||||
|                 'id': 'sjclive', | ||||
|                 'id': 'live', | ||||
|                 'ext': 'flv', | ||||
|                 'title': 'Massachusetts Supreme Judicial Court Oral Arguments', | ||||
|                 'uploader': 'www.suffolk.edu', | ||||
| @@ -1108,7 +1106,7 @@ class GenericIE(InfoExtractor): | ||||
|             'params': { | ||||
|                 'skip_download': True, | ||||
|             }, | ||||
|             'skip': 'does not contain a video anymore', | ||||
|             'skip': 'Only has video a few mornings per month, see http://www.suffolk.edu/sjc/', | ||||
|         }, | ||||
|         # Complex jwplayer | ||||
|         { | ||||
| @@ -1135,6 +1133,19 @@ class GenericIE(InfoExtractor): | ||||
|                 'skip_download': True, | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|             # JWPlatform iframe | ||||
|             'url': 'https://www.mediaite.com/tv/dem-senator-claims-gary-cohn-faked-a-bad-connection-during-trump-call-to-get-him-off-the-phone/', | ||||
|             'md5': 'ca00a040364b5b439230e7ebfd02c4e9', | ||||
|             'info_dict': { | ||||
|                 'id': 'O0c5JcKT', | ||||
|                 'ext': 'mp4', | ||||
|                 'upload_date': '20171122', | ||||
|                 'timestamp': 1511366290, | ||||
|                 'title': 'Dem Senator Claims Gary Cohn Faked a Bad Connection During Trump Call to Get Him Off the Phone', | ||||
|             }, | ||||
|             'add_ie': [JWPlatformIE.ie_key()], | ||||
|         }, | ||||
|         { | ||||
|             # Video.js embed, multiple formats | ||||
|             'url': 'http://ortcam.com/solidworks-урок-6-настройка-чертежа_33f9b7351.html', | ||||
| @@ -1458,23 +1469,6 @@ class GenericIE(InfoExtractor): | ||||
|                 'timestamp': 1432570283, | ||||
|             }, | ||||
|         }, | ||||
|         # Dailymotion Cloud video | ||||
|         { | ||||
|             'url': 'http://replay.publicsenat.fr/vod/le-debat/florent-kolandjian,dominique-cena,axel-decourtye,laurence-abeille,bruno-parmentier/175910', | ||||
|             'md5': 'dcaf23ad0c67a256f4278bce6e0bae38', | ||||
|             'info_dict': { | ||||
|                 'id': 'x2uy8t3', | ||||
|                 'ext': 'mp4', | ||||
|                 'title': 'Sauvons les abeilles ! - Le débat', | ||||
|                 'description': 'md5:d9082128b1c5277987825d684939ca26', | ||||
|                 'thumbnail': r're:^https?://.*\.jpe?g$', | ||||
|                 'timestamp': 1434970506, | ||||
|                 'upload_date': '20150622', | ||||
|                 'uploader': 'Public Sénat', | ||||
|                 'uploader_id': 'xa9gza', | ||||
|             }, | ||||
|             'skip': 'File not found.', | ||||
|         }, | ||||
|         # OnionStudios embed | ||||
|         { | ||||
|             'url': 'http://www.clickhole.com/video/dont-understand-bitcoin-man-will-mumble-explanatio-2537', | ||||
| @@ -1921,6 +1915,16 @@ class GenericIE(InfoExtractor): | ||||
|                 'title': 'Rescue Kit 14 Free Edition - Getting started', | ||||
|             }, | ||||
|             'playlist_count': 4, | ||||
|         }, | ||||
|         { | ||||
|             # vshare embed | ||||
|             'url': 'https://youtube-dl-demo.neocities.org/vshare.html', | ||||
|             'md5': '17b39f55b5497ae8b59f5fbce8e35886', | ||||
|             'info_dict': { | ||||
|                 'id': '0f64ce6', | ||||
|                 'title': 'vl14062007715967', | ||||
|                 'ext': 'mp4', | ||||
|             } | ||||
|         } | ||||
|         # { | ||||
|         #     # TODO: find another test | ||||
| @@ -2171,7 +2175,7 @@ class GenericIE(InfoExtractor): | ||||
|                 return self.playlist_result(self._parse_xspf(doc, video_id), video_id) | ||||
|             elif re.match(r'(?i)^(?:{[^}]+})?MPD$', doc.tag): | ||||
|                 info_dict['formats'] = self._parse_mpd_formats( | ||||
|                     doc, video_id, | ||||
|                     doc, | ||||
|                     mpd_base_url=compat_str(full_response.geturl()).rpartition('/')[0], | ||||
|                     mpd_url=url) | ||||
|                 self._sort_formats(info_dict['formats']) | ||||
| @@ -2680,11 +2684,6 @@ class GenericIE(InfoExtractor): | ||||
|         if senate_isvp_url: | ||||
|             return self.url_result(senate_isvp_url, 'SenateISVP') | ||||
|  | ||||
|         # Look for Dailymotion Cloud videos | ||||
|         dmcloud_url = DailymotionCloudIE._extract_dmcloud_url(webpage) | ||||
|         if dmcloud_url: | ||||
|             return self.url_result(dmcloud_url, 'DailymotionCloud') | ||||
|  | ||||
|         # Look for OnionStudios embeds | ||||
|         onionstudios_url = OnionStudiosIE._extract_url(webpage) | ||||
|         if onionstudios_url: | ||||
| @@ -2879,6 +2878,11 @@ class GenericIE(InfoExtractor): | ||||
|             return self.playlist_from_matches( | ||||
|                 channel9_urls, video_id, video_title, ie=Channel9IE.ie_key()) | ||||
|  | ||||
|         vshare_urls = VShareIE._extract_urls(webpage) | ||||
|         if vshare_urls: | ||||
|             return self.playlist_from_matches( | ||||
|                 vshare_urls, video_id, video_title, ie=VShareIE.ie_key()) | ||||
|  | ||||
|         def merge_dicts(dict1, dict2): | ||||
|             merged = {} | ||||
|             for k, v in dict1.items(): | ||||
|   | ||||
							
								
								
									
										22
									
								
								youtube_dl/extractor/gigya.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								youtube_dl/extractor/gigya.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| from .common import InfoExtractor | ||||
|  | ||||
| from ..utils import ( | ||||
|     ExtractorError, | ||||
|     urlencode_postdata, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class GigyaBaseIE(InfoExtractor): | ||||
|     def _gigya_login(self, auth_data): | ||||
|         auth_info = self._download_json( | ||||
|             'https://accounts.eu1.gigya.com/accounts.login', None, | ||||
|             note='Logging in', errnote='Unable to log in', | ||||
|             data=urlencode_postdata(auth_data)) | ||||
|  | ||||
|         error_message = auth_info.get('errorDetails') or auth_info.get('errorMessage') | ||||
|         if error_message: | ||||
|             raise ExtractorError( | ||||
|                 'Unable to login: %s' % error_message, expected=True) | ||||
|         return auth_info | ||||
| @@ -1,22 +1,47 @@ | ||||
| # coding: utf-8 | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import re | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..compat import compat_str | ||||
| from ..utils import ( | ||||
|     ExtractorError, | ||||
|     determine_ext, | ||||
|     ExtractorError, | ||||
|     int_or_none, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class HotStarIE(InfoExtractor): | ||||
| class HotStarBaseIE(InfoExtractor): | ||||
|     _GEO_COUNTRIES = ['IN'] | ||||
|  | ||||
|     def _download_json(self, *args, **kwargs): | ||||
|         response = super(HotStarBaseIE, self)._download_json(*args, **kwargs) | ||||
|         if response['resultCode'] != 'OK': | ||||
|             if kwargs.get('fatal'): | ||||
|                 raise ExtractorError( | ||||
|                     response['errorDescription'], expected=True) | ||||
|             return None | ||||
|         return response['resultObj'] | ||||
|  | ||||
|     def _download_content_info(self, content_id): | ||||
|         return self._download_json( | ||||
|             'https://account.hotstar.com/AVS/besc', content_id, query={ | ||||
|                 'action': 'GetAggregatedContentDetails', | ||||
|                 'appVersion': '5.0.40', | ||||
|                 'channel': 'PCTV', | ||||
|                 'contentId': content_id, | ||||
|             })['contentInfo'][0] | ||||
|  | ||||
|  | ||||
| class HotStarIE(HotStarBaseIE): | ||||
|     _VALID_URL = r'https?://(?:www\.)?hotstar\.com/(?:.+?[/-])?(?P<id>\d{10})' | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.hotstar.com/on-air-with-aib--english-1000076273', | ||||
|         'info_dict': { | ||||
|             'id': '1000076273', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'On Air With AIB - English', | ||||
|             'title': 'On Air With AIB', | ||||
|             'description': 'md5:c957d8868e9bc793ccb813691cc4c434', | ||||
|             'timestamp': 1447227000, | ||||
|             'upload_date': '20151111', | ||||
| @@ -34,23 +59,11 @@ class HotStarIE(InfoExtractor): | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _download_json(self, url_or_request, video_id, note='Downloading JSON metadata', fatal=True, query=None): | ||||
|         json_data = super(HotStarIE, self)._download_json( | ||||
|             url_or_request, video_id, note, fatal=fatal, query=query) | ||||
|         if json_data['resultCode'] != 'OK': | ||||
|             if fatal: | ||||
|                 raise ExtractorError(json_data['errorDescription']) | ||||
|             return None | ||||
|         return json_data['resultObj'] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         video_id = self._match_id(url) | ||||
|         video_data = self._download_json( | ||||
|             'http://account.hotstar.com/AVS/besc', video_id, query={ | ||||
|                 'action': 'GetAggregatedContentDetails', | ||||
|                 'channel': 'PCTV', | ||||
|                 'contentId': video_id, | ||||
|             })['contentInfo'][0] | ||||
|  | ||||
|         video_data = self._download_content_info(video_id) | ||||
|  | ||||
|         title = video_data['episodeTitle'] | ||||
|  | ||||
|         if video_data.get('encrypted') == 'Y': | ||||
| @@ -99,3 +112,51 @@ class HotStarIE(InfoExtractor): | ||||
|             'episode_number': int_or_none(video_data.get('episodeNumber')), | ||||
|             'series': video_data.get('contentTitle'), | ||||
|         } | ||||
|  | ||||
|  | ||||
| class HotStarPlaylistIE(HotStarBaseIE): | ||||
|     IE_NAME = 'hotstar:playlist' | ||||
|     _VALID_URL = r'(?P<url>https?://(?:www\.)?hotstar\.com/tv/[^/]+/(?P<content_id>\d+))/(?P<type>[^/]+)/(?P<id>\d+)' | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.hotstar.com/tv/pratidaan/14982/episodes/14812/9993', | ||||
|         'info_dict': { | ||||
|             'id': '14812', | ||||
|         }, | ||||
|         'playlist_mincount': 75, | ||||
|     }, { | ||||
|         'url': 'http://www.hotstar.com/tv/pratidaan/14982/popular-clips/9998/9998', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|     _ITEM_TYPES = { | ||||
|         'episodes': 'EPISODE', | ||||
|         'popular-clips': 'CLIPS', | ||||
|     } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         mobj = re.match(self._VALID_URL, url) | ||||
|         base_url = mobj.group('url') | ||||
|         content_id = mobj.group('content_id') | ||||
|         playlist_type = mobj.group('type') | ||||
|  | ||||
|         content_info = self._download_content_info(content_id) | ||||
|         playlist_id = compat_str(content_info['categoryId']) | ||||
|  | ||||
|         collection = self._download_json( | ||||
|             'https://search.hotstar.com/AVS/besc', playlist_id, query={ | ||||
|                 'action': 'SearchContents', | ||||
|                 'appVersion': '5.0.40', | ||||
|                 'channel': 'PCTV', | ||||
|                 'moreFilters': 'series:%s;' % playlist_id, | ||||
|                 'query': '*', | ||||
|                 'searchOrder': 'last_broadcast_date desc,year desc,title asc', | ||||
|                 'type': self._ITEM_TYPES.get(playlist_type, 'EPISODE'), | ||||
|             }) | ||||
|  | ||||
|         entries = [ | ||||
|             self.url_result( | ||||
|                 '%s/_/%s' % (base_url, video['contentId']), | ||||
|                 ie=HotStarIE.ie_key(), video_id=video['contentId']) | ||||
|             for video in collection['response']['docs'] | ||||
|             if video.get('contentId')] | ||||
|  | ||||
|         return self.playlist_result(entries, playlist_id) | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import itertools | ||||
| import re | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| @@ -7,7 +8,6 @@ from ..compat import compat_str | ||||
| from ..utils import ( | ||||
|     get_element_by_attribute, | ||||
|     int_or_none, | ||||
|     limit_length, | ||||
|     lowercase_escape, | ||||
|     try_get, | ||||
| ) | ||||
| @@ -130,13 +130,21 @@ class InstagramIE(InfoExtractor): | ||||
|                 video_url = media.get('video_url') | ||||
|                 height = int_or_none(media.get('dimensions', {}).get('height')) | ||||
|                 width = int_or_none(media.get('dimensions', {}).get('width')) | ||||
|                 description = media.get('caption') | ||||
|                 description = try_get( | ||||
|                     media, lambda x: x['edge_media_to_caption']['edges'][0]['node']['text'], | ||||
|                     compat_str) or media.get('caption') | ||||
|                 thumbnail = media.get('display_src') | ||||
|                 timestamp = int_or_none(media.get('date')) | ||||
|                 timestamp = int_or_none(media.get('taken_at_timestamp') or media.get('date')) | ||||
|                 uploader = media.get('owner', {}).get('full_name') | ||||
|                 uploader_id = media.get('owner', {}).get('username') | ||||
|                 like_count = int_or_none(media.get('likes', {}).get('count')) | ||||
|                 comment_count = int_or_none(media.get('comments', {}).get('count')) | ||||
|  | ||||
|                 def get_count(key, kind): | ||||
|                     return int_or_none(try_get( | ||||
|                         media, (lambda x: x['edge_media_%s' % key]['count'], | ||||
|                                 lambda x: x['%ss' % kind]['count']))) | ||||
|                 like_count = get_count('preview_like', 'like') | ||||
|                 comment_count = get_count('to_comment', 'comment') | ||||
|  | ||||
|                 comments = [{ | ||||
|                     'author': comment.get('user', {}).get('username'), | ||||
|                     'author_id': comment.get('user', {}).get('id'), | ||||
| @@ -212,7 +220,7 @@ class InstagramIE(InfoExtractor): | ||||
|  | ||||
|  | ||||
| class InstagramUserIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:www\.)?instagram\.com/(?P<username>[^/]{2,})/?(?:$|[?#])' | ||||
|     _VALID_URL = r'https?://(?:www\.)?instagram\.com/(?P<id>[^/]{2,})/?(?:$|[?#])' | ||||
|     IE_DESC = 'Instagram user profile' | ||||
|     IE_NAME = 'instagram:user' | ||||
|     _TEST = { | ||||
| @@ -221,82 +229,79 @@ class InstagramUserIE(InfoExtractor): | ||||
|             'id': 'porsche', | ||||
|             'title': 'porsche', | ||||
|         }, | ||||
|         'playlist_mincount': 2, | ||||
|         'playlist': [{ | ||||
|             'info_dict': { | ||||
|                 'id': '614605558512799803_462752227', | ||||
|                 'ext': 'mp4', | ||||
|                 'title': '#Porsche Intelligent Performance.', | ||||
|                 'thumbnail': r're:^https?://.*\.jpg', | ||||
|                 'uploader': 'Porsche', | ||||
|                 'uploader_id': 'porsche', | ||||
|                 'timestamp': 1387486713, | ||||
|                 'upload_date': '20131219', | ||||
|             }, | ||||
|         }], | ||||
|         'playlist_count': 5, | ||||
|         'params': { | ||||
|             'extract_flat': True, | ||||
|             'skip_download': True, | ||||
|             'playlistend': 5, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         mobj = re.match(self._VALID_URL, url) | ||||
|         uploader_id = mobj.group('username') | ||||
|     def _entries(self, uploader_id): | ||||
|         query = { | ||||
|             '__a': 1, | ||||
|         } | ||||
|  | ||||
|         entries = [] | ||||
|         page_count = 0 | ||||
|         media_url = 'http://instagram.com/%s/media' % uploader_id | ||||
|         while True: | ||||
|         def get_count(kind): | ||||
|             return int_or_none(try_get( | ||||
|                 node, lambda x: x['%ss' % kind]['count'])) | ||||
|  | ||||
|         for page_num in itertools.count(1): | ||||
|             page = self._download_json( | ||||
|                 media_url, uploader_id, | ||||
|                 note='Downloading page %d ' % (page_count + 1), | ||||
|             ) | ||||
|             page_count += 1 | ||||
|                 'https://instagram.com/%s/' % uploader_id, uploader_id, | ||||
|                 note='Downloading page %d' % page_num, | ||||
|                 fatal=False, query=query) | ||||
|             if not page: | ||||
|                 break | ||||
|  | ||||
|             for it in page['items']: | ||||
|                 if it.get('type') != 'video': | ||||
|             nodes = try_get(page, lambda x: x['user']['media']['nodes'], list) | ||||
|             if not nodes: | ||||
|                 break | ||||
|  | ||||
|             max_id = None | ||||
|  | ||||
|             for node in nodes: | ||||
|                 node_id = node.get('id') | ||||
|                 if node_id: | ||||
|                     max_id = node_id | ||||
|  | ||||
|                 if node.get('__typename') != 'GraphVideo' and node.get('is_video') is not True: | ||||
|                     continue | ||||
|                 video_id = node.get('code') | ||||
|                 if not video_id: | ||||
|                     continue | ||||
|                 like_count = int_or_none(it.get('likes', {}).get('count')) | ||||
|                 user = it.get('user', {}) | ||||
|  | ||||
|                 formats = [{ | ||||
|                     'format_id': k, | ||||
|                     'height': v.get('height'), | ||||
|                     'width': v.get('width'), | ||||
|                     'url': v['url'], | ||||
|                 } for k, v in it['videos'].items()] | ||||
|                 self._sort_formats(formats) | ||||
|                 info = self.url_result( | ||||
|                     'https://instagram.com/p/%s/' % video_id, | ||||
|                     ie=InstagramIE.ie_key(), video_id=video_id) | ||||
|  | ||||
|                 thumbnails_el = it.get('images', {}) | ||||
|                 thumbnail = thumbnails_el.get('thumbnail', {}).get('url') | ||||
|                 description = try_get( | ||||
|                     node, [lambda x: x['caption'], lambda x: x['text']['id']], | ||||
|                     compat_str) | ||||
|                 thumbnail = node.get('thumbnail_src') or node.get('display_src') | ||||
|                 timestamp = int_or_none(node.get('date')) | ||||
|  | ||||
|                 # In some cases caption is null, which corresponds to None | ||||
|                 # in python. As a result, it.get('caption', {}) gives None | ||||
|                 title = (it.get('caption') or {}).get('text', it['id']) | ||||
|                 comment_count = get_count('comment') | ||||
|                 like_count = get_count('like') | ||||
|                 view_count = int_or_none(node.get('video_views')) | ||||
|  | ||||
|                 entries.append({ | ||||
|                     'id': it['id'], | ||||
|                     'title': limit_length(title, 80), | ||||
|                     'formats': formats, | ||||
|                 info.update({ | ||||
|                     'description': description, | ||||
|                     'thumbnail': thumbnail, | ||||
|                     'webpage_url': it.get('link'), | ||||
|                     'uploader': user.get('full_name'), | ||||
|                     'uploader_id': user.get('username'), | ||||
|                     'timestamp': timestamp, | ||||
|                     'comment_count': comment_count, | ||||
|                     'like_count': like_count, | ||||
|                     'timestamp': int_or_none(it.get('created_time')), | ||||
|                     'view_count': view_count, | ||||
|                 }) | ||||
|  | ||||
|             if not page['items']: | ||||
|                 break | ||||
|             max_id = page['items'][-1]['id'].split('_')[0] | ||||
|             media_url = ( | ||||
|                 'http://instagram.com/%s/media?max_id=%s' % ( | ||||
|                     uploader_id, max_id)) | ||||
|                 yield info | ||||
|  | ||||
|         return { | ||||
|             '_type': 'playlist', | ||||
|             'entries': entries, | ||||
|             'id': uploader_id, | ||||
|             'title': uploader_id, | ||||
|         } | ||||
|             if not max_id: | ||||
|                 break | ||||
|  | ||||
|             query['max_id'] = max_id | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         uploader_id = self._match_id(url) | ||||
|         return self.playlist_result( | ||||
|             self._entries(uploader_id), uploader_id, uploader_id) | ||||
|   | ||||
| @@ -4,6 +4,7 @@ from __future__ import unicode_literals | ||||
| import uuid | ||||
| import xml.etree.ElementTree as etree | ||||
| import json | ||||
| import re | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..compat import ( | ||||
| @@ -25,7 +26,7 @@ from ..utils import ( | ||||
| class ITVIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:www\.)?itv\.com/hub/[^/]+/(?P<id>[0-9a-zA-Z]+)' | ||||
|     _GEO_COUNTRIES = ['GB'] | ||||
|     _TEST = { | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.itv.com/hub/mr-bean-animated-series/2a2936a0053', | ||||
|         'info_dict': { | ||||
|             'id': '2a2936a0053', | ||||
| @@ -36,7 +37,11 @@ class ITVIE(InfoExtractor): | ||||
|             # rtmp download | ||||
|             'skip_download': True, | ||||
|         }, | ||||
|     } | ||||
|     }, { | ||||
|         # unavailable via data-playlist-url | ||||
|         'url': 'https://www.itv.com/hub/through-the-keyhole/2a2271a0033', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         video_id = self._match_id(url) | ||||
| @@ -100,6 +105,18 @@ class ITVIE(InfoExtractor): | ||||
|             'Content-Type': 'text/xml; charset=utf-8', | ||||
|             'SOAPAction': 'http://tempuri.org/PlaylistService/GetPlaylist', | ||||
|         }) | ||||
|  | ||||
|         info = self._search_json_ld(webpage, video_id, default={}) | ||||
|         formats = [] | ||||
|         subtitles = {} | ||||
|  | ||||
|         def extract_subtitle(sub_url): | ||||
|             ext = determine_ext(sub_url, 'ttml') | ||||
|             subtitles.setdefault('en', []).append({ | ||||
|                 'url': sub_url, | ||||
|                 'ext': 'ttml' if ext == 'xml' else ext, | ||||
|             }) | ||||
|  | ||||
|         resp_env = self._download_xml( | ||||
|             params['data-playlist-url'], video_id, | ||||
|             headers=headers, data=etree.tostring(req_env)) | ||||
| @@ -110,41 +127,59 @@ class ITVIE(InfoExtractor): | ||||
|             if fault_code == 'InvalidGeoRegion': | ||||
|                 self.raise_geo_restricted( | ||||
|                     msg=fault_string, countries=self._GEO_COUNTRIES) | ||||
|             raise ExtractorError('%s said: %s' % (self.IE_NAME, fault_string)) | ||||
|         title = xpath_text(playlist, 'EpisodeTitle', fatal=True) | ||||
|         video_element = xpath_element(playlist, 'VideoEntries/Video', fatal=True) | ||||
|         media_files = xpath_element(video_element, 'MediaFiles', fatal=True) | ||||
|         rtmp_url = media_files.attrib['base'] | ||||
|             elif fault_code != 'InvalidEntity': | ||||
|                 raise ExtractorError( | ||||
|                     '%s said: %s' % (self.IE_NAME, fault_string), expected=True) | ||||
|             info.update({ | ||||
|                 'title': self._og_search_title(webpage), | ||||
|                 'episode_title': params.get('data-video-episode'), | ||||
|                 'series': params.get('data-video-title'), | ||||
|             }) | ||||
|         else: | ||||
|             title = xpath_text(playlist, 'EpisodeTitle', default=None) | ||||
|             info.update({ | ||||
|                 'title': title, | ||||
|                 'episode_title': title, | ||||
|                 'episode_number': int_or_none(xpath_text(playlist, 'EpisodeNumber')), | ||||
|                 'series': xpath_text(playlist, 'ProgrammeTitle'), | ||||
|                 'duration': parse_duration(xpath_text(playlist, 'Duration')), | ||||
|             }) | ||||
|             video_element = xpath_element(playlist, 'VideoEntries/Video', fatal=True) | ||||
|             media_files = xpath_element(video_element, 'MediaFiles', fatal=True) | ||||
|             rtmp_url = media_files.attrib['base'] | ||||
|  | ||||
|         formats = [] | ||||
|         for media_file in media_files.findall('MediaFile'): | ||||
|             play_path = xpath_text(media_file, 'URL') | ||||
|             if not play_path: | ||||
|                 continue | ||||
|             tbr = int_or_none(media_file.get('bitrate'), 1000) | ||||
|             f = { | ||||
|                 'format_id': 'rtmp' + ('-%d' % tbr if tbr else ''), | ||||
|                 'play_path': play_path, | ||||
|                 # Providing this swfVfy allows to avoid truncated downloads | ||||
|                 'player_url': 'http://www.itv.com/mercury/Mercury_VideoPlayer.swf', | ||||
|                 'page_url': url, | ||||
|                 'tbr': tbr, | ||||
|                 'ext': 'flv', | ||||
|             } | ||||
|             app = self._search_regex( | ||||
|                 'rtmpe?://[^/]+/(.+)$', rtmp_url, 'app', default=None) | ||||
|             if app: | ||||
|                 f.update({ | ||||
|                     'url': rtmp_url.split('?', 1)[0], | ||||
|                     'app': app, | ||||
|                 }) | ||||
|             else: | ||||
|                 f['url'] = rtmp_url | ||||
|             formats.append(f) | ||||
|             for media_file in media_files.findall('MediaFile'): | ||||
|                 play_path = xpath_text(media_file, 'URL') | ||||
|                 if not play_path: | ||||
|                     continue | ||||
|                 tbr = int_or_none(media_file.get('bitrate'), 1000) | ||||
|                 f = { | ||||
|                     'format_id': 'rtmp' + ('-%d' % tbr if tbr else ''), | ||||
|                     'play_path': play_path, | ||||
|                     # Providing this swfVfy allows to avoid truncated downloads | ||||
|                     'player_url': 'http://www.itv.com/mercury/Mercury_VideoPlayer.swf', | ||||
|                     'page_url': url, | ||||
|                     'tbr': tbr, | ||||
|                     'ext': 'flv', | ||||
|                 } | ||||
|                 app = self._search_regex( | ||||
|                     'rtmpe?://[^/]+/(.+)$', rtmp_url, 'app', default=None) | ||||
|                 if app: | ||||
|                     f.update({ | ||||
|                         'url': rtmp_url.split('?', 1)[0], | ||||
|                         'app': app, | ||||
|                     }) | ||||
|                 else: | ||||
|                     f['url'] = rtmp_url | ||||
|                 formats.append(f) | ||||
|  | ||||
|         ios_playlist_url = params.get('data-video-playlist') | ||||
|             for caption_url in video_element.findall('ClosedCaptioningURIs/URL'): | ||||
|                 if caption_url.text: | ||||
|                     extract_subtitle(caption_url.text) | ||||
|  | ||||
|         ios_playlist_url = params.get('data-video-playlist') or params.get('data-video-id') | ||||
|         hmac = params.get('data-video-hmac') | ||||
|         if ios_playlist_url and hmac: | ||||
|         if ios_playlist_url and hmac and re.match(r'https?://', ios_playlist_url): | ||||
|             headers = self.geo_verification_headers() | ||||
|             headers.update({ | ||||
|                 'Accept': 'application/vnd.itv.vod.playlist.v2+json', | ||||
| @@ -159,12 +194,12 @@ class ITVIE(InfoExtractor): | ||||
|                         'token': '' | ||||
|                     }, | ||||
|                     'device': { | ||||
|                         'manufacturer': 'Apple', | ||||
|                         'model': 'iPad', | ||||
|                         'manufacturer': 'Safari', | ||||
|                         'model': '5', | ||||
|                         'os': { | ||||
|                             'name': 'iPhone OS', | ||||
|                             'version': '9.3', | ||||
|                             'type': 'ios' | ||||
|                             'name': 'Windows NT', | ||||
|                             'version': '6.1', | ||||
|                             'type': 'desktop' | ||||
|                         } | ||||
|                     }, | ||||
|                     'client': { | ||||
| @@ -173,10 +208,10 @@ class ITVIE(InfoExtractor): | ||||
|                     }, | ||||
|                     'variantAvailability': { | ||||
|                         'featureset': { | ||||
|                             'min': ['hls', 'aes'], | ||||
|                             'max': ['hls', 'aes'] | ||||
|                             'min': ['hls', 'aes', 'outband-webvtt'], | ||||
|                             'max': ['hls', 'aes', 'outband-webvtt'] | ||||
|                         }, | ||||
|                         'platformTag': 'mobile' | ||||
|                         'platformTag': 'dotcom' | ||||
|                     } | ||||
|                 }).encode(), headers=headers, fatal=False) | ||||
|             if ios_playlist: | ||||
| @@ -197,27 +232,22 @@ class ITVIE(InfoExtractor): | ||||
|                         formats.append({ | ||||
|                             'url': href, | ||||
|                         }) | ||||
|                 subs = video_data.get('Subtitles') | ||||
|                 if isinstance(subs, list): | ||||
|                     for sub in subs: | ||||
|                         if not isinstance(sub, dict): | ||||
|                             continue | ||||
|                         href = sub.get('Href') | ||||
|                         if isinstance(href, compat_str): | ||||
|                             extract_subtitle(href) | ||||
|                 if not info.get('duration'): | ||||
|                     info['duration'] = parse_duration(video_data.get('Duration')) | ||||
|  | ||||
|         self._sort_formats(formats) | ||||
|  | ||||
|         subtitles = {} | ||||
|         for caption_url in video_element.findall('ClosedCaptioningURIs/URL'): | ||||
|             if not caption_url.text: | ||||
|                 continue | ||||
|             ext = determine_ext(caption_url.text, 'ttml') | ||||
|             subtitles.setdefault('en', []).append({ | ||||
|                 'url': caption_url.text, | ||||
|                 'ext': 'ttml' if ext == 'xml' else ext, | ||||
|             }) | ||||
|  | ||||
|         info = self._search_json_ld(webpage, video_id, default={}) | ||||
|         info.update({ | ||||
|             'id': video_id, | ||||
|             'title': title, | ||||
|             'formats': formats, | ||||
|             'subtitles': subtitles, | ||||
|             'episode_title': title, | ||||
|             'episode_number': int_or_none(xpath_text(playlist, 'EpisodeNumber')), | ||||
|             'series': xpath_text(playlist, 'ProgrammeTitle'), | ||||
|             'duartion': parse_duration(xpath_text(playlist, 'Duration')), | ||||
|         }) | ||||
|         return info | ||||
|   | ||||
| @@ -24,7 +24,7 @@ class JWPlatformIE(InfoExtractor): | ||||
|     @staticmethod | ||||
|     def _extract_url(webpage): | ||||
|         mobj = re.search( | ||||
|             r'<script[^>]+?src=["\'](?P<url>(?:https?:)?//content.jwplatform.com/players/[a-zA-Z0-9]{8})', | ||||
|             r'<(?:script|iframe)[^>]+?src=["\'](?P<url>(?:https?:)?//content.jwplatform.com/players/[a-zA-Z0-9]{8})', | ||||
|             webpage) | ||||
|         if mobj: | ||||
|             return mobj.group('url') | ||||
|   | ||||
| @@ -125,9 +125,12 @@ class KalturaIE(InfoExtractor): | ||||
|                         (?:https?:)?//cdnapi(?:sec)?\.kaltura\.com(?::\d+)?/(?:(?!(?P=q1)).)*\b(?:p|partner_id)/(?P<partner_id>\d+)(?:(?!(?P=q1)).)* | ||||
|                     (?P=q1).*? | ||||
|                     (?: | ||||
|                         entry_?[Ii]d| | ||||
|                         (?P<q2>["'])entry_?[Ii]d(?P=q2) | ||||
|                     )\s*:\s* | ||||
|                         (?: | ||||
|                             entry_?[Ii]d| | ||||
|                             (?P<q2>["'])entry_?[Ii]d(?P=q2) | ||||
|                         )\s*:\s*| | ||||
|                         \[\s*(?P<q2_1>["'])entry_?[Ii]d(?P=q2_1)\s*\]\s*=\s* | ||||
|                     ) | ||||
|                     (?P<q3>["'])(?P<id>(?:(?!(?P=q3)).)+)(?P=q3) | ||||
|                 ''', webpage) or | ||||
|             re.search( | ||||
|   | ||||
| @@ -114,7 +114,7 @@ class LivestreamIE(InfoExtractor): | ||||
|  | ||||
|         smil_url = video_data.get('smil_url') | ||||
|         if smil_url: | ||||
|             formats.extend(self._extract_smil_formats(smil_url, video_id)) | ||||
|             formats.extend(self._extract_smil_formats(smil_url, video_id, fatal=False)) | ||||
|  | ||||
|         m3u8_url = video_data.get('m3u8_url') | ||||
|         if m3u8_url: | ||||
|   | ||||
| @@ -13,8 +13,15 @@ from ..utils import ( | ||||
| class MailRuIE(InfoExtractor): | ||||
|     IE_NAME = 'mailru' | ||||
|     IE_DESC = 'Видео@Mail.Ru' | ||||
|     _VALID_URL = r'https?://(?:(?:www|m)\.)?my\.mail\.ru/(?:video/.*#video=/?(?P<idv1>(?:[^/]+/){3}\d+)|(?:(?P<idv2prefix>(?:[^/]+/){2})video/(?P<idv2suffix>[^/]+/\d+))\.html)' | ||||
|  | ||||
|     _VALID_URL = r'''(?x) | ||||
|                     https?:// | ||||
|                         (?:(?:www|m)\.)?my\.mail\.ru/ | ||||
|                         (?: | ||||
|                             video/.*\#video=/?(?P<idv1>(?:[^/]+/){3}\d+)| | ||||
|                             (?:(?P<idv2prefix>(?:[^/]+/){2})video/(?P<idv2suffix>[^/]+/\d+))\.html| | ||||
|                             (?:video/embed|\+/video/meta)/(?P<metaid>\d+) | ||||
|                         ) | ||||
|                     ''' | ||||
|     _TESTS = [ | ||||
|         { | ||||
|             'url': 'http://my.mail.ru/video/top#video=/mail/sonypicturesrus/75/76', | ||||
| @@ -23,7 +30,7 @@ class MailRuIE(InfoExtractor): | ||||
|                 'id': '46301138_76', | ||||
|                 'ext': 'mp4', | ||||
|                 'title': 'Новый Человек-Паук. Высокое напряжение. Восстание Электро', | ||||
|                 'timestamp': 1393232740, | ||||
|                 'timestamp': 1393235077, | ||||
|                 'upload_date': '20140224', | ||||
|                 'uploader': 'sonypicturesrus', | ||||
|                 'uploader_id': 'sonypicturesrus@mail.ru', | ||||
| @@ -40,7 +47,7 @@ class MailRuIE(InfoExtractor): | ||||
|                 'title': 'Samsung Galaxy S5 Hammer Smash Fail Battery Explosion', | ||||
|                 'timestamp': 1397039888, | ||||
|                 'upload_date': '20140409', | ||||
|                 'uploader': 'hitech@corp.mail.ru', | ||||
|                 'uploader': 'hitech', | ||||
|                 'uploader_id': 'hitech@corp.mail.ru', | ||||
|                 'duration': 245, | ||||
|             }, | ||||
| @@ -65,28 +72,42 @@ class MailRuIE(InfoExtractor): | ||||
|         { | ||||
|             'url': 'http://m.my.mail.ru/mail/3sktvtr/video/_myvideo/138.html', | ||||
|             'only_matching': True, | ||||
|         }, | ||||
|         { | ||||
|             'url': 'https://my.mail.ru/video/embed/7949340477499637815', | ||||
|             'only_matching': True, | ||||
|         }, | ||||
|         { | ||||
|             'url': 'http://my.mail.ru/+/video/meta/7949340477499637815', | ||||
|             'only_matching': True, | ||||
|         } | ||||
|     ] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         mobj = re.match(self._VALID_URL, url) | ||||
|         video_id = mobj.group('idv1') | ||||
|         meta_id = mobj.group('metaid') | ||||
|  | ||||
|         if not video_id: | ||||
|             video_id = mobj.group('idv2prefix') + mobj.group('idv2suffix') | ||||
|  | ||||
|         webpage = self._download_webpage(url, video_id) | ||||
|         video_id = None | ||||
|         if meta_id: | ||||
|             meta_url = 'https://my.mail.ru/+/video/meta/%s' % meta_id | ||||
|         else: | ||||
|             video_id = mobj.group('idv1') | ||||
|             if not video_id: | ||||
|                 video_id = mobj.group('idv2prefix') + mobj.group('idv2suffix') | ||||
|             webpage = self._download_webpage(url, video_id) | ||||
|             page_config = self._parse_json(self._search_regex( | ||||
|                 r'(?s)<script[^>]+class="sp-video__page-config"[^>]*>(.+?)</script>', | ||||
|                 webpage, 'page config', default='{}'), video_id, fatal=False) | ||||
|             if page_config: | ||||
|                 meta_url = page_config.get('metaUrl') or page_config.get('video', {}).get('metaUrl') | ||||
|             else: | ||||
|                 meta_url = None | ||||
|  | ||||
|         video_data = None | ||||
|  | ||||
|         page_config = self._parse_json(self._search_regex( | ||||
|             r'(?s)<script[^>]+class="sp-video__page-config"[^>]*>(.+?)</script>', | ||||
|             webpage, 'page config', default='{}'), video_id, fatal=False) | ||||
|         if page_config: | ||||
|             meta_url = page_config.get('metaUrl') or page_config.get('video', {}).get('metaUrl') | ||||
|             if meta_url: | ||||
|                 video_data = self._download_json( | ||||
|                     meta_url, video_id, 'Downloading video meta JSON', fatal=False) | ||||
|         if meta_url: | ||||
|             video_data = self._download_json( | ||||
|                 meta_url, video_id or meta_id, 'Downloading video meta JSON', | ||||
|                 fatal=not video_id) | ||||
|  | ||||
|         # Fallback old approach | ||||
|         if not video_data: | ||||
|   | ||||
							
								
								
									
										77
									
								
								youtube_dl/extractor/massengeschmacktv.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								youtube_dl/extractor/massengeschmacktv.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import re | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..utils import ( | ||||
|     clean_html, | ||||
|     determine_ext, | ||||
|     int_or_none, | ||||
|     js_to_json, | ||||
|     mimetype2ext, | ||||
|     parse_filesize, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class MassengeschmackTVIE(InfoExtractor): | ||||
|     IE_NAME = 'massengeschmack.tv' | ||||
|     _VALID_URL = r'https?://(?:www\.)?massengeschmack\.tv/play/(?P<id>[^?&#]+)' | ||||
|  | ||||
|     _TEST = { | ||||
|         'url': 'https://massengeschmack.tv/play/fktv202', | ||||
|         'md5': 'a9e054db9c2b5a08f0a0527cc201e8d3', | ||||
|         'info_dict': { | ||||
|             'id': 'fktv202', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Fernsehkritik-TV - Folge 202', | ||||
|         }, | ||||
|     } | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         episode = self._match_id(url) | ||||
|  | ||||
|         webpage = self._download_webpage(url, episode) | ||||
|         title = clean_html(self._html_search_regex( | ||||
|             '<h3>([^<]+)</h3>', webpage, 'title')) | ||||
|         thumbnail = self._search_regex(r'POSTER\s*=\s*"([^"]+)', webpage, 'thumbnail', fatal=False) | ||||
|         sources = self._parse_json(self._search_regex(r'(?s)MEDIA\s*=\s*(\[.+?\]);', webpage, 'media'), episode, js_to_json) | ||||
|  | ||||
|         formats = [] | ||||
|         for source in sources: | ||||
|             furl = source.get('src') | ||||
|             if not furl: | ||||
|                 continue | ||||
|             furl = self._proto_relative_url(furl) | ||||
|             ext = determine_ext(furl) or mimetype2ext(source.get('type')) | ||||
|             if ext == 'm3u8': | ||||
|                 formats.extend(self._extract_m3u8_formats( | ||||
|                     furl, episode, 'mp4', 'm3u8_native', | ||||
|                     m3u8_id='hls', fatal=False)) | ||||
|             else: | ||||
|                 formats.append({ | ||||
|                     'url': furl, | ||||
|                     'format_id': determine_ext(furl), | ||||
|                 }) | ||||
|  | ||||
|         for (durl, format_id, width, height, filesize) in re.findall(r'''(?x) | ||||
|                                    <a[^>]+?href="(?P<url>(?:https:)?//[^"]+)".*? | ||||
|                                    <strong>(?P<format_id>.+?)</strong>.*? | ||||
|                                    <small>(?:(?P<width>\d+)x(?P<height>\d+))?\s+?\((?P<filesize>[\d,]+\s*[GM]iB)\)</small> | ||||
|                                 ''', webpage): | ||||
|             formats.append({ | ||||
|                 'url': durl, | ||||
|                 'format_id': format_id, | ||||
|                 'width': int_or_none(width), | ||||
|                 'height': int_or_none(height), | ||||
|                 'filesize': parse_filesize(filesize), | ||||
|                 'vcodec': 'none' if format_id.startswith('Audio') else None, | ||||
|             }) | ||||
|  | ||||
|         self._sort_formats(formats, ('width', 'height', 'filesize', 'tbr')) | ||||
|  | ||||
|         return { | ||||
|             'id': episode, | ||||
|             'title': title, | ||||
|             'formats': formats, | ||||
|             'thumbnail': thumbnail, | ||||
|         } | ||||
| @@ -2,19 +2,18 @@ from __future__ import unicode_literals | ||||
|  | ||||
| import re | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from .gigya import GigyaBaseIE | ||||
|  | ||||
| from ..compat import compat_str | ||||
| from ..utils import ( | ||||
|     ExtractorError, | ||||
|     int_or_none, | ||||
|     parse_duration, | ||||
|     try_get, | ||||
|     unified_timestamp, | ||||
|     urlencode_postdata, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class MedialaanIE(InfoExtractor): | ||||
| class MedialaanIE(GigyaBaseIE): | ||||
|     _VALID_URL = r'''(?x) | ||||
|                     https?:// | ||||
|                         (?:www\.|nieuws\.)? | ||||
| @@ -119,15 +118,7 @@ class MedialaanIE(InfoExtractor): | ||||
|             'password': password, | ||||
|         } | ||||
|  | ||||
|         auth_info = self._download_json( | ||||
|             'https://accounts.eu1.gigya.com/accounts.login', None, | ||||
|             note='Logging in', errnote='Unable to log in', | ||||
|             data=urlencode_postdata(auth_data)) | ||||
|  | ||||
|         error_message = auth_info.get('errorDetails') or auth_info.get('errorMessage') | ||||
|         if error_message: | ||||
|             raise ExtractorError( | ||||
|                 'Unable to login: %s' % error_message, expected=True) | ||||
|         auth_info = self._gigya_login(auth_data) | ||||
|  | ||||
|         self._uid = auth_info['UID'] | ||||
|         self._uid_signature = auth_info['UIDSignature'] | ||||
|   | ||||
| @@ -18,7 +18,7 @@ class MegaphoneIE(InfoExtractor): | ||||
|             'id': 'GLT9749789991', | ||||
|             'ext': 'mp3', | ||||
|             'title': '#97 What Kind Of Idiot Gets Phished?', | ||||
|             'thumbnail': 're:^https://.*\.png.*$', | ||||
|             'thumbnail': r're:^https://.*\.png.*$', | ||||
|             'duration': 1776.26375, | ||||
|             'author': 'Reply All', | ||||
|         }, | ||||
|   | ||||
| @@ -40,21 +40,29 @@ class MnetIE(InfoExtractor): | ||||
|     def _real_extract(self, url): | ||||
|         video_id = self._match_id(url) | ||||
|  | ||||
|         # TODO: extract rtmp formats | ||||
|         # no stype -> rtmp url | ||||
|         # stype=H -> m3u8 url | ||||
|         # stype=M -> mpd url | ||||
|         info = self._download_json( | ||||
|             'http://content.api.mnet.com/player/vodConfig?id=%s&ctype=CLIP' % video_id, | ||||
|             video_id, 'Downloading vod config JSON')['data']['info'] | ||||
|             'http://content.api.mnet.com/player/vodConfig', | ||||
|             video_id, 'Downloading vod config JSON', query={ | ||||
|                 'id': video_id, | ||||
|                 'ctype': 'CLIP', | ||||
|                 'stype': 'H', | ||||
|             })['data']['info'] | ||||
|  | ||||
|         title = info['title'] | ||||
|  | ||||
|         rtmp_info = self._download_json( | ||||
|             info['cdn'], video_id, 'Downloading vod cdn JSON') | ||||
|  | ||||
|         formats = [{ | ||||
|             'url': rtmp_info['serverurl'] + rtmp_info['fileurl'], | ||||
|             'ext': 'flv', | ||||
|             'page_url': url, | ||||
|             'player_url': 'http://flvfile.mnet.com/service/player/201602/cjem_player_tv.swf?v=201602191318', | ||||
|         }] | ||||
|         cdn_data = self._download_json( | ||||
|             info['cdn'], video_id, 'Downloading vod cdn JSON')['data'][0] | ||||
|         m3u8_url = cdn_data['url'] | ||||
|         token = cdn_data.get('token') | ||||
|         if token and token != '-': | ||||
|             m3u8_url += '?' + token | ||||
|         formats = self._extract_wowza_formats( | ||||
|             m3u8_url, video_id, skip_protocols=['rtmp', 'rtsp', 'f4m']) | ||||
|         self._sort_formats(formats) | ||||
|  | ||||
|         description = info.get('ment') | ||||
|         duration = parse_duration(info.get('time')) | ||||
|   | ||||
| @@ -115,10 +115,17 @@ class MTVServicesInfoExtractor(InfoExtractor): | ||||
|             if transcript.get('kind') != 'captions': | ||||
|                 continue | ||||
|             lang = transcript.get('srclang') | ||||
|             subtitles[lang] = [{ | ||||
|                 'url': compat_str(typographic.get('src')), | ||||
|                 'ext': typographic.get('format') | ||||
|             } for typographic in transcript.findall('./typographic')] | ||||
|             for typographic in transcript.findall('./typographic'): | ||||
|                 sub_src = typographic.get('src') | ||||
|                 if not sub_src: | ||||
|                     continue | ||||
|                 ext = typographic.get('format') | ||||
|                 if ext == 'cea-608': | ||||
|                     ext = 'scc' | ||||
|                 subtitles.setdefault(lang, []).append({ | ||||
|                     'url': compat_str(sub_src), | ||||
|                     'ext': ext | ||||
|                 }) | ||||
|         return subtitles | ||||
|  | ||||
|     def _get_video_info(self, itemdoc, use_hls=True): | ||||
|   | ||||
| @@ -1,177 +0,0 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import binascii | ||||
| import base64 | ||||
| import hashlib | ||||
| import re | ||||
| import json | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..compat import ( | ||||
|     compat_ord, | ||||
|     compat_urllib_parse_unquote, | ||||
|     compat_urllib_parse_urlencode, | ||||
| ) | ||||
| from ..utils import ( | ||||
|     ExtractorError, | ||||
|     sanitized_Request, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class MyVideoIE(InfoExtractor): | ||||
|     _WORKING = False | ||||
|     _VALID_URL = r'https?://(?:www\.)?myvideo\.de/(?:[^/]+/)?watch/(?P<id>[0-9]+)/[^?/]+.*' | ||||
|     IE_NAME = 'myvideo' | ||||
|     _TEST = { | ||||
|         'url': 'http://www.myvideo.de/watch/8229274/bowling_fail_or_win', | ||||
|         'md5': '2d2753e8130479ba2cb7e0a37002053e', | ||||
|         'info_dict': { | ||||
|             'id': '8229274', | ||||
|             'ext': 'flv', | ||||
|             'title': 'bowling-fail-or-win', | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     # Original Code from: https://github.com/dersphere/plugin.video.myvideo_de.git | ||||
|     # Released into the Public Domain by Tristan Fischer on 2013-05-19 | ||||
|     # https://github.com/rg3/youtube-dl/pull/842 | ||||
|     def __rc4crypt(self, data, key): | ||||
|         x = 0 | ||||
|         box = list(range(256)) | ||||
|         for i in list(range(256)): | ||||
|             x = (x + box[i] + compat_ord(key[i % len(key)])) % 256 | ||||
|             box[i], box[x] = box[x], box[i] | ||||
|         x = 0 | ||||
|         y = 0 | ||||
|         out = '' | ||||
|         for char in data: | ||||
|             x = (x + 1) % 256 | ||||
|             y = (y + box[x]) % 256 | ||||
|             box[x], box[y] = box[y], box[x] | ||||
|             out += chr(compat_ord(char) ^ box[(box[x] + box[y]) % 256]) | ||||
|         return out | ||||
|  | ||||
|     def __md5(self, s): | ||||
|         return hashlib.md5(s).hexdigest().encode() | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         mobj = re.match(self._VALID_URL, url) | ||||
|         video_id = mobj.group('id') | ||||
|  | ||||
|         GK = ( | ||||
|             b'WXpnME1EZGhNRGhpTTJNM01XVmhOREU0WldNNVpHTTJOakpt' | ||||
|             b'TW1FMU5tVTBNR05pWkRaa05XRXhNVFJoWVRVd1ptSXhaVEV3' | ||||
|             b'TnpsbA0KTVRkbU1tSTRNdz09' | ||||
|         ) | ||||
|  | ||||
|         # Get video webpage | ||||
|         webpage_url = 'http://www.myvideo.de/watch/%s' % video_id | ||||
|         webpage = self._download_webpage(webpage_url, video_id) | ||||
|  | ||||
|         mobj = re.search('source src=\'(.+?)[.]([^.]+)\'', webpage) | ||||
|         if mobj is not None: | ||||
|             self.report_extraction(video_id) | ||||
|             video_url = mobj.group(1) + '.flv' | ||||
|  | ||||
|             video_title = self._html_search_regex('<title>([^<]+)</title>', | ||||
|                                                   webpage, 'title') | ||||
|  | ||||
|             return { | ||||
|                 'id': video_id, | ||||
|                 'url': video_url, | ||||
|                 'title': video_title, | ||||
|             } | ||||
|  | ||||
|         mobj = re.search(r'data-video-service="/service/data/video/%s/config' % video_id, webpage) | ||||
|         if mobj is not None: | ||||
|             request = sanitized_Request('http://www.myvideo.de/service/data/video/%s/config' % video_id, '') | ||||
|             response = self._download_webpage(request, video_id, | ||||
|                                               'Downloading video info') | ||||
|             info = json.loads(base64.b64decode(response).decode('utf-8')) | ||||
|             return { | ||||
|                 'id': video_id, | ||||
|                 'title': info['title'], | ||||
|                 'url': info['streaming_url'].replace('rtmpe', 'rtmpt'), | ||||
|                 'play_path': info['filename'], | ||||
|                 'ext': 'flv', | ||||
|                 'thumbnail': info['thumbnail'][0]['url'], | ||||
|             } | ||||
|  | ||||
|         # try encxml | ||||
|         mobj = re.search('var flashvars={(.+?)}', webpage) | ||||
|         if mobj is None: | ||||
|             raise ExtractorError('Unable to extract video') | ||||
|  | ||||
|         params = {} | ||||
|         encxml = '' | ||||
|         sec = mobj.group(1) | ||||
|         for (a, b) in re.findall('(.+?):\'(.+?)\',?', sec): | ||||
|             if not a == '_encxml': | ||||
|                 params[a] = b | ||||
|             else: | ||||
|                 encxml = compat_urllib_parse_unquote(b) | ||||
|         if not params.get('domain'): | ||||
|             params['domain'] = 'www.myvideo.de' | ||||
|         xmldata_url = '%s?%s' % (encxml, compat_urllib_parse_urlencode(params)) | ||||
|         if 'flash_playertype=MTV' in xmldata_url: | ||||
|             self._downloader.report_warning('avoiding MTV player') | ||||
|             xmldata_url = ( | ||||
|                 'http://www.myvideo.de/dynamic/get_player_video_xml.php' | ||||
|                 '?flash_playertype=D&ID=%s&_countlimit=4&autorun=yes' | ||||
|             ) % video_id | ||||
|  | ||||
|         # get enc data | ||||
|         enc_data = self._download_webpage(xmldata_url, video_id).split('=')[1] | ||||
|         enc_data_b = binascii.unhexlify(enc_data) | ||||
|         sk = self.__md5( | ||||
|             base64.b64decode(base64.b64decode(GK)) + | ||||
|             self.__md5( | ||||
|                 str(video_id).encode('utf-8') | ||||
|             ) | ||||
|         ) | ||||
|         dec_data = self.__rc4crypt(enc_data_b, sk) | ||||
|  | ||||
|         # extracting infos | ||||
|         self.report_extraction(video_id) | ||||
|  | ||||
|         video_url = None | ||||
|         mobj = re.search('connectionurl=\'(.*?)\'', dec_data) | ||||
|         if mobj: | ||||
|             video_url = compat_urllib_parse_unquote(mobj.group(1)) | ||||
|             if 'myvideo2flash' in video_url: | ||||
|                 self.report_warning( | ||||
|                     'Rewriting URL to use unencrypted rtmp:// ...', | ||||
|                     video_id) | ||||
|                 video_url = video_url.replace('rtmpe://', 'rtmp://') | ||||
|  | ||||
|         if not video_url: | ||||
|             # extract non rtmp videos | ||||
|             mobj = re.search('path=\'(http.*?)\' source=\'(.*?)\'', dec_data) | ||||
|             if mobj is None: | ||||
|                 raise ExtractorError('unable to extract url') | ||||
|             video_url = compat_urllib_parse_unquote(mobj.group(1)) + compat_urllib_parse_unquote(mobj.group(2)) | ||||
|  | ||||
|         video_file = self._search_regex('source=\'(.*?)\'', dec_data, 'video file') | ||||
|         video_file = compat_urllib_parse_unquote(video_file) | ||||
|  | ||||
|         if not video_file.endswith('f4m'): | ||||
|             ppath, prefix = video_file.split('.') | ||||
|             video_playpath = '%s:%s' % (prefix, ppath) | ||||
|         else: | ||||
|             video_playpath = '' | ||||
|  | ||||
|         video_swfobj = self._search_regex(r'swfobject\.embedSWF\(\'(.+?)\'', webpage, 'swfobj') | ||||
|         video_swfobj = compat_urllib_parse_unquote(video_swfobj) | ||||
|  | ||||
|         video_title = self._html_search_regex("<h1(?: class='globalHd')?>(.*?)</h1>", | ||||
|                                               webpage, 'title') | ||||
|  | ||||
|         return { | ||||
|             'id': video_id, | ||||
|             'url': video_url, | ||||
|             'tc_url': video_url, | ||||
|             'title': video_title, | ||||
|             'ext': 'flv', | ||||
|             'play_path': video_playpath, | ||||
|             'player_url': video_swfobj, | ||||
|         } | ||||
| @@ -15,7 +15,7 @@ from ..utils import ( | ||||
|  | ||||
|  | ||||
| class NBCIE(AdobePassIE): | ||||
|     _VALID_URL = r'https?(?P<permalink>://(?:www\.)?nbc\.com/[^/]+/video/[^/]+/(?P<id>n?\d+))' | ||||
|     _VALID_URL = r'https?(?P<permalink>://(?:www\.)?nbc\.com/(?:classic-tv/)?[^/]+/video/[^/]+/(?P<id>n?\d+))' | ||||
|  | ||||
|     _TESTS = [ | ||||
|         { | ||||
| @@ -67,7 +67,11 @@ class NBCIE(AdobePassIE): | ||||
|                 'skip_download': True, | ||||
|             }, | ||||
|             'skip': 'Only works from US', | ||||
|         } | ||||
|         }, | ||||
|         { | ||||
|             'url': 'https://www.nbc.com/classic-tv/charles-in-charge/video/charles-in-charge-pilot/n3310', | ||||
|             'only_matching': True, | ||||
|         }, | ||||
|     ] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|   | ||||
| @@ -1,45 +1,106 @@ | ||||
| # coding: utf-8 | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..compat import ( | ||||
|     compat_urllib_parse_unquote_plus | ||||
| ) | ||||
| from ..utils import ( | ||||
|     int_or_none, | ||||
|     parse_duration, | ||||
|     remove_end, | ||||
|     unified_strdate, | ||||
|     urljoin | ||||
| ) | ||||
|  | ||||
|  | ||||
| class NDTVIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:www\.)?ndtv\.com/video/(?:[^/]+/)+[^/?^&]+-(?P<id>\d+)' | ||||
|     _VALID_URL = r'https?://(?:[^/]+\.)?ndtv\.com/(?:[^/]+/)*videos?/?(?:[^/]+/)*[^/?^&]+-(?P<id>\d+)' | ||||
|  | ||||
|     _TEST = { | ||||
|         'url': 'http://www.ndtv.com/video/news/news/ndtv-exclusive-don-t-need-character-certificate-from-rahul-gandhi-says-arvind-kejriwal-300710', | ||||
|         'md5': '39f992dbe5fb531c395d8bbedb1e5e88', | ||||
|         'info_dict': { | ||||
|             'id': '300710', | ||||
|             'ext': 'mp4', | ||||
|             'title': "NDTV exclusive: Don't need character certificate from Rahul Gandhi, says Arvind Kejriwal", | ||||
|             'description': 'md5:ab2d4b4a6056c5cb4caa6d729deabf02', | ||||
|             'upload_date': '20131208', | ||||
|             'duration': 1327, | ||||
|             'thumbnail': r're:https?://.*\.jpg', | ||||
|     _TESTS = [ | ||||
|         { | ||||
|             'url': 'https://khabar.ndtv.com/video/show/prime-time/prime-time-ill-system-and-poor-education-468818', | ||||
|             'md5': '78efcf3880ef3fd9b83d405ca94a38eb', | ||||
|             'info_dict': { | ||||
|                 'id': '468818', | ||||
|                 'ext': 'mp4', | ||||
|                 'title': "प्राइम टाइम: सिस्टम बीमार, स्कूल बदहाल", | ||||
|                 'description': 'md5:f410512f1b49672e5695dea16ef2731d', | ||||
|                 'upload_date': '20170928', | ||||
|                 'duration': 2218, | ||||
|                 'thumbnail': r're:https?://.*\.jpg', | ||||
|             } | ||||
|         }, | ||||
|     } | ||||
|         { | ||||
|             # __filename is url | ||||
|             'url': 'http://movies.ndtv.com/videos/cracker-free-diwali-wishes-from-karan-johar-kriti-sanon-other-stars-470304', | ||||
|             'md5': 'f1d709352305b44443515ac56b45aa46', | ||||
|             'info_dict': { | ||||
|                 'id': '470304', | ||||
|                 'ext': 'mp4', | ||||
|                 'title': "Cracker-Free Diwali Wishes From Karan Johar, Kriti Sanon & Other Stars", | ||||
|                 'description': 'md5:f115bba1adf2f6433fa7c1ade5feb465', | ||||
|                 'upload_date': '20171019', | ||||
|                 'duration': 137, | ||||
|                 'thumbnail': r're:https?://.*\.jpg', | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|             'url': 'https://www.ndtv.com/video/news/news/delhi-s-air-quality-status-report-after-diwali-is-very-poor-470372', | ||||
|             'only_matching': True | ||||
|         }, | ||||
|         { | ||||
|             'url': 'https://auto.ndtv.com/videos/the-cnb-daily-october-13-2017-469935', | ||||
|             'only_matching': True | ||||
|         }, | ||||
|         { | ||||
|             'url': 'https://sports.ndtv.com/cricket/videos/2nd-t20i-rock-thrown-at-australia-cricket-team-bus-after-win-over-india-469764', | ||||
|             'only_matching': True | ||||
|         }, | ||||
|         { | ||||
|             'url': 'http://gadgets.ndtv.com/videos/uncharted-the-lost-legacy-review-465568', | ||||
|             'only_matching': True | ||||
|         }, | ||||
|         { | ||||
|             'url': 'http://profit.ndtv.com/videos/news/video-indian-economy-on-very-solid-track-international-monetary-fund-chief-470040', | ||||
|             'only_matching': True | ||||
|         }, | ||||
|         { | ||||
|             'url': 'http://food.ndtv.com/video-basil-seeds-coconut-porridge-419083', | ||||
|             'only_matching': True | ||||
|         }, | ||||
|         { | ||||
|             'url': 'https://doctor.ndtv.com/videos/top-health-stories-of-the-week-467396', | ||||
|             'only_matching': True | ||||
|         }, | ||||
|         { | ||||
|             'url': 'https://swirlster.ndtv.com/video/how-to-make-friends-at-work-469324', | ||||
|             'only_matching': True | ||||
|         } | ||||
|     ] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         video_id = self._match_id(url) | ||||
|         webpage = self._download_webpage(url, video_id) | ||||
|  | ||||
|         title = remove_end(self._og_search_title(webpage), ' - NDTV') | ||||
|         # '__title' does not contain extra words such as sub-site name, "Video" etc. | ||||
|         title = compat_urllib_parse_unquote_plus( | ||||
|             self._search_regex(r"__title\s*=\s*'([^']+)'", webpage, 'title', default=None) or | ||||
|             self._og_search_title(webpage)) | ||||
|  | ||||
|         filename = self._search_regex( | ||||
|             r"__filename='([^']+)'", webpage, 'video filename') | ||||
|         video_url = 'http://bitcast-b.bitgravity.com/ndtvod/23372/ndtv/%s' % filename | ||||
|             r"(?:__)?filename\s*[:=]\s*'([^']+)'", webpage, 'video filename') | ||||
|         # in "movies" sub-site pages, filename is URL | ||||
|         video_url = urljoin('https://ndtvod.bc-ssl.cdn.bitgravity.com/23372/ndtv/', filename.lstrip('/')) | ||||
|  | ||||
|         duration = int_or_none(self._search_regex( | ||||
|             r"__duration='([^']+)'", webpage, 'duration', fatal=False)) | ||||
|         # "doctor" sub-site has MM:SS format | ||||
|         duration = parse_duration(self._search_regex( | ||||
|             r"(?:__)?duration\s*[:=]\s*'([^']+)'", webpage, 'duration', fatal=False)) | ||||
|  | ||||
|         # "sports", "doctor", "swirlster" sub-sites don't have 'publish-date' | ||||
|         upload_date = unified_strdate(self._html_search_meta( | ||||
|             'publish-date', webpage, 'upload date', fatal=False)) | ||||
|             'publish-date', webpage, 'upload date', default=None) or self._html_search_meta( | ||||
|             'uploadDate', webpage, 'upload date', default=None) or self._search_regex( | ||||
|             r'datePublished"\s*:\s*"([^"]+)"', webpage, 'upload date', fatal=False)) | ||||
|  | ||||
|         description = remove_end(self._og_search_description(webpage), ' (Read more)') | ||||
|  | ||||
|   | ||||
| @@ -28,7 +28,7 @@ class NexxIE(InfoExtractor): | ||||
|     _TESTS = [{ | ||||
|         # movie | ||||
|         'url': 'https://api.nexx.cloud/v3/748/videos/byid/128907', | ||||
|         'md5': '16746bfc28c42049492385c989b26c4a', | ||||
|         'md5': '828cea195be04e66057b846288295ba1', | ||||
|         'info_dict': { | ||||
|             'id': '128907', | ||||
|             'ext': 'mp4', | ||||
| @@ -42,9 +42,6 @@ class NexxIE(InfoExtractor): | ||||
|             'timestamp': 1384264416, | ||||
|             'upload_date': '20131112', | ||||
|         }, | ||||
|         'params': { | ||||
|             'format': 'bestvideo', | ||||
|         }, | ||||
|     }, { | ||||
|         # episode | ||||
|         'url': 'https://api.nexx.cloud/v3/741/videos/byid/247858', | ||||
| @@ -62,7 +59,6 @@ class NexxIE(InfoExtractor): | ||||
|             'season_number': 2, | ||||
|         }, | ||||
|         'params': { | ||||
|             'format': 'bestvideo', | ||||
|             'skip_download': True, | ||||
|         }, | ||||
|     }, { | ||||
| @@ -193,35 +189,67 @@ class NexxIE(InfoExtractor): | ||||
|         stream_data = video['streamdata'] | ||||
|         language = general.get('language_raw') or '' | ||||
|  | ||||
|         # TODO: reverse more cdns and formats | ||||
|         # TODO: reverse more cdns | ||||
|  | ||||
|         cdn = stream_data['cdnType'] | ||||
|         assert cdn == 'azure' | ||||
|  | ||||
|         azure_locator = stream_data['azureLocator'] | ||||
|  | ||||
|         AZURE_URL = 'http://nx-p%02d.akamaized.net/' | ||||
|         AZURE_URL = 'http://nx%s%02d.akamaized.net/' | ||||
|  | ||||
|         for secure in ('s', ''): | ||||
|             cdn_shield = stream_data.get('cdnShieldHTTP%s' % secure.upper()) | ||||
|             if cdn_shield: | ||||
|                 azure_base = 'http%s://%s' % (secure, cdn_shield) | ||||
|                 break | ||||
|         else: | ||||
|             azure_base = AZURE_URL % int(stream_data['azureAccount'].replace('nexxplayplus', '')) | ||||
|         def get_cdn_shield_base(shield_type='', prefix='-p'): | ||||
|             for secure in ('', 's'): | ||||
|                 cdn_shield = stream_data.get('cdnShield%sHTTP%s' % (shield_type, secure.upper())) | ||||
|                 if cdn_shield: | ||||
|                     return 'http%s://%s' % (secure, cdn_shield) | ||||
|             else: | ||||
|                 return AZURE_URL % (prefix, int(stream_data['azureAccount'].replace('nexxplayplus', ''))) | ||||
|  | ||||
|         azure_stream_base = get_cdn_shield_base() | ||||
|         is_ml = ',' in language | ||||
|         azure_m3u8_url = '%s%s/%s_src%s.ism/Manifest(format=m3u8-aapl)' % ( | ||||
|             azure_base, azure_locator, video_id, ('_manifest' if is_ml else '')) | ||||
|         azure_manifest_url = '%s%s/%s_src%s.ism/Manifest' % ( | ||||
|             azure_stream_base, azure_locator, video_id, ('_manifest' if is_ml else '')) + '%s' | ||||
|  | ||||
|         protection_token = try_get( | ||||
|             video, lambda x: x['protectiondata']['token'], compat_str) | ||||
|         if protection_token: | ||||
|             azure_m3u8_url += '?hdnts=%s' % protection_token | ||||
|             azure_manifest_url += '?hdnts=%s' % protection_token | ||||
|  | ||||
|         formats = self._extract_m3u8_formats( | ||||
|             azure_m3u8_url, video_id, 'mp4', entry_protocol='m3u8_native', | ||||
|             m3u8_id='%s-hls' % cdn) | ||||
|             azure_manifest_url % '(format=m3u8-aapl)', | ||||
|             video_id, 'mp4', 'm3u8_native', | ||||
|             m3u8_id='%s-hls' % cdn, fatal=False) | ||||
|         formats.extend(self._extract_mpd_formats( | ||||
|             azure_manifest_url % '(format=mpd-time-csf)', | ||||
|             video_id, mpd_id='%s-dash' % cdn, fatal=False)) | ||||
|         formats.extend(self._extract_ism_formats( | ||||
|             azure_manifest_url % '', video_id, ism_id='%s-mss' % cdn, fatal=False)) | ||||
|  | ||||
|         azure_progressive_base = get_cdn_shield_base('Prog', '-d') | ||||
|         azure_file_distribution = stream_data.get('azureFileDistribution') | ||||
|         if azure_file_distribution: | ||||
|             fds = azure_file_distribution.split(',') | ||||
|             if fds: | ||||
|                 for fd in fds: | ||||
|                     ss = fd.split(':') | ||||
|                     if len(ss) == 2: | ||||
|                         tbr = int_or_none(ss[0]) | ||||
|                         if tbr: | ||||
|                             f = { | ||||
|                                 'url': '%s%s/%s_src_%s_%d.mp4' % ( | ||||
|                                     azure_progressive_base, azure_locator, video_id, ss[1], tbr), | ||||
|                                 'format_id': '%s-http-%d' % (cdn, tbr), | ||||
|                                 'tbr': tbr, | ||||
|                             } | ||||
|                             width_height = ss[1].split('x') | ||||
|                             if len(width_height) == 2: | ||||
|                                 f.update({ | ||||
|                                     'width': int_or_none(width_height[0]), | ||||
|                                     'height': int_or_none(width_height[1]), | ||||
|                                 }) | ||||
|                             formats.append(f) | ||||
|  | ||||
|         self._sort_formats(formats) | ||||
|  | ||||
|         return { | ||||
|   | ||||
| @@ -10,7 +10,7 @@ from ..utils import update_url_query | ||||
| class NickIE(MTVServicesInfoExtractor): | ||||
|     # None of videos on the website are still alive? | ||||
|     IE_NAME = 'nick.com' | ||||
|     _VALID_URL = r'https?://(?:(?:www|beta)\.)?nick(?:jr)?\.com/(?:[^/]+/)?(?:videos/clip|[^/]+/videos)/(?P<id>[^/?#.]+)' | ||||
|     _VALID_URL = r'https?://(?P<domain>(?:(?:www|beta)\.)?nick(?:jr)?\.com)/(?:[^/]+/)?(?:videos/clip|[^/]+/videos)/(?P<id>[^/?#.]+)' | ||||
|     _FEED_URL = 'http://udat.mtvnservices.com/service1/dispatch.htm' | ||||
|     _GEO_COUNTRIES = ['US'] | ||||
|     _TESTS = [{ | ||||
| @@ -69,13 +69,64 @@ class NickIE(MTVServicesInfoExtractor): | ||||
|             'mgid': uri, | ||||
|         } | ||||
|  | ||||
|     def _extract_mgid(self, webpage): | ||||
|         return self._search_regex(r'data-contenturi="([^"]+)', webpage, 'mgid') | ||||
|     def _real_extract(self, url): | ||||
|         domain, display_id = re.match(self._VALID_URL, url).groups() | ||||
|         video_data = self._download_json( | ||||
|             'http://%s/data/video.endLevel.json' % domain, | ||||
|             display_id, query={ | ||||
|                 'urlKey': display_id, | ||||
|             }) | ||||
|         return self._get_videos_info(video_data['player'] + video_data['id']) | ||||
|  | ||||
|  | ||||
| class NickBrIE(MTVServicesInfoExtractor): | ||||
|     IE_NAME = 'nickelodeon:br' | ||||
|     _VALID_URL = r'https?://(?P<domain>(?:www\.)?nickjr|mundonick\.uol)\.com\.br/(?:programas/)?[^/]+/videos/(?:episodios/)?(?P<id>[^/?#.]+)' | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.nickjr.com.br/patrulha-canina/videos/210-labirinto-de-pipoca/', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://mundonick.uol.com.br/programas/the-loud-house/videos/muitas-irmas/7ljo9j', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         domain, display_id = re.match(self._VALID_URL, url).groups() | ||||
|         webpage = self._download_webpage(url, display_id) | ||||
|         uri = self._search_regex( | ||||
|             r'data-(?:contenturi|mgid)="([^"]+)', webpage, 'mgid') | ||||
|         video_id = self._id_from_uri(uri) | ||||
|         config = self._download_json( | ||||
|             'http://media.mtvnservices.com/pmt/e1/access/index.html', | ||||
|             video_id, query={ | ||||
|                 'uri': uri, | ||||
|                 'configtype': 'edge', | ||||
|             }, headers={ | ||||
|                 'Referer': url, | ||||
|             }) | ||||
|         info_url = self._remove_template_parameter(config['feedWithQueryParams']) | ||||
|         if info_url == 'None': | ||||
|             if domain.startswith('www.'): | ||||
|                 domain = domain[4:] | ||||
|             content_domain = { | ||||
|                 'mundonick.uol': 'mundonick.com.br', | ||||
|                 'nickjr': 'br.nickelodeonjunior.tv', | ||||
|             }[domain] | ||||
|             query = { | ||||
|                 'mgid': uri, | ||||
|                 'imageEp': content_domain, | ||||
|                 'arcEp': content_domain, | ||||
|             } | ||||
|             if domain == 'nickjr.com.br': | ||||
|                 query['ep'] = 'c4b16088' | ||||
|             info_url = update_url_query( | ||||
|                 'http://feeds.mtvnservices.com/od/feed/intl-mrss-player-feed', query) | ||||
|         return self._get_videos_info_from_url(info_url, video_id) | ||||
|  | ||||
|  | ||||
| class NickDeIE(MTVServicesInfoExtractor): | ||||
|     IE_NAME = 'nick.de' | ||||
|     _VALID_URL = r'https?://(?:www\.)?(?P<host>nick\.(?:de|com\.pl)|nickelodeon\.(?:nl|at))/[^/]+/(?:[^/]+/)*(?P<id>[^/?#&]+)' | ||||
|     _VALID_URL = r'https?://(?:www\.)?(?P<host>nick\.(?:de|com\.pl|ch)|nickelodeon\.(?:nl|be|at|dk|no|se))/[^/]+/(?:[^/]+/)*(?P<id>[^/?#&]+)' | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.nick.de/playlist/3773-top-videos/videos/episode/17306-zu-wasser-und-zu-land-rauchende-erdnusse', | ||||
|         'only_matching': True, | ||||
| @@ -91,6 +142,21 @@ class NickDeIE(MTVServicesInfoExtractor): | ||||
|     }, { | ||||
|         'url': 'http://www.nick.com.pl/seriale/474-spongebob-kanciastoporty/wideo/17412-teatr-to-jest-to-rodeo-oszolom', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.nickelodeon.no/program/2626-bulderhuset/videoer/90947-femteklasse-veronica-vs-vanzilla', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.nickelodeon.dk/serier/2626-hojs-hus/videoer/761-tissepause', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.nickelodeon.se/serier/2626-lugn-i-stormen/videos/998-', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.nick.ch/shows/2304-adventure-time-abenteuerzeit-mit-finn-und-jake', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.nickelodeon.be/afspeellijst/4530-top-videos/videos/episode/73917-inval-broodschapper-lariekoek-arie', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _extract_mrss_url(self, webpage, host): | ||||
| @@ -132,13 +198,28 @@ class NickNightIE(NickDeIE): | ||||
|  | ||||
| class NickRuIE(MTVServicesInfoExtractor): | ||||
|     IE_NAME = 'nickelodeonru' | ||||
|     _VALID_URL = r'https?://(?:www\.)nickelodeon\.ru/(?:playlist|shows|videos)/(?:[^/]+/)*(?P<id>[^/?#&]+)' | ||||
|     _VALID_URL = r'https?://(?:www\.)nickelodeon\.(?:ru|fr|es|pt|ro|hu)/[^/]+/(?:[^/]+/)*(?P<id>[^/?#&]+)' | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.nickelodeon.ru/shows/henrydanger/videos/episodes/3-sezon-15-seriya-licenziya-na-polyot/pmomfb#playlist/7airc6', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.nickelodeon.ru/videos/smotri-na-nickelodeon-v-iyule/g9hvh7', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.nickelodeon.fr/programmes/bob-l-eponge/videos/le-marathon-de-booh-kini-bottom-mardi-31-octobre/nfn7z0', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.nickelodeon.es/videos/nickelodeon-consejos-tortitas/f7w7xy', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.nickelodeon.pt/series/spongebob-squarepants/videos/a-bolha-de-tinta-gigante/xutq1b', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.nickelodeon.ro/emisiuni/shimmer-si-shine/video/nahal-din-bomboane/uw5u2k', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.nickelodeon.hu/musorok/spongyabob-kockanadrag/videok/episodes/buborekfujas-az-elszakadt-nadrag/q57iob#playlist/k6te4y', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|   | ||||
| @@ -40,7 +40,7 @@ class NiconicoIE(InfoExtractor): | ||||
|             'uploader': 'takuya0301', | ||||
|             'uploader_id': '2698420', | ||||
|             'upload_date': '20131123', | ||||
|             'timestamp': 1385182762, | ||||
|             'timestamp': int,  # timestamp is unstable | ||||
|             'description': '(c) copyright 2008, Blender Foundation / www.bigbuckbunny.org', | ||||
|             'duration': 33, | ||||
|             'view_count': int, | ||||
| @@ -115,8 +115,8 @@ class NiconicoIE(InfoExtractor): | ||||
|         'skip': 'Requires an account', | ||||
|     }, { | ||||
|         # "New" HTML5 video | ||||
|         # md5 is unstable | ||||
|         'url': 'http://www.nicovideo.jp/watch/sm31464864', | ||||
|         'md5': '351647b4917660986dc0fa8864085135', | ||||
|         'info_dict': { | ||||
|             'id': 'sm31464864', | ||||
|             'ext': 'mp4', | ||||
| @@ -124,7 +124,7 @@ class NiconicoIE(InfoExtractor): | ||||
|             'description': 'md5:e52974af9a96e739196b2c1ca72b5feb', | ||||
|             'timestamp': 1498514060, | ||||
|             'upload_date': '20170626', | ||||
|             'uploader': 'ゲス', | ||||
|             'uploader': 'ゲスト', | ||||
|             'uploader_id': '40826363', | ||||
|             'thumbnail': r're:https?://.*', | ||||
|             'duration': 198, | ||||
| @@ -132,6 +132,25 @@ class NiconicoIE(InfoExtractor): | ||||
|             'comment_count': int, | ||||
|         }, | ||||
|         'skip': 'Requires an account', | ||||
|     }, { | ||||
|         # Video without owner | ||||
|         'url': 'http://www.nicovideo.jp/watch/sm18238488', | ||||
|         'md5': 'd265680a1f92bdcbbd2a507fc9e78a9e', | ||||
|         'info_dict': { | ||||
|             'id': 'sm18238488', | ||||
|             'ext': 'mp4', | ||||
|             'title': '【実写版】ミュータントタートルズ', | ||||
|             'description': 'md5:15df8988e47a86f9e978af2064bf6d8e', | ||||
|             'timestamp': 1341160408, | ||||
|             'upload_date': '20120701', | ||||
|             'uploader': None, | ||||
|             'uploader_id': None, | ||||
|             'thumbnail': r're:https?://.*', | ||||
|             'duration': 5271, | ||||
|             'view_count': int, | ||||
|             'comment_count': int, | ||||
|         }, | ||||
|         'skip': 'Requires an account', | ||||
|     }, { | ||||
|         'url': 'http://sp.nicovideo.jp/watch/sm28964488?ss_pos=1&cp_in=wt_tg', | ||||
|         'only_matching': True, | ||||
| @@ -395,7 +414,9 @@ class NiconicoIE(InfoExtractor): | ||||
|  | ||||
|         webpage_url = get_video_info('watch_url') or url | ||||
|  | ||||
|         owner = api_data.get('owner', {}) | ||||
|         # Note: cannot use api_data.get('owner', {}) because owner may be set to "null" | ||||
|         # in the JSON, which will cause None to be returned instead of {}. | ||||
|         owner = try_get(api_data, lambda x: x.get('owner'), dict) or {} | ||||
|         uploader_id = get_video_info(['ch_id', 'user_id']) or owner.get('id') | ||||
|         uploader = get_video_info(['ch_name', 'user_nickname']) or owner.get('nickname') | ||||
|  | ||||
|   | ||||
| @@ -70,7 +70,7 @@ class NocoIE(InfoExtractor): | ||||
|             return | ||||
|  | ||||
|         login = self._download_json( | ||||
|             self._LOGIN_URL, None, 'Logging in as %s' % username, | ||||
|             self._LOGIN_URL, None, 'Logging in', | ||||
|             data=urlencode_postdata({ | ||||
|                 'a': 'login', | ||||
|                 'cookie': '1', | ||||
|   | ||||
| @@ -1,261 +0,0 @@ | ||||
| # coding: utf-8 | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import re | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..compat import compat_str | ||||
| from ..utils import ( | ||||
|     ExtractorError, | ||||
|     determine_ext, | ||||
|     int_or_none, | ||||
|     parse_iso8601, | ||||
|     parse_duration, | ||||
|     remove_start, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class NowTVBaseIE(InfoExtractor): | ||||
|     _VIDEO_FIELDS = ( | ||||
|         'id', 'title', 'free', 'geoblocked', 'articleLong', 'articleShort', | ||||
|         'broadcastStartDate', 'seoUrl', 'duration', 'files', | ||||
|         'format.defaultImage169Format', 'format.defaultImage169Logo') | ||||
|  | ||||
|     def _extract_video(self, info, display_id=None): | ||||
|         video_id = compat_str(info['id']) | ||||
|  | ||||
|         files = info['files'] | ||||
|         if not files: | ||||
|             if info.get('geoblocked', False): | ||||
|                 raise ExtractorError( | ||||
|                     'Video %s is not available from your location due to geo restriction' % video_id, | ||||
|                     expected=True) | ||||
|             if not info.get('free', True): | ||||
|                 raise ExtractorError( | ||||
|                     'Video %s is not available for free' % video_id, expected=True) | ||||
|  | ||||
|         formats = [] | ||||
|         for item in files['items']: | ||||
|             if determine_ext(item['path']) != 'f4v': | ||||
|                 continue | ||||
|             app, play_path = remove_start(item['path'], '/').split('/', 1) | ||||
|             formats.append({ | ||||
|                 'url': 'rtmpe://fms.rtl.de', | ||||
|                 'app': app, | ||||
|                 'play_path': 'mp4:%s' % play_path, | ||||
|                 'ext': 'flv', | ||||
|                 'page_url': 'http://rtlnow.rtl.de', | ||||
|                 'player_url': 'http://cdn.static-fra.de/now/vodplayer.swf', | ||||
|                 'tbr': int_or_none(item.get('bitrate')), | ||||
|             }) | ||||
|         self._sort_formats(formats) | ||||
|  | ||||
|         title = info['title'] | ||||
|         description = info.get('articleLong') or info.get('articleShort') | ||||
|         timestamp = parse_iso8601(info.get('broadcastStartDate'), ' ') | ||||
|         duration = parse_duration(info.get('duration')) | ||||
|  | ||||
|         f = info.get('format', {}) | ||||
|         thumbnail = f.get('defaultImage169Format') or f.get('defaultImage169Logo') | ||||
|  | ||||
|         return { | ||||
|             'id': video_id, | ||||
|             'display_id': display_id or info.get('seoUrl'), | ||||
|             'title': title, | ||||
|             'description': description, | ||||
|             'thumbnail': thumbnail, | ||||
|             'timestamp': timestamp, | ||||
|             'duration': duration, | ||||
|             'formats': formats, | ||||
|         } | ||||
|  | ||||
|  | ||||
| class NowTVIE(NowTVBaseIE): | ||||
|     _WORKING = False | ||||
|     _VALID_URL = r'https?://(?:www\.)?nowtv\.(?:de|at|ch)/(?:rtl|rtl2|rtlnitro|superrtl|ntv|vox)/(?P<show_id>[^/]+)/(?:(?:list/[^/]+|jahr/\d{4}/\d{1,2})/)?(?P<id>[^/]+)/(?:player|preview)' | ||||
|  | ||||
|     _TESTS = [{ | ||||
|         # rtl | ||||
|         'url': 'http://www.nowtv.de/rtl/bauer-sucht-frau/die-neuen-bauern-und-eine-hochzeit/player', | ||||
|         'info_dict': { | ||||
|             'id': '203519', | ||||
|             'display_id': 'bauer-sucht-frau/die-neuen-bauern-und-eine-hochzeit', | ||||
|             'ext': 'flv', | ||||
|             'title': 'Inka Bause stellt die neuen Bauern vor', | ||||
|             'description': 'md5:e234e1ed6d63cf06be5c070442612e7e', | ||||
|             'thumbnail': r're:^https?://.*\.jpg$', | ||||
|             'timestamp': 1432580700, | ||||
|             'upload_date': '20150525', | ||||
|             'duration': 2786, | ||||
|         }, | ||||
|         'params': { | ||||
|             # rtmp download | ||||
|             'skip_download': True, | ||||
|         }, | ||||
|     }, { | ||||
|         # rtl2 | ||||
|         'url': 'http://www.nowtv.de/rtl2/berlin-tag-nacht/berlin-tag-nacht-folge-934/player', | ||||
|         'info_dict': { | ||||
|             'id': '203481', | ||||
|             'display_id': 'berlin-tag-nacht/berlin-tag-nacht-folge-934', | ||||
|             'ext': 'flv', | ||||
|             'title': 'Berlin - Tag & Nacht (Folge 934)', | ||||
|             'description': 'md5:c85e88c2e36c552dfe63433bc9506dd0', | ||||
|             'thumbnail': r're:^https?://.*\.jpg$', | ||||
|             'timestamp': 1432666800, | ||||
|             'upload_date': '20150526', | ||||
|             'duration': 2641, | ||||
|         }, | ||||
|         'params': { | ||||
|             # rtmp download | ||||
|             'skip_download': True, | ||||
|         }, | ||||
|     }, { | ||||
|         # rtlnitro | ||||
|         'url': 'http://www.nowtv.de/rtlnitro/alarm-fuer-cobra-11-die-autobahnpolizei/hals-und-beinbruch-2014-08-23-21-10-00/player', | ||||
|         'info_dict': { | ||||
|             'id': '165780', | ||||
|             'display_id': 'alarm-fuer-cobra-11-die-autobahnpolizei/hals-und-beinbruch-2014-08-23-21-10-00', | ||||
|             'ext': 'flv', | ||||
|             'title': 'Hals- und Beinbruch', | ||||
|             'description': 'md5:b50d248efffe244e6f56737f0911ca57', | ||||
|             'thumbnail': r're:^https?://.*\.jpg$', | ||||
|             'timestamp': 1432415400, | ||||
|             'upload_date': '20150523', | ||||
|             'duration': 2742, | ||||
|         }, | ||||
|         'params': { | ||||
|             # rtmp download | ||||
|             'skip_download': True, | ||||
|         }, | ||||
|     }, { | ||||
|         # superrtl | ||||
|         'url': 'http://www.nowtv.de/superrtl/medicopter-117/angst/player', | ||||
|         'info_dict': { | ||||
|             'id': '99205', | ||||
|             'display_id': 'medicopter-117/angst', | ||||
|             'ext': 'flv', | ||||
|             'title': 'Angst!', | ||||
|             'description': 'md5:30cbc4c0b73ec98bcd73c9f2a8c17c4e', | ||||
|             'thumbnail': r're:^https?://.*\.jpg$', | ||||
|             'timestamp': 1222632900, | ||||
|             'upload_date': '20080928', | ||||
|             'duration': 3025, | ||||
|         }, | ||||
|         'params': { | ||||
|             # rtmp download | ||||
|             'skip_download': True, | ||||
|         }, | ||||
|     }, { | ||||
|         # ntv | ||||
|         'url': 'http://www.nowtv.de/ntv/ratgeber-geld/thema-ua-der-erste-blick-die-apple-watch/player', | ||||
|         'info_dict': { | ||||
|             'id': '203521', | ||||
|             'display_id': 'ratgeber-geld/thema-ua-der-erste-blick-die-apple-watch', | ||||
|             'ext': 'flv', | ||||
|             'title': 'Thema u.a.: Der erste Blick: Die Apple Watch', | ||||
|             'description': 'md5:4312b6c9d839ffe7d8caf03865a531af', | ||||
|             'thumbnail': r're:^https?://.*\.jpg$', | ||||
|             'timestamp': 1432751700, | ||||
|             'upload_date': '20150527', | ||||
|             'duration': 1083, | ||||
|         }, | ||||
|         'params': { | ||||
|             # rtmp download | ||||
|             'skip_download': True, | ||||
|         }, | ||||
|     }, { | ||||
|         # vox | ||||
|         'url': 'http://www.nowtv.de/vox/der-hundeprofi/buero-fall-chihuahua-joel/player', | ||||
|         'info_dict': { | ||||
|             'id': '128953', | ||||
|             'display_id': 'der-hundeprofi/buero-fall-chihuahua-joel', | ||||
|             'ext': 'flv', | ||||
|             'title': "Büro-Fall / Chihuahua 'Joel'", | ||||
|             'description': 'md5:e62cb6bf7c3cc669179d4f1eb279ad8d', | ||||
|             'thumbnail': r're:^https?://.*\.jpg$', | ||||
|             'timestamp': 1432408200, | ||||
|             'upload_date': '20150523', | ||||
|             'duration': 3092, | ||||
|         }, | ||||
|         'params': { | ||||
|             # rtmp download | ||||
|             'skip_download': True, | ||||
|         }, | ||||
|     }, { | ||||
|         'url': 'http://www.nowtv.de/rtl/bauer-sucht-frau/die-neuen-bauern-und-eine-hochzeit/preview', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.nowtv.at/rtl/bauer-sucht-frau/die-neuen-bauern-und-eine-hochzeit/preview?return=/rtl/bauer-sucht-frau/die-neuen-bauern-und-eine-hochzeit', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.nowtv.de/rtl2/echtzeit/list/aktuell/schnelles-geld-am-ende-der-welt/player', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.nowtv.de/rtl2/zuhause-im-glueck/jahr/2015/11/eine-erschuetternde-diagnose/player', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         mobj = re.match(self._VALID_URL, url) | ||||
|         display_id = '%s/%s' % (mobj.group('show_id'), mobj.group('id')) | ||||
|  | ||||
|         info = self._download_json( | ||||
|             'https://api.nowtv.de/v3/movies/%s?fields=%s' | ||||
|             % (display_id, ','.join(self._VIDEO_FIELDS)), display_id) | ||||
|  | ||||
|         return self._extract_video(info, display_id) | ||||
|  | ||||
|  | ||||
| class NowTVListIE(NowTVBaseIE): | ||||
|     _VALID_URL = r'https?://(?:www\.)?nowtv\.(?:de|at|ch)/(?:rtl|rtl2|rtlnitro|superrtl|ntv|vox)/(?P<show_id>[^/]+)/list/(?P<id>[^?/#&]+)$' | ||||
|  | ||||
|     _SHOW_FIELDS = ('title', ) | ||||
|     _SEASON_FIELDS = ('id', 'headline', 'seoheadline', ) | ||||
|  | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.nowtv.at/rtl/stern-tv/list/aktuell', | ||||
|         'info_dict': { | ||||
|             'id': '17006', | ||||
|             'title': 'stern TV - Aktuell', | ||||
|         }, | ||||
|         'playlist_count': 1, | ||||
|     }, { | ||||
|         'url': 'http://www.nowtv.at/rtl/das-supertalent/list/free-staffel-8', | ||||
|         'info_dict': { | ||||
|             'id': '20716', | ||||
|             'title': 'Das Supertalent - FREE Staffel 8', | ||||
|         }, | ||||
|         'playlist_count': 14, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         mobj = re.match(self._VALID_URL, url) | ||||
|         show_id = mobj.group('show_id') | ||||
|         season_id = mobj.group('id') | ||||
|  | ||||
|         fields = [] | ||||
|         fields.extend(self._SHOW_FIELDS) | ||||
|         fields.extend('formatTabs.%s' % field for field in self._SEASON_FIELDS) | ||||
|         fields.extend( | ||||
|             'formatTabs.formatTabPages.container.movies.%s' % field | ||||
|             for field in self._VIDEO_FIELDS) | ||||
|  | ||||
|         list_info = self._download_json( | ||||
|             'https://api.nowtv.de/v3/formats/seo?fields=%s&name=%s.php' | ||||
|             % (','.join(fields), show_id), | ||||
|             season_id) | ||||
|  | ||||
|         season = next( | ||||
|             season for season in list_info['formatTabs']['items'] | ||||
|             if season.get('seoheadline') == season_id) | ||||
|  | ||||
|         title = '%s - %s' % (list_info['title'], season['headline']) | ||||
|  | ||||
|         entries = [] | ||||
|         for container in season['formatTabPages']['items']: | ||||
|             for info in ((container.get('container') or {}).get('movies') or {}).get('items') or []: | ||||
|                 entries.append(self._extract_video(info)) | ||||
|  | ||||
|         return self.playlist_result( | ||||
|             entries, compat_str(season.get('id') or season_id), title) | ||||
| @@ -14,6 +14,7 @@ from ..utils import ( | ||||
|     int_or_none, | ||||
|     qualities, | ||||
|     unescapeHTML, | ||||
|     urlencode_postdata, | ||||
| ) | ||||
|  | ||||
|  | ||||
| @@ -56,7 +57,7 @@ class OdnoklassnikiIE(InfoExtractor): | ||||
|         'url': 'http://ok.ru/video/64211978996595-1', | ||||
|         'md5': '2f206894ffb5dbfcce2c5a14b909eea5', | ||||
|         'info_dict': { | ||||
|             'id': '64211978996595-1', | ||||
|             'id': 'V_VztHT5BzY', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Космическая среда от 26 августа 2015', | ||||
|             'description': 'md5:848eb8b85e5e3471a3a803dae1343ed0', | ||||
| @@ -127,9 +128,14 @@ class OdnoklassnikiIE(InfoExtractor): | ||||
|         if metadata: | ||||
|             metadata = self._parse_json(metadata, video_id) | ||||
|         else: | ||||
|             data = {} | ||||
|             st_location = flashvars.get('location') | ||||
|             if st_location: | ||||
|                 data['st.location'] = st_location | ||||
|             metadata = self._download_json( | ||||
|                 compat_urllib_parse_unquote(flashvars['metadataUrl']), | ||||
|                 video_id, 'Downloading metadata JSON') | ||||
|                 video_id, 'Downloading metadata JSON', | ||||
|                 data=urlencode_postdata(data)) | ||||
|  | ||||
|         movie = metadata['movie'] | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,7 @@ class OnceIE(InfoExtractor): | ||||
|     ADAPTIVE_URL_TEMPLATE = 'http://once.unicornmedia.com/now/master/playlist/%s/%s/%s/content.m3u8' | ||||
|     PROGRESSIVE_URL_TEMPLATE = 'http://once.unicornmedia.com/now/media/progressive/%s/%s/%s/%s/content.mp4' | ||||
|  | ||||
|     def _extract_once_formats(self, url): | ||||
|     def _extract_once_formats(self, url, http_formats_preference=None): | ||||
|         domain_id, application_id, media_item_id = re.match( | ||||
|             OnceIE._VALID_URL, url).groups() | ||||
|         formats = self._extract_m3u8_formats( | ||||
| @@ -35,6 +35,7 @@ class OnceIE(InfoExtractor): | ||||
|                     'format_id': adaptive_format['format_id'].replace( | ||||
|                         'hls', 'http'), | ||||
|                     'protocol': 'http', | ||||
|                     'preference': http_formats_preference, | ||||
|                 }) | ||||
|                 progressive_formats.append(progressive_format) | ||||
|         self._check_formats(progressive_formats, media_item_id) | ||||
|   | ||||
| @@ -140,7 +140,7 @@ class PhantomJSwrapper(object): | ||||
|         for name in self._TMP_FILE_NAMES: | ||||
|             try: | ||||
|                 os.remove(self._TMP_FILES[name].name) | ||||
|             except: | ||||
|             except (IOError, OSError): | ||||
|                 pass | ||||
|  | ||||
|     def _save_cookies(self, url): | ||||
| @@ -242,7 +242,7 @@ class PhantomJSwrapper(object): | ||||
|  | ||||
|  | ||||
| class OpenloadIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:openload\.(?:co|io)|oload\.tv)/(?:f|embed)/(?P<id>[a-zA-Z0-9-_]+)' | ||||
|     _VALID_URL = r'https?://(?:www\.)?(?:openload\.(?:co|io|link)|oload\.tv)/(?:f|embed)/(?P<id>[a-zA-Z0-9-_]+)' | ||||
|  | ||||
|     _TESTS = [{ | ||||
|         'url': 'https://openload.co/f/kUEfGclsU9o', | ||||
| @@ -286,6 +286,9 @@ class OpenloadIE(InfoExtractor): | ||||
|     }, { | ||||
|         'url': 'https://oload.tv/embed/KnG-kKZdcfY/', | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://www.openload.link/f/KnG-kKZdcfY', | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     _USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36' | ||||
|   | ||||
| @@ -49,13 +49,13 @@ class ORFTVthekIE(InfoExtractor): | ||||
|         'params': { | ||||
|             'skip_download': True,  # rtsp downloads | ||||
|         }, | ||||
|         '_skip': 'Blocked outside of Austria / Germany', | ||||
|         'skip': 'Blocked outside of Austria / Germany', | ||||
|     }, { | ||||
|         'url': 'http://tvthek.orf.at/topic/Fluechtlingskrise/10463081/Heimat-Fremde-Heimat/13879132/Senioren-betreuen-Migrantenkinder/13879141', | ||||
|         'skip_download': True, | ||||
|         'only_matching': True, | ||||
|     }, { | ||||
|         'url': 'http://tvthek.orf.at/profile/Universum/35429', | ||||
|         'skip_download': True, | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|   | ||||
| @@ -33,7 +33,7 @@ class PandaTVIE(InfoExtractor): | ||||
|         video_id = self._match_id(url) | ||||
|  | ||||
|         config = self._download_json( | ||||
|             'https://www.panda.tv/api_room?roomid=%s' % video_id, video_id) | ||||
|             'https://www.panda.tv/api_room_v2?roomid=%s' % video_id, video_id) | ||||
|  | ||||
|         error_code = config.get('errno', 0) | ||||
|         if error_code is not 0: | ||||
| @@ -66,6 +66,11 @@ class PandaTVIE(InfoExtractor): | ||||
|             plflag1 = '4' | ||||
|         live_panda = 'live_panda' if plflag0 < 1 else '' | ||||
|  | ||||
|         plflag_auth = self._parse_json(video_info['plflag_list'], video_id) | ||||
|         sign = plflag_auth['auth']['sign'] | ||||
|         ts = plflag_auth['auth']['time'] | ||||
|         rid = plflag_auth['auth']['rid'] | ||||
|  | ||||
|         quality_key = qualities(['OD', 'HD', 'SD']) | ||||
|         suffix = ['_small', '_mid', ''] | ||||
|         formats = [] | ||||
| @@ -77,8 +82,8 @@ class PandaTVIE(InfoExtractor): | ||||
|                 continue | ||||
|             for pref, (ext, pl) in enumerate((('m3u8', '-hls'), ('flv', ''))): | ||||
|                 formats.append({ | ||||
|                     'url': 'https://pl%s%s.live.panda.tv/live_panda/%s%s%s.%s' | ||||
|                     % (pl, plflag1, room_key, live_panda, suffix[quality], ext), | ||||
|                     'url': 'https://pl%s%s.live.panda.tv/live_panda/%s%s%s.%s?sign=%s&ts=%s&rid=%s' | ||||
|                     % (pl, plflag1, room_key, live_panda, suffix[quality], ext, sign, ts, rid), | ||||
|                     'format_id': '%s-%s' % (k, ext), | ||||
|                     'quality': quality, | ||||
|                     'source_preference': pref, | ||||
|   | ||||
| @@ -11,7 +11,7 @@ class ParliamentLiveUKIE(InfoExtractor): | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://parliamentlive.tv/Event/Index/c1e9d44d-fd6c-4263-b50f-97ed26cc998b', | ||||
|         'info_dict': { | ||||
|             'id': 'c1e9d44d-fd6c-4263-b50f-97ed26cc998b', | ||||
|             'id': '1_af9nv9ym', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Home Affairs Committee', | ||||
|             'uploader_id': 'FFMPEG-01', | ||||
| @@ -28,14 +28,14 @@ class ParliamentLiveUKIE(InfoExtractor): | ||||
|         webpage = self._download_webpage( | ||||
|             'http://vodplayer.parliamentlive.tv/?mid=' + video_id, video_id) | ||||
|         widget_config = self._parse_json(self._search_regex( | ||||
|             r'kWidgetConfig\s*=\s*({.+});', | ||||
|             r'(?s)kWidgetConfig\s*=\s*({.+});', | ||||
|             webpage, 'kaltura widget config'), video_id) | ||||
|         kaltura_url = 'kaltura:%s:%s' % (widget_config['wid'][1:], widget_config['entry_id']) | ||||
|         kaltura_url = 'kaltura:%s:%s' % ( | ||||
|             widget_config['wid'][1:], widget_config['entry_id']) | ||||
|         event_title = self._download_json( | ||||
|             'http://parliamentlive.tv/Event/GetShareVideo/' + video_id, video_id)['event']['title'] | ||||
|         return { | ||||
|             '_type': 'url_transparent', | ||||
|             'id': video_id, | ||||
|             'title': event_title, | ||||
|             'description': '', | ||||
|             'url': kaltura_url, | ||||
|   | ||||
| @@ -67,7 +67,7 @@ class PatreonIE(InfoExtractor): | ||||
|             'https://www.patreon.com/processLogin', | ||||
|             compat_urllib_parse_urlencode(login_form).encode('utf-8') | ||||
|         ) | ||||
|         login_page = self._download_webpage(request, None, note='Logging in as %s' % username) | ||||
|         login_page = self._download_webpage(request, None, note='Logging in') | ||||
|  | ||||
|         if re.search(r'onLoginFailed', login_page): | ||||
|             raise ExtractorError('Unable to login, incorrect username and/or password', expected=True) | ||||
|   | ||||
| @@ -187,7 +187,7 @@ class PBSIE(InfoExtractor): | ||||
|     _VALID_URL = r'''(?x)https?:// | ||||
|         (?: | ||||
|            # Direct video URL | ||||
|            (?:%s)/(?:viralplayer|video)/(?P<id>[0-9]+)/? | | ||||
|            (?:%s)/(?:(?:vir|port)alplayer|video)/(?P<id>[0-9]+)(?:[?/]|$) | | ||||
|            # Article with embedded player (or direct video) | ||||
|            (?:www\.)?pbs\.org/(?:[^/]+/){1,5}(?P<presumptive_id>[^/]+?)(?:\.html)?/?(?:$|[?\#]) | | ||||
|            # Player | ||||
| @@ -367,6 +367,10 @@ class PBSIE(InfoExtractor): | ||||
|         { | ||||
|             'url': 'http://watch.knpb.org/video/2365616055/', | ||||
|             'only_matching': True, | ||||
|         }, | ||||
|         { | ||||
|             'url': 'https://player.pbs.org/portalplayer/3004638221/?uid=', | ||||
|             'only_matching': True, | ||||
|         } | ||||
|     ] | ||||
|     _ERRORS = { | ||||
| @@ -417,6 +421,7 @@ class PBSIE(InfoExtractor): | ||||
|                 r'class="coveplayerid">([^<]+)<',                       # coveplayer | ||||
|                 r'<section[^>]+data-coveid="(\d+)"',                    # coveplayer from http://www.pbs.org/wgbh/frontline/film/real-csi/ | ||||
|                 r'<input type="hidden" id="pbs_video_id_[0-9]+" value="([0-9]+)"/>',  # jwplayer | ||||
|                 r"(?s)window\.PBS\.playerConfig\s*=\s*{.*?id\s*:\s*'([0-9]+)',", | ||||
|             ] | ||||
|  | ||||
|             media_id = self._search_regex( | ||||
|   | ||||
							
								
								
									
										83
									
								
								youtube_dl/extractor/performgroup.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								youtube_dl/extractor/performgroup.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| # coding: utf-8 | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import re | ||||
|  | ||||
| from .common import InfoExtractor | ||||
| from ..utils import int_or_none | ||||
|  | ||||
|  | ||||
| class PerformGroupIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://player\.performgroup\.com/eplayer(?:/eplayer\.html|\.js)#/?(?P<id>[0-9a-f]{26})\.(?P<auth_token>[0-9a-z]{26})' | ||||
|     _TESTS = [{ | ||||
|         # http://www.faz.net/aktuell/sport/fussball/wm-2018-playoffs-schweiz-besiegt-nordirland-1-0-15286104.html | ||||
|         'url': 'http://player.performgroup.com/eplayer/eplayer.html#d478c41c5d192f56b9aa859de8.1w4crrej5w14e1ed4s1ce4ykab', | ||||
|         'md5': '259cb03d142e2e52471e8837ecacb29f', | ||||
|         'info_dict': { | ||||
|             'id': 'xgrwobuzumes1lwjxtcdpwgxd', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Liga MX: Keine Einsicht nach Horrorfoul', | ||||
|             'description': 'md5:7cd3b459c82725b021e046ab10bf1c5b', | ||||
|             'timestamp': 1511533477, | ||||
|             'upload_date': '20171124', | ||||
|         } | ||||
|     }] | ||||
|  | ||||
|     def _call_api(self, service, auth_token, content_id, referer_url): | ||||
|         return self._download_json( | ||||
|             'http://ep3.performfeeds.com/ep%s/%s/%s/' % (service, auth_token, content_id), | ||||
|             content_id, headers={ | ||||
|                 'Referer': referer_url, | ||||
|                 'Origin': 'http://player.performgroup.com', | ||||
|             }, query={ | ||||
|                 '_fmt': 'json', | ||||
|             }) | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         player_id, auth_token = re.search(self._VALID_URL, url).groups() | ||||
|         bootstrap = self._call_api('bootstrap', auth_token, player_id, url) | ||||
|         video = bootstrap['config']['dataSource']['sourceItems'][0]['videos'][0] | ||||
|         video_id = video['uuid'] | ||||
|         vod = self._call_api('vod', auth_token, video_id, url) | ||||
|         media = vod['videos']['video'][0]['media'] | ||||
|  | ||||
|         formats = [] | ||||
|         hls_url = media.get('hls', {}).get('url') | ||||
|         if hls_url: | ||||
|             formats.extend(self._extract_m3u8_formats(hls_url, video_id, 'mp4', 'm3u8_native', m3u8_id='hls', fatal=False)) | ||||
|  | ||||
|         hds_url = media.get('hds', {}).get('url') | ||||
|         if hds_url: | ||||
|             formats.extend(self._extract_f4m_formats(hds_url + '?hdcore', video_id, f4m_id='hds', fatal=False)) | ||||
|  | ||||
|         for c in media.get('content', []): | ||||
|             c_url = c.get('url') | ||||
|             if not c_url: | ||||
|                 continue | ||||
|             tbr = int_or_none(c.get('bitrate'), 1000) | ||||
|             format_id = 'http' | ||||
|             if tbr: | ||||
|                 format_id += '-%d' % tbr | ||||
|             formats.append({ | ||||
|                 'format_id': format_id, | ||||
|                 'url': c_url, | ||||
|                 'tbr': tbr, | ||||
|                 'width': int_or_none(c.get('width')), | ||||
|                 'height': int_or_none(c.get('height')), | ||||
|                 'filesize': int_or_none(c.get('fileSize')), | ||||
|                 'vcodec': c.get('type'), | ||||
|                 'fps': int_or_none(c.get('videoFrameRate')), | ||||
|                 'vbr': int_or_none(c.get('videoRate'), 1000), | ||||
|                 'abr': int_or_none(c.get('audioRate'), 1000), | ||||
|             }) | ||||
|         self._sort_formats(formats) | ||||
|  | ||||
|         return { | ||||
|             'id': video_id, | ||||
|             'title': video['title'], | ||||
|             'description': video.get('description'), | ||||
|             'thumbnail': video.get('poster'), | ||||
|             'duration': int_or_none(video.get('duration')), | ||||
|             'timestamp': int_or_none(video.get('publishedTime'), 1000), | ||||
|             'formats': formats, | ||||
|         } | ||||
| @@ -116,7 +116,7 @@ class PluralsightIE(PluralsightBaseIE): | ||||
|             post_url = compat_urlparse.urljoin(self._LOGIN_URL, post_url) | ||||
|  | ||||
|         response = self._download_webpage( | ||||
|             post_url, None, 'Logging in as %s' % username, | ||||
|             post_url, None, 'Logging in', | ||||
|             data=urlencode_postdata(login_form), | ||||
|             headers={'Content-Type': 'application/x-www-form-urlencoded'}) | ||||
|  | ||||
| @@ -131,6 +131,13 @@ class PluralsightIE(PluralsightBaseIE): | ||||
|             if BLOCKED in response: | ||||
|                 raise ExtractorError( | ||||
|                     'Unable to login: %s' % BLOCKED, expected=True) | ||||
|             MUST_AGREE = 'To continue using Pluralsight, you must agree to' | ||||
|             if any(p in response for p in (MUST_AGREE, '>Disagree<', '>Agree<')): | ||||
|                 raise ExtractorError( | ||||
|                     'Unable to login: %s some documents. Go to pluralsight.com, ' | ||||
|                     'log in and agree with what Pluralsight requires.' | ||||
|                     % MUST_AGREE, expected=True) | ||||
|  | ||||
|             raise ExtractorError('Unable to log in') | ||||
|  | ||||
|     def _get_subtitles(self, author, clip_id, lang, name, duration, video_id): | ||||
|   | ||||
| @@ -77,12 +77,14 @@ class PornComIE(InfoExtractor): | ||||
|         self._sort_formats(formats) | ||||
|  | ||||
|         view_count = str_to_int(self._search_regex( | ||||
|             r'class=["\']views["\'][^>]*><p>([\d,.]+)', webpage, | ||||
|             (r'Views:\s*</span>\s*<span>\s*([\d,.]+)', | ||||
|              r'class=["\']views["\'][^>]*><p>([\d,.]+)'), webpage, | ||||
|             'view count', fatal=False)) | ||||
|  | ||||
|         def extract_list(kind): | ||||
|             s = self._search_regex( | ||||
|                 r'(?s)<p[^>]*>%s:(.+?)</p>' % kind.capitalize(), | ||||
|                 (r'(?s)%s:\s*</span>\s*<span>(.+?)</span>' % kind.capitalize(), | ||||
|                  r'(?s)<p[^>]*>%s:(.+?)</p>' % kind.capitalize()), | ||||
|                 webpage, kind, fatal=False) | ||||
|             return re.findall(r'<a[^>]+>([^<]+)</a>', s or '') | ||||
|  | ||||
|   | ||||
| @@ -17,6 +17,7 @@ from ..utils import ( | ||||
|     parse_duration, | ||||
|     strip_or_none, | ||||
|     try_get, | ||||
|     unescapeHTML, | ||||
|     unified_strdate, | ||||
|     unified_timestamp, | ||||
|     update_url_query, | ||||
| @@ -249,6 +250,41 @@ class RaiPlayLiveIE(RaiBaseIE): | ||||
|         } | ||||
|  | ||||
|  | ||||
| class RaiPlayPlaylistIE(InfoExtractor): | ||||
|     _VALID_URL = r'https?://(?:www\.)?raiplay\.it/programmi/(?P<id>[^/?#&]+)' | ||||
|     _TESTS = [{ | ||||
|         'url': 'http://www.raiplay.it/programmi/nondirloalmiocapo/', | ||||
|         'info_dict': { | ||||
|             'id': 'nondirloalmiocapo', | ||||
|             'title': 'Non dirlo al mio capo', | ||||
|             'description': 'md5:9f3d603b2947c1c7abb098f3b14fac86', | ||||
|         }, | ||||
|         'playlist_mincount': 12, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|         playlist_id = self._match_id(url) | ||||
|  | ||||
|         webpage = self._download_webpage(url, playlist_id) | ||||
|  | ||||
|         title = self._html_search_meta( | ||||
|             ('programma', 'nomeProgramma'), webpage, 'title') | ||||
|         description = unescapeHTML(self._html_search_meta( | ||||
|             ('description', 'og:description'), webpage, 'description')) | ||||
|         print(description) | ||||
|  | ||||
|         entries = [] | ||||
|         for mobj in re.finditer( | ||||
|                 r'<a\b[^>]+\bhref=(["\'])(?P<path>/raiplay/video/.+?)\1', | ||||
|                 webpage): | ||||
|             video_url = urljoin(url, mobj.group('path')) | ||||
|             entries.append(self.url_result( | ||||
|                 video_url, ie=RaiPlayIE.ie_key(), | ||||
|                 video_id=RaiPlayIE._match_id(video_url))) | ||||
|  | ||||
|         return self.playlist_result(entries, playlist_id, title, description) | ||||
|  | ||||
|  | ||||
| class RaiIE(RaiBaseIE): | ||||
|     _VALID_URL = r'https?://[^/]+\.(?:rai\.(?:it|tv)|rainews\.it)/dl/.+?-(?P<id>%s)(?:-.+?)?\.html' % RaiBaseIE._UUID_RE | ||||
|     _TESTS = [{ | ||||
|   | ||||
| @@ -68,7 +68,7 @@ class RoosterTeethIE(InfoExtractor): | ||||
|  | ||||
|         login_request = self._download_webpage( | ||||
|             self._LOGIN_URL, None, | ||||
|             note='Logging in as %s' % username, | ||||
|             note='Logging in', | ||||
|             data=urlencode_postdata(login_form), | ||||
|             headers={ | ||||
|                 'Referer': self._LOGIN_URL, | ||||
|   | ||||
| @@ -21,7 +21,7 @@ class RozhlasIE(InfoExtractor): | ||||
|         } | ||||
|     }, { | ||||
|         'url': 'http://prehravac.rozhlas.cz/audio/3421320/embed', | ||||
|         'skip_download': True, | ||||
|         'only_matching': True, | ||||
|     }] | ||||
|  | ||||
|     def _real_extract(self, url): | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user