Compare commits
302 Commits
2017.04.26
...
2017.06.23
Author | SHA1 | Date | |
---|---|---|---|
![]() |
170719414d | ||
![]() |
38dad4737f | ||
![]() |
ddbb4c5c3e | ||
![]() |
fa3ea7223a | ||
![]() |
0f4a5a73e7 | ||
![]() |
18166bb8e8 | ||
![]() |
d4893e764b | ||
![]() |
97b6e30113 | ||
![]() |
9be9ec5980 | ||
![]() |
048b55804d | ||
![]() |
6ce79d7ac0 | ||
![]() |
1641ca402d | ||
![]() |
85cbcede5b | ||
![]() |
a1de83e5f0 | ||
![]() |
fee00b3884 | ||
![]() |
2d2132ac6e | ||
![]() |
cc2ffe5afe | ||
![]() |
560050669b | ||
![]() |
eaa006d1bd | ||
![]() |
a6f29820c6 | ||
![]() |
1433734c35 | ||
![]() |
aefce8e6dc | ||
![]() |
8b6ac49ecc | ||
![]() |
b08e235f09 | ||
![]() |
be80986ed9 | ||
![]() |
473e87064b | ||
![]() |
4f90d2aeac | ||
![]() |
b230fefc3c | ||
![]() |
96a2daa1ee | ||
![]() |
0ea6efbb7a | ||
![]() |
6a9cb29509 | ||
![]() |
ca27037171 | ||
![]() |
0bf4b71b75 | ||
![]() |
5215f45327 | ||
![]() |
0a268c6e11 | ||
![]() |
7dd5415cd0 | ||
![]() |
b5dc33daa9 | ||
![]() |
97fa1f8dc4 | ||
![]() |
b081f53b08 | ||
![]() |
cb1e6d8985 | ||
![]() |
9932ac5c58 | ||
![]() |
bf87c36c93 | ||
![]() |
b4a3d461e4 | ||
![]() |
72b409559c | ||
![]() |
534863e057 | ||
![]() |
16bc958287 | ||
![]() |
624bd0104c | ||
![]() |
28a4d6cce8 | ||
![]() |
2ae2ffda5e | ||
![]() |
70e7967202 | ||
![]() |
6e999fbc12 | ||
![]() |
7409af9eb3 | ||
![]() |
4e3637034c | ||
![]() |
1afd0b0da7 | ||
![]() |
7515830422 | ||
![]() |
f5521ea209 | ||
![]() |
34646967ba | ||
![]() |
e4d2e76d8e | ||
![]() |
87f5646937 | ||
![]() |
cc69a3de1b | ||
![]() |
15aeeb1188 | ||
![]() |
1693bebe4d | ||
![]() |
4244a13a1d | ||
![]() |
931adf8cc1 | ||
![]() |
c996943418 | ||
![]() |
76e6378358 | ||
![]() |
a355b57f58 | ||
![]() |
1508da30c2 | ||
![]() |
eb703e5380 | ||
![]() |
0a3924e746 | ||
![]() |
e1db730d86 | ||
![]() |
537191826f | ||
![]() |
130880ba48 | ||
![]() |
f8ba3fda4d | ||
![]() |
e1b90cc3db | ||
![]() |
43e6579558 | ||
![]() |
6d923aab35 | ||
![]() |
62bafabc09 | ||
![]() |
9edcdac90c | ||
![]() |
cd138d8bd4 | ||
![]() |
cd750b731c | ||
![]() |
4bede0d8f5 | ||
![]() |
f129c3f349 | ||
![]() |
39d4c1be4d | ||
![]() |
f7a747ce59 | ||
![]() |
4489d41816 | ||
![]() |
87b5184a0d | ||
![]() |
c56ad5c975 | ||
![]() |
6b7ce85cdc | ||
![]() |
d10d0e3cf8 | ||
![]() |
941ea38ef5 | ||
![]() |
99bea8d298 | ||
![]() |
a49eccdfa7 | ||
![]() |
a846173d93 | ||
![]() |
78e210dea5 | ||
![]() |
8555204274 | ||
![]() |
164fcbfeb7 | ||
![]() |
bc22df29c4 | ||
![]() |
7e688d2f6a | ||
![]() |
5a6d1da442 | ||
![]() |
703751add4 | ||
![]() |
4050be78e5 | ||
![]() |
4d9fc40100 | ||
![]() |
765522345f | ||
![]() |
6bceb36b99 | ||
![]() |
1e0d65f0bd | ||
![]() |
03327bc9a6 | ||
![]() |
b407d8533d | ||
![]() |
20e2c9de04 | ||
![]() |
d16c0121b9 | ||
![]() |
7f4c3a7439 | ||
![]() |
28dbde9cc3 | ||
![]() |
cc304ce588 | ||
![]() |
98a0618941 | ||
![]() |
fd545fc6d1 | ||
![]() |
97067db2ae | ||
![]() |
c130f0a37b | ||
![]() |
d3d4ba7f24 | ||
![]() |
5552c9eb0f | ||
![]() |
59ed87cbd9 | ||
![]() |
b7f8749304 | ||
![]() |
5192ee17e7 | ||
![]() |
e834f04400 | ||
![]() |
884d09f330 | ||
![]() |
9e35298f97 | ||
![]() |
0551f1b07b | ||
![]() |
de53511201 | ||
![]() |
2570e85167 | ||
![]() |
9dc5ab041f | ||
![]() |
01f3c8e290 | ||
![]() |
06c1b3ce07 | ||
![]() |
0b75e42dfb | ||
![]() |
a609e61a90 | ||
![]() |
afdb387cd8 | ||
![]() |
dc4e4f90a2 | ||
![]() |
fdc20f87a6 | ||
![]() |
35a2d221a3 | ||
![]() |
daa4e9ff90 | ||
![]() |
2ca29f1aaf | ||
![]() |
77d682da9d | ||
![]() |
8fffac6927 | ||
![]() |
5f6fbcea08 | ||
![]() |
00cb0faca8 | ||
![]() |
bfdf6fcc66 | ||
![]() |
bcaa1dd060 | ||
![]() |
0e2d626ddd | ||
![]() |
9221d5d7a8 | ||
![]() |
9d63e57d1f | ||
![]() |
3bc1eea0d8 | ||
![]() |
7769f83701 | ||
![]() |
650bd94716 | ||
![]() |
36b226d48f | ||
![]() |
f2e2f0c777 | ||
![]() |
6f76679804 | ||
![]() |
7073015a23 | ||
![]() |
89fd03079b | ||
![]() |
1c45b7a8a9 | ||
![]() |
60f5c9fb19 | ||
![]() |
c360e641e9 | ||
![]() |
6f3c632c24 | ||
![]() |
09b866e171 | ||
![]() |
166d12b00c | ||
![]() |
2b8e6a68f8 | ||
![]() |
d105a7edc6 | ||
![]() |
5d29af3d15 | ||
![]() |
ca04de463d | ||
![]() |
946826eec7 | ||
![]() |
76d5a36391 | ||
![]() |
56f9c77f0e | ||
![]() |
0de136341a | ||
![]() |
1339ecb2f8 | ||
![]() |
efe9316703 | ||
![]() |
851a01aed6 | ||
![]() |
b845766597 | ||
![]() |
fa26734e07 | ||
![]() |
12f01118b0 | ||
![]() |
7fc60f4ee9 | ||
![]() |
58bb440283 | ||
![]() |
7ad4362357 | ||
![]() |
6c52477f59 | ||
![]() |
116283ff64 | ||
![]() |
7274f3d0e9 | ||
![]() |
3166b1f0ac | ||
![]() |
39ee263819 | ||
![]() |
a7ed6b341c | ||
![]() |
cbd84b5817 | ||
![]() |
6d1ded7502 | ||
![]() |
5d0968f0af | ||
![]() |
8d65880e24 | ||
![]() |
b972fb037b | ||
![]() |
5996d21aea | ||
![]() |
afa0200bf0 | ||
![]() |
e9137224b3 | ||
![]() |
804181dda9 | ||
![]() |
8fa17117df | ||
![]() |
3b859145c2 | ||
![]() |
04c09f1961 | ||
![]() |
bf82b87323 | ||
![]() |
b6eb74e340 | ||
![]() |
3d40084b83 | ||
![]() |
52294cdda7 | ||
![]() |
2eeb588efe | ||
![]() |
4ac0f573ef | ||
![]() |
3892a9f4ab | ||
![]() |
3995d37da5 | ||
![]() |
e4a75d7932 | ||
![]() |
e00eb564e9 | ||
![]() |
10c87c151b | ||
![]() |
228cd9bb90 | ||
![]() |
566fbbaefd | ||
![]() |
74c09c852a | ||
![]() |
fd178b8748 | ||
![]() |
a57a8e9918 | ||
![]() |
1f9fefe7f5 | ||
![]() |
8b4774dcac | ||
![]() |
a99cc4ca16 | ||
![]() |
9cafc3fd8b | ||
![]() |
329e3dd5ad | ||
![]() |
1d9e0a4f40 | ||
![]() |
7ad53cb7ff | ||
![]() |
b2ad479d17 | ||
![]() |
4ac6dc3732 | ||
![]() |
cc7bda4fff | ||
![]() |
50ad078b7b | ||
![]() |
4947f13cd0 | ||
![]() |
7f09e523e8 | ||
![]() |
4fe14732a2 | ||
![]() |
ff6f9a6704 | ||
![]() |
0c26548601 | ||
![]() |
5401bea27f | ||
![]() |
7a6d33a9a5 | ||
![]() |
fa2a36d9bc | ||
![]() |
55949fede6 | ||
![]() |
7fc875195f | ||
![]() |
c6fe5a7e12 | ||
![]() |
ae21d2fd94 | ||
![]() |
77481f1386 | ||
![]() |
d86d169dd5 | ||
![]() |
b9f9f361fa | ||
![]() |
ab39a25c75 | ||
![]() |
a146fa1c68 | ||
![]() |
e0c1e9a98c | ||
![]() |
086041e2f8 | ||
![]() |
74da856544 | ||
![]() |
9edf47df7b | ||
![]() |
238cec17ae | ||
![]() |
50534b7158 | ||
![]() |
9cd4209724 | ||
![]() |
33a81c2c6f | ||
![]() |
deef31955b | ||
![]() |
9dac2cec2d | ||
![]() |
6ec371cd9e | ||
![]() |
13081db1f5 | ||
![]() |
b07ea5eaec | ||
![]() |
5599253009 | ||
![]() |
98ce1a3fd3 | ||
![]() |
ba5c3caf88 | ||
![]() |
b5c39537be | ||
![]() |
1c7c76e4fb | ||
![]() |
557194591a | ||
![]() |
27e70a8f6c | ||
![]() |
a4c81e4968 | ||
![]() |
7986c3abcd | ||
![]() |
a1ebfd4494 | ||
![]() |
d19093bd50 | ||
![]() |
24eb7c2578 | ||
![]() |
e7db6759e4 | ||
![]() |
b364c87c42 | ||
![]() |
9222d94510 | ||
![]() |
edd9221cd2 | ||
![]() |
bc8a2ea071 | ||
![]() |
7527923371 | ||
![]() |
20783b8b50 | ||
![]() |
bf2a5555c0 | ||
![]() |
fb8e8b2d16 | ||
![]() |
b62985a9a5 | ||
![]() |
e31fed95b4 | ||
![]() |
3fd0f70f6a | ||
![]() |
33c62efc32 | ||
![]() |
6b4ddd336c | ||
![]() |
c12b4b80f8 | ||
![]() |
064fafe932 | ||
![]() |
ac1a5b9a12 | ||
![]() |
a15777491a | ||
![]() |
d8571dd6bf | ||
![]() |
c0fa4245ce | ||
![]() |
8814ae42bc | ||
![]() |
0f63dc2402 | ||
![]() |
dde97ea8da | ||
![]() |
30bb6ce1a4 | ||
![]() |
c89b49f743 | ||
![]() |
6f4a888416 | ||
![]() |
f5edd7ae51 | ||
![]() |
96820c1c6b | ||
![]() |
c95e2b5911 | ||
![]() |
374560f018 | ||
![]() |
ff99fe529e | ||
![]() |
e095109da1 | ||
![]() |
d68afc5bc9 | ||
![]() |
76c1951036 | ||
![]() |
e8bfe2a946 | ||
![]() |
3dc8b61b7f |
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.04.26*. 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.
|
### Make sure you are using the *latest* version: run `youtube-dl --version` and ensure your version is *2017.06.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.04.26**
|
- [ ] I've **verified** and **I assure** that I'm running youtube-dl **2017.06.23**
|
||||||
|
|
||||||
### Before submitting an *issue* make sure you have:
|
### Before submitting an *issue* make sure you have:
|
||||||
- [ ] At least skimmed through [README](https://github.com/rg3/youtube-dl/blob/master/README.md) and **most notably** [FAQ](https://github.com/rg3/youtube-dl#faq) and [BUGS](https://github.com/rg3/youtube-dl#bugs) sections
|
- [ ] At least skimmed through [README](https://github.com/rg3/youtube-dl/blob/master/README.md) and **most notably** [FAQ](https://github.com/rg3/youtube-dl#faq) and [BUGS](https://github.com/rg3/youtube-dl#bugs) sections
|
||||||
@@ -35,7 +35,7 @@ $ youtube-dl -v <your command line>
|
|||||||
[debug] User config: []
|
[debug] User config: []
|
||||||
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
[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] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
||||||
[debug] youtube-dl version 2017.04.26
|
[debug] youtube-dl version 2017.06.23
|
||||||
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
[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] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
||||||
[debug] Proxy map: {}
|
[debug] Proxy map: {}
|
||||||
|
8
AUTHORS
8
AUTHORS
@@ -212,3 +212,11 @@ Xiao Di Guan
|
|||||||
Thomas Winant
|
Thomas Winant
|
||||||
Daniel Twardowski
|
Daniel Twardowski
|
||||||
Jeremie Jarosh
|
Jeremie Jarosh
|
||||||
|
Gerard Rovira
|
||||||
|
Marvin Ewald
|
||||||
|
Frédéric Bournival
|
||||||
|
Timendum
|
||||||
|
gritstub
|
||||||
|
Adam Voss
|
||||||
|
Mike Fährmann
|
||||||
|
Jan Kundrát
|
||||||
|
312
ChangeLog
312
ChangeLog
@@ -1,3 +1,297 @@
|
|||||||
|
version 2017.06.23
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [adobepass] Fix extraction on older python 2.6
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [youtube] Adapt to new automatic captions rendition (#13467)
|
||||||
|
* [hgtv.com:show] Relax video config regular expression (#13279, #13461)
|
||||||
|
* [drtuber] Fix formats extraction (#12058)
|
||||||
|
* [youporn] Fix upload date extraction
|
||||||
|
* [youporn] Improve formats extraction
|
||||||
|
* [youporn] Fix title extraction (#13456)
|
||||||
|
* [googledrive] Fix formats sorting (#13443)
|
||||||
|
* [watchindianporn] Fix extraction (#13411, #13415)
|
||||||
|
+ [vimeo] Add fallback mp4 extension for original format
|
||||||
|
+ [ruv] Add support for ruv.is (#13396)
|
||||||
|
* [viu] Fix extraction on older python 2.6
|
||||||
|
* [pandora.tv] Fix upload_date extraction (#12846)
|
||||||
|
+ [asiancrush] Add support for asiancrush.com (#13420)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.06.18
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [downloader/common] Use utils.shell_quote for debug command line
|
||||||
|
* [utils] Use compat_shlex_quote in shell_quote
|
||||||
|
* [postprocessor/execafterdownload] Encode command line (#13407)
|
||||||
|
* [compat] Fix compat_shlex_quote on Windows (#5889, #10254)
|
||||||
|
* [postprocessor/metadatafromtitle] Fix missing optional meta fields processing
|
||||||
|
in --metadata-from-title (#13408)
|
||||||
|
* [extractor/common] Fix json dumping with --geo-bypass
|
||||||
|
+ [extractor/common] Improve jwplayer subtitles extraction
|
||||||
|
+ [extractor/common] Improve jwplayer formats extraction (#13379)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [polskieradio] Fix extraction (#13392)
|
||||||
|
+ [xfileshare] Add support for fastvideo.me (#13385)
|
||||||
|
* [bilibili] Fix extraction of videos with double quotes in titles (#13387)
|
||||||
|
* [4tube] Fix extraction (#13381, #13382)
|
||||||
|
+ [disney] Add support for disneychannel.de (#13383)
|
||||||
|
* [npo] Improve URL regular expression (#13376)
|
||||||
|
+ [corus] Add support for showcase.ca
|
||||||
|
+ [corus] Add support for history.ca (#13359)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.06.12
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [utils] Handle compat_HTMLParseError in extract_attributes (#13349)
|
||||||
|
+ [compat] Introduce compat_HTMLParseError
|
||||||
|
* [utils] Improve unified_timestamp
|
||||||
|
* [extractor/generic] Ensure format id is unicode string
|
||||||
|
* [extractor/common] Return unicode string from _match_id
|
||||||
|
+ [YoutubeDL] Sanitize more fields (#13313)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [xfileshare] Add support for rapidvideo.tv (#13348)
|
||||||
|
* [xfileshare] Modernize and pass Referer
|
||||||
|
+ [rutv] Add support for testplayer.vgtrk.com (#13347)
|
||||||
|
+ [newgrounds] Extract more metadata (#13232)
|
||||||
|
+ [newgrounds:playlist] Add support for playlists (#10611)
|
||||||
|
* [newgrounds] Improve formats and uploader extraction (#13346)
|
||||||
|
* [msn] Fix formats extraction
|
||||||
|
* [turbo] Ensure format id is string
|
||||||
|
* [sexu] Ensure height is int
|
||||||
|
* [jove] Ensure comment count is int
|
||||||
|
* [golem] Ensure format id is string
|
||||||
|
* [gfycat] Ensure filesize is int
|
||||||
|
* [foxgay] Ensure height is int
|
||||||
|
* [flickr] Ensure format id is string
|
||||||
|
* [sohu] Fix numeric fields
|
||||||
|
* [safari] Improve authentication detection (#13319)
|
||||||
|
* [liveleak] Ensure height is int (#13313)
|
||||||
|
* [streamango] Make title optional (#13292)
|
||||||
|
* [rtlnl] Improve URL regular expression (#13295)
|
||||||
|
* [tvplayer] Fix extraction (#13291)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.06.05
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [YoutubeDL] Don't emit ANSI escape codes on Windows (#13270)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [bandcamp:weekly] Add support for bandcamp weekly (#12758)
|
||||||
|
* [pornhub:playlist] Fix extraction (#13281)
|
||||||
|
- [godtv] Remove extractor (#13175)
|
||||||
|
* [safari] Fix typo (#13252)
|
||||||
|
* [youtube] Improve chapters extraction (#13247)
|
||||||
|
* [1tv] Lower preference for HTTP formats (#13246)
|
||||||
|
* [francetv] Relax URL regular expression
|
||||||
|
* [drbonanza] Fix extraction (#13231)
|
||||||
|
* [packtpub] Fix authentication (#13240)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.05.29
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [youtube] Fix DASH MPD extraction for videos with non-encrypted format URLs
|
||||||
|
(#13211)
|
||||||
|
* [xhamster] Fix uploader and like/dislike count extraction (#13216))
|
||||||
|
+ [xhamster] Extract categories (#11728)
|
||||||
|
+ [abcnews] Add support for embed URLs (#12851)
|
||||||
|
* [gaskrank] Fix extraction (#12493)
|
||||||
|
* [medialaan] Fix videos with missing videoUrl (#12774)
|
||||||
|
* [dvtv] Fix playlist support
|
||||||
|
+ [dvtv] Add support for DASH and HLS formats (#3063)
|
||||||
|
+ [beam:vod] Add support for beam.pro/mixer.com VODs (#13032))
|
||||||
|
* [cbsinteractive] Relax URL regular expression (#13213)
|
||||||
|
* [adn] Fix formats extraction
|
||||||
|
+ [youku] Extract more metadata (#10433)
|
||||||
|
* [cbsnews] Fix extraction (#13205)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.05.26
|
||||||
|
|
||||||
|
Core
|
||||||
|
+ [utils] strip_jsonp() can recognize more patterns
|
||||||
|
* [postprocessor/ffmpeg] Fix metadata filename handling on Python 2 (#13182)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [youtube] DASH MPDs with cipher signatures are recognized now (#11381)
|
||||||
|
+ [bbc] Add support for authentication
|
||||||
|
* [tudou] Merge into youku extractor (#12214)
|
||||||
|
* [youku:show] Fix extraction
|
||||||
|
* [youku] Fix extraction (#13191)
|
||||||
|
* [udemy] Fix extraction for outputs' format entries without URL (#13192)
|
||||||
|
* [vimeo] Fix formats' sorting (#13189)
|
||||||
|
* [cbsnews] Fix extraction for 60 Minutes videos (#12861)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.05.23
|
||||||
|
|
||||||
|
Core
|
||||||
|
+ [downloader/external] Pass -loglevel to ffmpeg downloader (#13183)
|
||||||
|
+ [adobepass] Add support for Bright House Networks (#13149)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [streamcz] Add support for subtitles (#13174)
|
||||||
|
* [youtube] Fix DASH manifest signature decryption (#8944, #13156)
|
||||||
|
* [toggle] Relax URL regular expression (#13172)
|
||||||
|
* [toypics] Fix extraction (#13077)
|
||||||
|
* [njpwworld] Fix extraction (#13162, #13169)
|
||||||
|
+ [hitbox] Add support for smashcast.tv (#13154)
|
||||||
|
* [mitele] Update app key regular expression (#13158)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.05.18.1
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [jsinterp] Fix typo and cleanup regular expressions (#13134)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.05.18
|
||||||
|
|
||||||
|
Core
|
||||||
|
+ [jsinterp] Add support for quoted names and indexers (#13123, #13124, #13125,
|
||||||
|
#13126, #13128, #13129, #13130, #13131, #13132)
|
||||||
|
+ [extractor/common] Add support for schemeless URLs in _extract_wowza_formats
|
||||||
|
(#13088, #13092)
|
||||||
|
+ [utils] Recognize more audio codecs (#13081)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [vier] Extract more metadata (#12539)
|
||||||
|
* [vier] Improve extraction (#12801)
|
||||||
|
+ Add support for authentication
|
||||||
|
* Bypass authentication when no credentials provided
|
||||||
|
* Improve extraction robustness
|
||||||
|
* [dailymail] Fix sources extraction (#13057)
|
||||||
|
* [dailymotion] Extend URL regular expression (#13079)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.05.14
|
||||||
|
|
||||||
|
Core
|
||||||
|
+ [extractor/common] Respect Width and Height attributes in ISM manifests
|
||||||
|
+ [postprocessor/metadatafromtitle] Add support regular expression syntax for
|
||||||
|
--metadata-from-title (#13065)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [mediaset] Add support for video.mediaset.it (#12708, #12964)
|
||||||
|
* [orf:radio] Fix extraction (#11643, #12926)
|
||||||
|
* [aljazeera] Extend URL regular expression (#13053)
|
||||||
|
* [imdb] Relax URL regular expression (#13056)
|
||||||
|
+ [francetv] Add support for mobile.france.tv (#13068)
|
||||||
|
+ [upskill] Add support for upskillcourses.com (#13043)
|
||||||
|
* [thescene] Fix extraction (#13061)
|
||||||
|
* [condenast] Improve embed support
|
||||||
|
* [liveleak] Fix extraction (#12053)
|
||||||
|
+ [douyu] Support Douyu shows (#12228)
|
||||||
|
* [myspace] Improve URL regular expression (#13040)
|
||||||
|
* [adultswim] Use desktop platform in assets URL (#13041)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.05.09
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [YoutubeDL] Force --restrict-filenames when no locale is set on all python
|
||||||
|
versions (#13027)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [francetv] Adapt to site redesign (#13034)
|
||||||
|
+ [packtpub] Add support for authentication (#12622)
|
||||||
|
* [drtv] Lower preference for SignLanguage formats (#13013, #13016)
|
||||||
|
+ [cspan] Add support for brightcove live embeds (#13028)
|
||||||
|
* [vrv] Extract DASH formats and subtitles
|
||||||
|
* [funimation] Fix authentication (#13021)
|
||||||
|
* [adultswim] Fix extraction (#8640, #10950, #11042, #12121)
|
||||||
|
+ Add support for Adobe Pass authentication
|
||||||
|
+ Add support for live streams
|
||||||
|
+ Add support for show pages
|
||||||
|
* [turner] Extract thumbnail, is_live and strip description
|
||||||
|
+ [nonktube] Add support for nonktube.com (#8647, #13024)
|
||||||
|
+ [nuevo] Pass headers to _extract_nuevo
|
||||||
|
* [nbc] Improve extraction (#12364)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.05.07
|
||||||
|
|
||||||
|
Common
|
||||||
|
* [extractor/common] Fix typo in _extract_akamai_formats
|
||||||
|
+ [postprocessor/ffmpeg] Embed chapters into media file with --add-metadata
|
||||||
|
+ [extractor/common] Introduce chapters meta field
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [youtube] Fix authentication (#12820, #12927, #12973, #12992, #12993, #12995,
|
||||||
|
#13003)
|
||||||
|
* [bilibili] Fix video downloading (#13001)
|
||||||
|
* [rmcdecouverte] Fix extraction (#12937)
|
||||||
|
* [theplatform] Extract chapters
|
||||||
|
* [bandcamp] Fix thumbnail extraction (#12980)
|
||||||
|
* [pornhub] Extend URL regular expression (#12996)
|
||||||
|
+ [youtube] Extract chapters
|
||||||
|
+ [nrk] Extract chapters
|
||||||
|
+ [vice] Add support for ooyala embeds in article pages
|
||||||
|
+ [vice] Support vice articles (#12968)
|
||||||
|
* [vice] Fix extraction for non en_us videos (#12967)
|
||||||
|
* [gdcvault] Fix extraction for some videos (#12733)
|
||||||
|
* [pbs] Improve multipart video support (#12981)
|
||||||
|
* [laola1tv] Fix extraction (#12880)
|
||||||
|
+ [cda] Support birthday verification (#12789)
|
||||||
|
* [leeco] Fix extraction (#12974)
|
||||||
|
+ [pbs] Extract chapters
|
||||||
|
* [amp] Imporove thumbnail and subtitles extraction
|
||||||
|
* [foxsports] Fix extraction (#12945)
|
||||||
|
- [coub] Remove comment count extraction (#12941)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.05.01
|
||||||
|
|
||||||
|
Core
|
||||||
|
+ [extractor/common] Extract view count from JSON-LD
|
||||||
|
* [utils] Improve unified_timestamp
|
||||||
|
+ [utils] Add video/mp2t to mimetype2ext
|
||||||
|
* [downloader/external] Properly handle live stream downloading cancellation
|
||||||
|
(#8932)
|
||||||
|
+ [utils] Add support for unicode whitespace in clean_html on python 2 (#12906)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [infoq] Make audio format extraction non fatal (#12938)
|
||||||
|
* [brightcove] Allow whitespace around attribute names in embedded code
|
||||||
|
+ [zaq1] Add support for zaq1.pl (#12693)
|
||||||
|
+ [xvideos] Extract duration (#12828)
|
||||||
|
* [vevo] Fix extraction (#12879)
|
||||||
|
+ [noovo] Add support for noovo.ca (#12792)
|
||||||
|
+ [washingtonpost] Add support for embeds (#12699)
|
||||||
|
* [yandexmusic:playlist] Fix extraction for python 3 (#12888)
|
||||||
|
* [anvato] Improve extraction (#12913)
|
||||||
|
* Promote to regular shortcut based extractor
|
||||||
|
* Add mcp to access key mapping table
|
||||||
|
* Add support for embeds extraction
|
||||||
|
* Add support for anvato embeds in generic extractor
|
||||||
|
* [xtube] Fix extraction for older FLV videos (#12734)
|
||||||
|
* [tvplayer] Fix extraction (#12908)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.04.28
|
||||||
|
|
||||||
|
Core
|
||||||
|
+ [adobepass] Use geo verification headers for all requests
|
||||||
|
- [downloader/fragment] Remove assert for resume_len when no fragments
|
||||||
|
downloaded
|
||||||
|
+ [extractor/common] Add manifest_url for explicit group rendition formats
|
||||||
|
* [extractor/common] Fix manifest_url for m3u8 formats
|
||||||
|
- [extractor/common] Don't list master m3u8 playlists in format list (#12832)
|
||||||
|
|
||||||
|
Extractor
|
||||||
|
* [aenetworks] Fix extraction for shows with single season
|
||||||
|
+ [go] Add support for Disney, DisneyJunior and DisneyXD show pages
|
||||||
|
* [youtube] Recognize new locale-based player URLs (#12885)
|
||||||
|
+ [streamable] Add support for new embedded URL schema (#12844)
|
||||||
|
* [arte:+7] Relax URL regular expression (#12837)
|
||||||
|
|
||||||
|
|
||||||
version 2017.04.26
|
version 2017.04.26
|
||||||
|
|
||||||
Core
|
Core
|
||||||
@@ -6,19 +300,19 @@ Core
|
|||||||
* [YoutubeDL] Fix output template for missing timestamp (#12796)
|
* [YoutubeDL] Fix output template for missing timestamp (#12796)
|
||||||
* [socks] Handle cases where credentials are required but missing
|
* [socks] Handle cases where credentials are required but missing
|
||||||
* [extractor/common] Improve HLS extraction (#12211)
|
* [extractor/common] Improve HLS extraction (#12211)
|
||||||
- Extract m3u8 parsing to separate method
|
* Extract m3u8 parsing to separate method
|
||||||
- Improve rendition groups extraction
|
* Improve rendition groups extraction
|
||||||
- Build stream name according stream GROUP-ID
|
* Build stream name according stream GROUP-ID
|
||||||
- Ignore reference to AUDIO group without URI when stream has no CODECS
|
* Ignore reference to AUDIO group without URI when stream has no CODECS
|
||||||
- Use float for scaled tbr in _parse_m3u8_formats
|
* Use float for scaled tbr in _parse_m3u8_formats
|
||||||
* [utils] Add support for TTML styles in dfxp2srt
|
* [utils] Add support for TTML styles in dfxp2srt
|
||||||
* [downloader/hls] No need to download keys for fragments that have been
|
* [downloader/hls] No need to download keys for fragments that have been
|
||||||
already downloaded
|
already downloaded
|
||||||
* [downloader/fragment] Improve fragment downloading
|
* [downloader/fragment] Improve fragment downloading
|
||||||
- Resume immediately
|
* Resume immediately
|
||||||
- Don't concatenate fragments and decrypt them on every resume
|
* Don't concatenate fragments and decrypt them on every resume
|
||||||
- Optimize disk storage usage, don't store intermediate fragments on disk
|
* Optimize disk storage usage, don't store intermediate fragments on disk
|
||||||
- Store bookkeeping download state file
|
* Store bookkeeping download state file
|
||||||
+ [extractor/common] Add support for multiple getters in try_get
|
+ [extractor/common] Add support for multiple getters in try_get
|
||||||
+ [extractor/common] Add support for video of WebPage context in _json_ld
|
+ [extractor/common] Add support for video of WebPage context in _json_ld
|
||||||
(#12778)
|
(#12778)
|
||||||
|
2
Makefile
2
Makefile
@@ -101,7 +101,7 @@ youtube-dl.tar.gz: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-
|
|||||||
--exclude '*.pyc' \
|
--exclude '*.pyc' \
|
||||||
--exclude '*.pyo' \
|
--exclude '*.pyo' \
|
||||||
--exclude '*~' \
|
--exclude '*~' \
|
||||||
--exclude '__pycache' \
|
--exclude '__pycache__' \
|
||||||
--exclude '.git' \
|
--exclude '.git' \
|
||||||
--exclude 'testdata' \
|
--exclude 'testdata' \
|
||||||
--exclude 'docs/_build' \
|
--exclude 'docs/_build' \
|
||||||
|
53
README.md
53
README.md
@@ -145,18 +145,18 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo
|
|||||||
--max-views COUNT Do not download any videos with more than
|
--max-views COUNT Do not download any videos with more than
|
||||||
COUNT views
|
COUNT views
|
||||||
--match-filter FILTER Generic video filter. Specify any key (see
|
--match-filter FILTER Generic video filter. Specify any key (see
|
||||||
help for -o for a list of available keys)
|
the "OUTPUT TEMPLATE" for a list of
|
||||||
to match if the key is present, !key to
|
available keys) to match if the key is
|
||||||
check if the key is not present, key >
|
present, !key to check if the key is not
|
||||||
NUMBER (like "comment_count > 12", also
|
present, key > NUMBER (like "comment_count
|
||||||
works with >=, <, <=, !=, =) to compare
|
> 12", also works with >=, <, <=, !=, =) to
|
||||||
against a number, key = 'LITERAL' (like
|
compare against a number, key = 'LITERAL'
|
||||||
"uploader = 'Mike Smith'", also works with
|
(like "uploader = 'Mike Smith'", also works
|
||||||
!=) to match against a string literal and &
|
with !=) to match against a string literal
|
||||||
to require multiple matches. Values which
|
and & to require multiple matches. Values
|
||||||
are not known are excluded unless you put a
|
which are not known are excluded unless you
|
||||||
question mark (?) after the operator. For
|
put a question mark (?) after the operator.
|
||||||
example, to only match videos that have
|
For example, to only match videos that have
|
||||||
been liked more than 100 times and disliked
|
been liked more than 100 times and disliked
|
||||||
less than 50 times (or the dislike
|
less than 50 times (or the dislike
|
||||||
functionality is not available at the given
|
functionality is not available at the given
|
||||||
@@ -277,8 +277,8 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo
|
|||||||
--get-filename Simulate, quiet but print output filename
|
--get-filename Simulate, quiet but print output filename
|
||||||
--get-format Simulate, quiet but print output format
|
--get-format Simulate, quiet but print output format
|
||||||
-j, --dump-json Simulate, quiet but print JSON information.
|
-j, --dump-json Simulate, quiet but print JSON information.
|
||||||
See --output for a description of available
|
See the "OUTPUT TEMPLATE" for a description
|
||||||
keys.
|
of available keys.
|
||||||
-J, --dump-single-json Simulate, quiet but print JSON information
|
-J, --dump-single-json Simulate, quiet but print JSON information
|
||||||
for each command-line argument. If the URL
|
for each command-line argument. If the URL
|
||||||
refers to a playlist, dump the whole
|
refers to a playlist, dump the whole
|
||||||
@@ -400,12 +400,14 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo
|
|||||||
--add-metadata Write metadata to the video file
|
--add-metadata Write metadata to the video file
|
||||||
--metadata-from-title FORMAT Parse additional metadata like song title /
|
--metadata-from-title FORMAT Parse additional metadata like song title /
|
||||||
artist from the video title. The format
|
artist from the video title. The format
|
||||||
syntax is the same as --output, the parsed
|
syntax is the same as --output. Regular
|
||||||
parameters replace existing values.
|
expression with named capture groups may
|
||||||
Additional templates: %(album)s,
|
also be used. The parsed parameters replace
|
||||||
%(artist)s. Example: --metadata-from-title
|
existing values. Example: --metadata-from-
|
||||||
"%(artist)s - %(title)s" matches a title
|
title "%(artist)s - %(title)s" matches a
|
||||||
like "Coldplay - Paradise"
|
title like "Coldplay - Paradise". Example
|
||||||
|
(regex): --metadata-from-title
|
||||||
|
"(?P<artist>.+?) - (?P<title>.+)"
|
||||||
--xattrs Write metadata to the video file's xattrs
|
--xattrs Write metadata to the video file's xattrs
|
||||||
(using dublin core and xdg standards)
|
(using dublin core and xdg standards)
|
||||||
--fixup POLICY Automatically correct known faults of the
|
--fixup POLICY Automatically correct known faults of the
|
||||||
@@ -472,7 +474,10 @@ machine twitch login my_twitch_account_name password my_twitch_password
|
|||||||
```
|
```
|
||||||
To activate authentication with the `.netrc` file you should pass `--netrc` to youtube-dl or place it in the [configuration file](#configuration).
|
To activate authentication with the `.netrc` file you should pass `--netrc` to youtube-dl or place it in the [configuration file](#configuration).
|
||||||
|
|
||||||
On Windows you may also need to setup the `%HOME%` environment variable manually.
|
On Windows you may also need to setup the `%HOME%` environment variable manually. For example:
|
||||||
|
```
|
||||||
|
set HOME=%USERPROFILE%
|
||||||
|
```
|
||||||
|
|
||||||
# OUTPUT TEMPLATE
|
# OUTPUT TEMPLATE
|
||||||
|
|
||||||
@@ -530,13 +535,14 @@ The basic usage is not to set any template arguments when downloading a single f
|
|||||||
- `playlist_id` (string): Playlist identifier
|
- `playlist_id` (string): Playlist identifier
|
||||||
- `playlist_title` (string): Playlist title
|
- `playlist_title` (string): Playlist title
|
||||||
|
|
||||||
|
|
||||||
Available for the video that belongs to some logical chapter or section:
|
Available for the video that belongs to some logical chapter or section:
|
||||||
|
|
||||||
- `chapter` (string): Name or title of the chapter the video belongs to
|
- `chapter` (string): Name or title of the chapter the video belongs to
|
||||||
- `chapter_number` (numeric): Number of the chapter the video belongs to
|
- `chapter_number` (numeric): Number of the chapter the video belongs to
|
||||||
- `chapter_id` (string): Id of the chapter the video belongs to
|
- `chapter_id` (string): Id of the chapter the video belongs to
|
||||||
|
|
||||||
Available for the video that is an episode of some series or programme:
|
Available for the video that is an episode of some series or programme:
|
||||||
|
|
||||||
- `series` (string): Title of the series or programme the video episode belongs to
|
- `series` (string): Title of the series or programme the video episode belongs to
|
||||||
- `season` (string): Title of the season the video episode belongs to
|
- `season` (string): Title of the season the video episode belongs to
|
||||||
- `season_number` (numeric): Number of the season the video episode belongs to
|
- `season_number` (numeric): Number of the season the video episode belongs to
|
||||||
@@ -546,6 +552,7 @@ Available for the video that is an episode of some series or programme:
|
|||||||
- `episode_id` (string): Id of the video episode
|
- `episode_id` (string): Id of the video episode
|
||||||
|
|
||||||
Available for the media that is a track or a part of a music album:
|
Available for the media that is a track or a part of a music album:
|
||||||
|
|
||||||
- `track` (string): Title of the track
|
- `track` (string): Title of the track
|
||||||
- `track_number` (numeric): Number of the track within an album or a disc
|
- `track_number` (numeric): Number of the track within an album or a disc
|
||||||
- `track_id` (string): Id of the track
|
- `track_id` (string): Id of the track
|
||||||
@@ -647,7 +654,7 @@ Also filtering work for comparisons `=` (equals), `!=` (not equals), `^=` (begin
|
|||||||
- `acodec`: Name of the audio codec in use
|
- `acodec`: Name of the audio codec in use
|
||||||
- `vcodec`: Name of the video codec in use
|
- `vcodec`: Name of the video codec in use
|
||||||
- `container`: Name of the container format
|
- `container`: Name of the container format
|
||||||
- `protocol`: The protocol that will be used for the actual download, lower-case (`http`, `https`, `rtsp`, `rtmp`, `rtmpe`, `mms`, `f4m`, `ism`, `m3u8`, or `m3u8_native`)
|
- `protocol`: The protocol that will be used for the actual download, lower-case (`http`, `https`, `rtsp`, `rtmp`, `rtmpe`, `mms`, `f4m`, `ism`, `http_dash_segments`, `m3u8`, or `m3u8_native`)
|
||||||
- `format_id`: A short description of the format
|
- `format_id`: A short description of the format
|
||||||
|
|
||||||
Note that none of the aforementioned meta fields are guaranteed to be present since this solely depends on the metadata obtained by particular extractor, i.e. the metadata offered by the video hoster.
|
Note that none of the aforementioned meta fields are guaranteed to be present since this solely depends on the metadata obtained by particular extractor, i.e. the metadata offered by the video hoster.
|
||||||
|
@@ -8,7 +8,7 @@ import re
|
|||||||
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
README_FILE = os.path.join(ROOT_DIR, 'README.md')
|
README_FILE = os.path.join(ROOT_DIR, 'README.md')
|
||||||
|
|
||||||
PREFIX = '''%YOUTUBE-DL(1)
|
PREFIX = r'''%YOUTUBE-DL(1)
|
||||||
|
|
||||||
# NAME
|
# NAME
|
||||||
|
|
||||||
|
@@ -45,6 +45,7 @@
|
|||||||
- **anderetijden**: npo.nl and ntr.nl
|
- **anderetijden**: npo.nl and ntr.nl
|
||||||
- **AnimeOnDemand**
|
- **AnimeOnDemand**
|
||||||
- **anitube.se**
|
- **anitube.se**
|
||||||
|
- **Anvato**
|
||||||
- **AnySex**
|
- **AnySex**
|
||||||
- **Aparat**
|
- **Aparat**
|
||||||
- **AppleConnect**
|
- **AppleConnect**
|
||||||
@@ -66,6 +67,8 @@
|
|||||||
- **arte.tv:info**
|
- **arte.tv:info**
|
||||||
- **arte.tv:magazine**
|
- **arte.tv:magazine**
|
||||||
- **arte.tv:playlist**
|
- **arte.tv:playlist**
|
||||||
|
- **AsianCrush**
|
||||||
|
- **AsianCrushPlaylist**
|
||||||
- **AtresPlayer**
|
- **AtresPlayer**
|
||||||
- **ATTTechChannel**
|
- **ATTTechChannel**
|
||||||
- **ATVAt**
|
- **ATVAt**
|
||||||
@@ -86,13 +89,13 @@
|
|||||||
- **bambuser:channel**
|
- **bambuser:channel**
|
||||||
- **Bandcamp**
|
- **Bandcamp**
|
||||||
- **Bandcamp:album**
|
- **Bandcamp:album**
|
||||||
|
- **Bandcamp:weekly**
|
||||||
- **bangumi.bilibili.com**: BiliBili番剧
|
- **bangumi.bilibili.com**: BiliBili番剧
|
||||||
- **bbc**: BBC
|
- **bbc**: BBC
|
||||||
- **bbc.co.uk**: BBC iPlayer
|
- **bbc.co.uk**: BBC iPlayer
|
||||||
- **bbc.co.uk:article**: BBC articles
|
- **bbc.co.uk:article**: BBC articles
|
||||||
- **bbc.co.uk:iplayer:playlist**
|
- **bbc.co.uk:iplayer:playlist**
|
||||||
- **bbc.co.uk:playlist**
|
- **bbc.co.uk:playlist**
|
||||||
- **Beam:live**
|
|
||||||
- **Beatport**
|
- **Beatport**
|
||||||
- **Beeg**
|
- **Beeg**
|
||||||
- **BehindKink**
|
- **BehindKink**
|
||||||
@@ -215,6 +218,7 @@
|
|||||||
- **DiscoveryVR**
|
- **DiscoveryVR**
|
||||||
- **Disney**
|
- **Disney**
|
||||||
- **Dotsub**
|
- **Dotsub**
|
||||||
|
- **DouyuShow**
|
||||||
- **DouyuTV**: 斗鱼
|
- **DouyuTV**: 斗鱼
|
||||||
- **DPlay**
|
- **DPlay**
|
||||||
- **DPlayIt**
|
- **DPlayIt**
|
||||||
@@ -280,7 +284,8 @@
|
|||||||
- **france2.fr:generation-quoi**
|
- **france2.fr:generation-quoi**
|
||||||
- **FranceCulture**
|
- **FranceCulture**
|
||||||
- **FranceInter**
|
- **FranceInter**
|
||||||
- **francetv**: France 2, 3, 4, 5 and Ô
|
- **FranceTV**
|
||||||
|
- **FranceTVEmbed**
|
||||||
- **francetvinfo.fr**
|
- **francetvinfo.fr**
|
||||||
- **Freesound**
|
- **Freesound**
|
||||||
- **freespeech.org**
|
- **freespeech.org**
|
||||||
@@ -308,7 +313,6 @@
|
|||||||
- **Go**
|
- **Go**
|
||||||
- **Go90**
|
- **Go90**
|
||||||
- **GodTube**
|
- **GodTube**
|
||||||
- **GodTV**
|
|
||||||
- **Golem**
|
- **Golem**
|
||||||
- **GoogleDrive**
|
- **GoogleDrive**
|
||||||
- **Goshgay**
|
- **Goshgay**
|
||||||
@@ -431,6 +435,7 @@
|
|||||||
- **MDR**: MDR.DE and KiKA
|
- **MDR**: MDR.DE and KiKA
|
||||||
- **media.ccc.de**
|
- **media.ccc.de**
|
||||||
- **Medialaan**
|
- **Medialaan**
|
||||||
|
- **Mediaset**
|
||||||
- **Medici**
|
- **Medici**
|
||||||
- **Meipai**: 美拍
|
- **Meipai**: 美拍
|
||||||
- **MelonVOD**
|
- **MelonVOD**
|
||||||
@@ -449,6 +454,8 @@
|
|||||||
- **mixcloud:playlist**
|
- **mixcloud:playlist**
|
||||||
- **mixcloud:stream**
|
- **mixcloud:stream**
|
||||||
- **mixcloud:user**
|
- **mixcloud:user**
|
||||||
|
- **Mixer:live**
|
||||||
|
- **Mixer:vod**
|
||||||
- **MLB**
|
- **MLB**
|
||||||
- **Mnet**
|
- **Mnet**
|
||||||
- **MoeVideo**: LetitBit video services: moevideo.net, playreplay.net and videochart.net
|
- **MoeVideo**: LetitBit video services: moevideo.net, playreplay.net and videochart.net
|
||||||
@@ -507,6 +514,7 @@
|
|||||||
- **netease:song**: 网易云音乐
|
- **netease:song**: 网易云音乐
|
||||||
- **Netzkino**
|
- **Netzkino**
|
||||||
- **Newgrounds**
|
- **Newgrounds**
|
||||||
|
- **NewgroundsPlaylist**
|
||||||
- **Newstube**
|
- **Newstube**
|
||||||
- **NextMedia**: 蘋果日報
|
- **NextMedia**: 蘋果日報
|
||||||
- **NextMediaActionNews**: 蘋果日報 - 動新聞
|
- **NextMediaActionNews**: 蘋果日報 - 動新聞
|
||||||
@@ -529,6 +537,8 @@
|
|||||||
- **NJPWWorld**: 新日本プロレスワールド
|
- **NJPWWorld**: 新日本プロレスワールド
|
||||||
- **NobelPrize**
|
- **NobelPrize**
|
||||||
- **Noco**
|
- **Noco**
|
||||||
|
- **NonkTube**
|
||||||
|
- **Noovo**
|
||||||
- **Normalboots**
|
- **Normalboots**
|
||||||
- **NosVideo**
|
- **NosVideo**
|
||||||
- **Nova**: TN.cz, Prásk.tv, Nova.cz, Novaplus.cz, FANDA.tv, Krásná.cz and Doma.cz
|
- **Nova**: TN.cz, Prásk.tv, Nova.cz, Novaplus.cz, FANDA.tv, Krásná.cz and Doma.cz
|
||||||
@@ -600,7 +610,6 @@
|
|||||||
- **pluralsight**
|
- **pluralsight**
|
||||||
- **pluralsight:course**
|
- **pluralsight:course**
|
||||||
- **plus.google**: Google Plus
|
- **plus.google**: Google Plus
|
||||||
- **pluzz.francetv.fr**
|
|
||||||
- **podomatic**
|
- **podomatic**
|
||||||
- **Pokemon**
|
- **Pokemon**
|
||||||
- **PolskieRadio**
|
- **PolskieRadio**
|
||||||
@@ -679,6 +688,7 @@
|
|||||||
- **rutube:person**: Rutube person videos
|
- **rutube:person**: Rutube person videos
|
||||||
- **RUTV**: RUTV.RU
|
- **RUTV**: RUTV.RU
|
||||||
- **Ruutu**
|
- **Ruutu**
|
||||||
|
- **Ruv**
|
||||||
- **safari**: safaribooksonline.com online video
|
- **safari**: safaribooksonline.com online video
|
||||||
- **safari:api**
|
- **safari:api**
|
||||||
- **safari:course**: safaribooksonline.com online courses
|
- **safari:course**: safaribooksonline.com online courses
|
||||||
@@ -798,16 +808,13 @@
|
|||||||
- **ToonGoggles**
|
- **ToonGoggles**
|
||||||
- **Tosh**: Tosh.0
|
- **Tosh**: Tosh.0
|
||||||
- **tou.tv**
|
- **tou.tv**
|
||||||
- **Toypics**: Toypics user profile
|
- **Toypics**: Toypics video
|
||||||
- **ToypicsUser**: Toypics user profile
|
- **ToypicsUser**: Toypics user profile
|
||||||
- **TrailerAddict** (Currently broken)
|
- **TrailerAddict** (Currently broken)
|
||||||
- **Trilulilu**
|
- **Trilulilu**
|
||||||
- **TruTV**
|
- **TruTV**
|
||||||
- **Tube8**
|
- **Tube8**
|
||||||
- **TubiTv**
|
- **TubiTv**
|
||||||
- **tudou**
|
|
||||||
- **tudou:album**
|
|
||||||
- **tudou:playlist**
|
|
||||||
- **Tumblr**
|
- **Tumblr**
|
||||||
- **tunein:clip**
|
- **tunein:clip**
|
||||||
- **tunein:program**
|
- **tunein:program**
|
||||||
@@ -858,6 +865,8 @@
|
|||||||
- **uol.com.br**
|
- **uol.com.br**
|
||||||
- **uplynk**
|
- **uplynk**
|
||||||
- **uplynk:preplay**
|
- **uplynk:preplay**
|
||||||
|
- **Upskill**
|
||||||
|
- **UpskillCourse**
|
||||||
- **Urort**: NRK P3 Urørt
|
- **Urort**: NRK P3 Urørt
|
||||||
- **URPlay**
|
- **URPlay**
|
||||||
- **USANetwork**
|
- **USANetwork**
|
||||||
@@ -877,9 +886,10 @@
|
|||||||
- **VGTV**: VGTV, BTTV, FTV, Aftenposten and Aftonbladet
|
- **VGTV**: VGTV, BTTV, FTV, Aftenposten and Aftonbladet
|
||||||
- **vh1.com**
|
- **vh1.com**
|
||||||
- **Viafree**
|
- **Viafree**
|
||||||
- **Vice**
|
- **vice**
|
||||||
|
- **vice:article**
|
||||||
|
- **vice:show**
|
||||||
- **Viceland**
|
- **Viceland**
|
||||||
- **ViceShow**
|
|
||||||
- **Vidbit**
|
- **Vidbit**
|
||||||
- **Viddler**
|
- **Viddler**
|
||||||
- **Videa**
|
- **Videa**
|
||||||
@@ -968,7 +978,7 @@
|
|||||||
- **WSJArticle**
|
- **WSJArticle**
|
||||||
- **XBef**
|
- **XBef**
|
||||||
- **XboxClips**
|
- **XboxClips**
|
||||||
- **XFileShare**: XFileShare based sites: DaClips, FileHoot, GorillaVid, MovPod, PowerWatch, Rapidvideo.ws, TheVideoBee, Vidto, Streamin.To, XVIDSTAGE, Vid ABC, VidBom, vidlo
|
- **XFileShare**: XFileShare based sites: DaClips, FileHoot, GorillaVid, MovPod, PowerWatch, Rapidvideo.ws, TheVideoBee, Vidto, Streamin.To, XVIDSTAGE, Vid ABC, VidBom, vidlo, RapidVideo.TV, FastVideo.me
|
||||||
- **XHamster**
|
- **XHamster**
|
||||||
- **XHamsterEmbed**
|
- **XHamsterEmbed**
|
||||||
- **xiami:album**: 虾米音乐 - 专辑
|
- **xiami:album**: 虾米音乐 - 专辑
|
||||||
@@ -1013,6 +1023,7 @@
|
|||||||
- **youtube:user**: YouTube.com user videos (URL or "ytuser" keyword)
|
- **youtube:user**: YouTube.com user videos (URL or "ytuser" keyword)
|
||||||
- **youtube:watchlater**: Youtube watch later list, ":ytwatchlater" for short (requires authentication)
|
- **youtube:watchlater**: Youtube watch later list, ":ytwatchlater" for short (requires authentication)
|
||||||
- **Zapiks**
|
- **Zapiks**
|
||||||
|
- **Zaq1**
|
||||||
- **ZDF**
|
- **ZDF**
|
||||||
- **ZDFChannel**
|
- **ZDFChannel**
|
||||||
- **zingmp3**: mp3.zing.vn
|
- **zingmp3**: mp3.zing.vn
|
||||||
|
@@ -184,16 +184,8 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'pluzz_francetv_11507',
|
'pluzz_francetv_11507',
|
||||||
'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/master.m3u8?caption=2017%2F16%2F156589847-1492488987.m3u8%3Afra%3AFrancais&audiotrack=0%3Afra%3AFrancais',
|
'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/master.m3u8?caption=2017%2F16%2F156589847-1492488987.m3u8%3Afra%3AFrancais&audiotrack=0%3Afra%3AFrancais',
|
||||||
[{
|
[{
|
||||||
'url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/master.m3u8?caption=2017%2F16%2F156589847-1492488987.m3u8%3Afra%3AFrancais&audiotrack=0%3Afra%3AFrancais',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'format_id': 'meta',
|
|
||||||
'format_note': 'Quality selection URL',
|
|
||||||
'protocol': 'm3u8',
|
|
||||||
'preference': -100,
|
|
||||||
'resolution': 'multiple'
|
|
||||||
}, {
|
|
||||||
'url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_0_av.m3u8?null=0',
|
'url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_0_av.m3u8?null=0',
|
||||||
'manifest_url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_0_av.m3u8?null=0',
|
'manifest_url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/master.m3u8?caption=2017%2F16%2F156589847-1492488987.m3u8%3Afra%3AFrancais&audiotrack=0%3Afra%3AFrancais',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '180',
|
'format_id': '180',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -204,7 +196,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 144,
|
'height': 144,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_1_av.m3u8?null=0',
|
'url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_1_av.m3u8?null=0',
|
||||||
'manifest_url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_1_av.m3u8?null=0',
|
'manifest_url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/master.m3u8?caption=2017%2F16%2F156589847-1492488987.m3u8%3Afra%3AFrancais&audiotrack=0%3Afra%3AFrancais',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '303',
|
'format_id': '303',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -215,7 +207,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 180,
|
'height': 180,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_2_av.m3u8?null=0',
|
'url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_2_av.m3u8?null=0',
|
||||||
'manifest_url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_2_av.m3u8?null=0',
|
'manifest_url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/master.m3u8?caption=2017%2F16%2F156589847-1492488987.m3u8%3Afra%3AFrancais&audiotrack=0%3Afra%3AFrancais',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '575',
|
'format_id': '575',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -226,7 +218,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 288,
|
'height': 288,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_3_av.m3u8?null=0',
|
'url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_3_av.m3u8?null=0',
|
||||||
'manifest_url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_3_av.m3u8?null=0',
|
'manifest_url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/master.m3u8?caption=2017%2F16%2F156589847-1492488987.m3u8%3Afra%3AFrancais&audiotrack=0%3Afra%3AFrancais',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '831',
|
'format_id': '831',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -237,7 +229,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 396,
|
'height': 396,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_4_av.m3u8?null=0',
|
'url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_4_av.m3u8?null=0',
|
||||||
'manifest_url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/index_4_av.m3u8?null=0',
|
'manifest_url': 'http://replayftv-vh.akamaihd.net/i/streaming-adaptatif_france-dom-tom/2017/S16/J2/156589847-58f59130c1f52-,standard1,standard2,standard3,standard4,standard5,.mp4.csmil/master.m3u8?caption=2017%2F16%2F156589847-1492488987.m3u8%3Afra%3AFrancais&audiotrack=0%3Afra%3AFrancais',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
'format_id': '1467',
|
'format_id': '1467',
|
||||||
@@ -254,28 +246,22 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'teamcoco_11995',
|
'teamcoco_11995',
|
||||||
'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/main.m3u8',
|
'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/main.m3u8',
|
||||||
[{
|
[{
|
||||||
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/main.m3u8',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'format_id': 'meta',
|
|
||||||
'format_note': 'Quality selection URL',
|
|
||||||
'protocol': 'm3u8',
|
|
||||||
'preference': -100,
|
|
||||||
'resolution': 'multiple',
|
|
||||||
}, {
|
|
||||||
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-audio-160k_v4.m3u8',
|
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-audio-160k_v4.m3u8',
|
||||||
|
'manifest_url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/main.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': 'audio-0-Default',
|
'format_id': 'audio-0-Default',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
'vcodec': 'none',
|
'vcodec': 'none',
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-audio-64k_v4.m3u8',
|
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-audio-64k_v4.m3u8',
|
||||||
|
'manifest_url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/main.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': 'audio-1-Default',
|
'format_id': 'audio-1-Default',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
'vcodec': 'none',
|
'vcodec': 'none',
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-audio-64k_v4.m3u8',
|
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-audio-64k_v4.m3u8',
|
||||||
'manifest_url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-audio-64k_v4.m3u8',
|
'manifest_url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/main.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '71',
|
'format_id': '71',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -284,7 +270,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'tbr': 71,
|
'tbr': 71,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-400k_v4.m3u8',
|
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-400k_v4.m3u8',
|
||||||
'manifest_url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-400k_v4.m3u8',
|
'manifest_url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/main.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '413',
|
'format_id': '413',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -295,7 +281,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 224,
|
'height': 224,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-400k_v4.m3u8',
|
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-400k_v4.m3u8',
|
||||||
'manifest_url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-400k_v4.m3u8',
|
'manifest_url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/main.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '522',
|
'format_id': '522',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -306,7 +292,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 224,
|
'height': 224,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-1m_v4.m3u8',
|
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-1m_v4.m3u8',
|
||||||
'manifest_url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-1m_v4.m3u8',
|
'manifest_url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/main.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '1205',
|
'format_id': '1205',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -317,7 +303,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 360,
|
'height': 360,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-2m_v4.m3u8',
|
'url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-2m_v4.m3u8',
|
||||||
'manifest_url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/hls/CONAN_020217_Highlight_show-2m_v4.m3u8',
|
'manifest_url': 'http://ak.storage-w.teamcococdn.com/cdn/2017-02/98599/ed8f/main.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '2374',
|
'format_id': '2374',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -334,15 +320,8 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'toggle_mobile_12211',
|
'toggle_mobile_12211',
|
||||||
'http://cdnapi.kaltura.com/p/2082311/sp/208231100/playManifest/protocol/http/entryId/0_89q6e8ku/format/applehttp/tags/mobile_sd/f/a.m3u8',
|
'http://cdnapi.kaltura.com/p/2082311/sp/208231100/playManifest/protocol/http/entryId/0_89q6e8ku/format/applehttp/tags/mobile_sd/f/a.m3u8',
|
||||||
[{
|
[{
|
||||||
'url': 'http://cdnapi.kaltura.com/p/2082311/sp/208231100/playManifest/protocol/http/entryId/0_89q6e8ku/format/applehttp/tags/mobile_sd/f/a.m3u8',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'format_id': 'meta',
|
|
||||||
'format_note': 'Quality selection URL',
|
|
||||||
'protocol': 'm3u8',
|
|
||||||
'preference': -100,
|
|
||||||
'resolution': 'multiple'
|
|
||||||
}, {
|
|
||||||
'url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/2/pv/1/flavorId/0_sa2ntrdg/name/a.mp4/index.m3u8',
|
'url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/2/pv/1/flavorId/0_sa2ntrdg/name/a.mp4/index.m3u8',
|
||||||
|
'manifest_url': 'http://cdnapi.kaltura.com/p/2082311/sp/208231100/playManifest/protocol/http/entryId/0_89q6e8ku/format/applehttp/tags/mobile_sd/f/a.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': 'audio-English',
|
'format_id': 'audio-English',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -350,6 +329,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'vcodec': 'none',
|
'vcodec': 'none',
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/2/pv/1/flavorId/0_r7y0nitg/name/a.mp4/index.m3u8',
|
'url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/2/pv/1/flavorId/0_r7y0nitg/name/a.mp4/index.m3u8',
|
||||||
|
'manifest_url': 'http://cdnapi.kaltura.com/p/2082311/sp/208231100/playManifest/protocol/http/entryId/0_89q6e8ku/format/applehttp/tags/mobile_sd/f/a.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': 'audio-Undefined',
|
'format_id': 'audio-Undefined',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -357,7 +337,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'vcodec': 'none',
|
'vcodec': 'none',
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/2/pv/1/flavorId/0_qlk9hlzr/name/a.mp4/index.m3u8',
|
'url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/2/pv/1/flavorId/0_qlk9hlzr/name/a.mp4/index.m3u8',
|
||||||
'manifest_url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/2/pv/1/flavorId/0_qlk9hlzr/name/a.mp4/index.m3u8',
|
'manifest_url': 'http://cdnapi.kaltura.com/p/2082311/sp/208231100/playManifest/protocol/http/entryId/0_89q6e8ku/format/applehttp/tags/mobile_sd/f/a.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '155',
|
'format_id': '155',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -366,7 +346,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 180,
|
'height': 180,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/2/pv/1/flavorId/0_oefackmi/name/a.mp4/index.m3u8',
|
'url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/2/pv/1/flavorId/0_oefackmi/name/a.mp4/index.m3u8',
|
||||||
'manifest_url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/2/pv/1/flavorId/0_oefackmi/name/a.mp4/index.m3u8',
|
'manifest_url': 'http://cdnapi.kaltura.com/p/2082311/sp/208231100/playManifest/protocol/http/entryId/0_89q6e8ku/format/applehttp/tags/mobile_sd/f/a.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '502',
|
'format_id': '502',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -375,7 +355,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 270,
|
'height': 270,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/12/pv/1/flavorId/0_vyg9pj7k/name/a.mp4/index.m3u8',
|
'url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/12/pv/1/flavorId/0_vyg9pj7k/name/a.mp4/index.m3u8',
|
||||||
'manifest_url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/12/pv/1/flavorId/0_vyg9pj7k/name/a.mp4/index.m3u8',
|
'manifest_url': 'http://cdnapi.kaltura.com/p/2082311/sp/208231100/playManifest/protocol/http/entryId/0_89q6e8ku/format/applehttp/tags/mobile_sd/f/a.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '827',
|
'format_id': '827',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -384,7 +364,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 360,
|
'height': 360,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/12/pv/1/flavorId/0_50n4psvx/name/a.mp4/index.m3u8',
|
'url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/12/pv/1/flavorId/0_50n4psvx/name/a.mp4/index.m3u8',
|
||||||
'manifest_url': 'http://k.toggle.sg/fhls/p/2082311/sp/208231100/serveFlavor/entryId/0_89q6e8ku/v/12/pv/1/flavorId/0_50n4psvx/name/a.mp4/index.m3u8',
|
'manifest_url': 'http://cdnapi.kaltura.com/p/2082311/sp/208231100/playManifest/protocol/http/entryId/0_89q6e8ku/format/applehttp/tags/mobile_sd/f/a.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '1396',
|
'format_id': '1396',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -398,16 +378,8 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'twitch_vod',
|
'twitch_vod',
|
||||||
'https://usher.ttvnw.net/vod/6528877?allow_source=true&allow_audio_only=true&allow_spectre=true&player=twitchweb&nauth=%7B%22user_id%22%3Anull%2C%22vod_id%22%3A6528877%2C%22expires%22%3A1492887874%2C%22chansub%22%3A%7B%22restricted_bitrates%22%3A%5B%5D%7D%2C%22privileged%22%3Afalse%2C%22https_required%22%3Afalse%7D&nauthsig=3e29296a6824a0f48f9e731383f77a614fc79bee',
|
'https://usher.ttvnw.net/vod/6528877?allow_source=true&allow_audio_only=true&allow_spectre=true&player=twitchweb&nauth=%7B%22user_id%22%3Anull%2C%22vod_id%22%3A6528877%2C%22expires%22%3A1492887874%2C%22chansub%22%3A%7B%22restricted_bitrates%22%3A%5B%5D%7D%2C%22privileged%22%3Afalse%2C%22https_required%22%3Afalse%7D&nauthsig=3e29296a6824a0f48f9e731383f77a614fc79bee',
|
||||||
[{
|
[{
|
||||||
'url': 'https://usher.ttvnw.net/vod/6528877?allow_source=true&allow_audio_only=true&allow_spectre=true&player=twitchweb&nauth=%7B%22user_id%22%3Anull%2C%22vod_id%22%3A6528877%2C%22expires%22%3A1492887874%2C%22chansub%22%3A%7B%22restricted_bitrates%22%3A%5B%5D%7D%2C%22privileged%22%3Afalse%2C%22https_required%22%3Afalse%7D&nauthsig=3e29296a6824a0f48f9e731383f77a614fc79bee',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'format_id': 'meta',
|
|
||||||
'format_note': 'Quality selection URL',
|
|
||||||
'protocol': 'm3u8',
|
|
||||||
'preference': -100,
|
|
||||||
'resolution': 'multiple'
|
|
||||||
}, {
|
|
||||||
'url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/audio_only/index-muted-HM49I092CC.m3u8',
|
'url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/audio_only/index-muted-HM49I092CC.m3u8',
|
||||||
'manifest_url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/audio_only/index-muted-HM49I092CC.m3u8',
|
'manifest_url': 'https://usher.ttvnw.net/vod/6528877?allow_source=true&allow_audio_only=true&allow_spectre=true&player=twitchweb&nauth=%7B%22user_id%22%3Anull%2C%22vod_id%22%3A6528877%2C%22expires%22%3A1492887874%2C%22chansub%22%3A%7B%22restricted_bitrates%22%3A%5B%5D%7D%2C%22privileged%22%3Afalse%2C%22https_required%22%3Afalse%7D&nauthsig=3e29296a6824a0f48f9e731383f77a614fc79bee',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': 'Audio Only',
|
'format_id': 'Audio Only',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -416,7 +388,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'tbr': 182.725,
|
'tbr': 182.725,
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/mobile/index-muted-HM49I092CC.m3u8',
|
'url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/mobile/index-muted-HM49I092CC.m3u8',
|
||||||
'manifest_url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/mobile/index-muted-HM49I092CC.m3u8',
|
'manifest_url': 'https://usher.ttvnw.net/vod/6528877?allow_source=true&allow_audio_only=true&allow_spectre=true&player=twitchweb&nauth=%7B%22user_id%22%3Anull%2C%22vod_id%22%3A6528877%2C%22expires%22%3A1492887874%2C%22chansub%22%3A%7B%22restricted_bitrates%22%3A%5B%5D%7D%2C%22privileged%22%3Afalse%2C%22https_required%22%3Afalse%7D&nauthsig=3e29296a6824a0f48f9e731383f77a614fc79bee',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': 'Mobile',
|
'format_id': 'Mobile',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -427,7 +399,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 226,
|
'height': 226,
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/low/index-muted-HM49I092CC.m3u8',
|
'url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/low/index-muted-HM49I092CC.m3u8',
|
||||||
'manifest_url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/low/index-muted-HM49I092CC.m3u8',
|
'manifest_url': 'https://usher.ttvnw.net/vod/6528877?allow_source=true&allow_audio_only=true&allow_spectre=true&player=twitchweb&nauth=%7B%22user_id%22%3Anull%2C%22vod_id%22%3A6528877%2C%22expires%22%3A1492887874%2C%22chansub%22%3A%7B%22restricted_bitrates%22%3A%5B%5D%7D%2C%22privileged%22%3Afalse%2C%22https_required%22%3Afalse%7D&nauthsig=3e29296a6824a0f48f9e731383f77a614fc79bee',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': 'Low',
|
'format_id': 'Low',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -438,7 +410,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 360,
|
'height': 360,
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/medium/index-muted-HM49I092CC.m3u8',
|
'url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/medium/index-muted-HM49I092CC.m3u8',
|
||||||
'manifest_url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/medium/index-muted-HM49I092CC.m3u8',
|
'manifest_url': 'https://usher.ttvnw.net/vod/6528877?allow_source=true&allow_audio_only=true&allow_spectre=true&player=twitchweb&nauth=%7B%22user_id%22%3Anull%2C%22vod_id%22%3A6528877%2C%22expires%22%3A1492887874%2C%22chansub%22%3A%7B%22restricted_bitrates%22%3A%5B%5D%7D%2C%22privileged%22%3Afalse%2C%22https_required%22%3Afalse%7D&nauthsig=3e29296a6824a0f48f9e731383f77a614fc79bee',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': 'Medium',
|
'format_id': 'Medium',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -449,7 +421,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 480,
|
'height': 480,
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/high/index-muted-HM49I092CC.m3u8',
|
'url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/high/index-muted-HM49I092CC.m3u8',
|
||||||
'manifest_url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/high/index-muted-HM49I092CC.m3u8',
|
'manifest_url': 'https://usher.ttvnw.net/vod/6528877?allow_source=true&allow_audio_only=true&allow_spectre=true&player=twitchweb&nauth=%7B%22user_id%22%3Anull%2C%22vod_id%22%3A6528877%2C%22expires%22%3A1492887874%2C%22chansub%22%3A%7B%22restricted_bitrates%22%3A%5B%5D%7D%2C%22privileged%22%3Afalse%2C%22https_required%22%3Afalse%7D&nauthsig=3e29296a6824a0f48f9e731383f77a614fc79bee',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': 'High',
|
'format_id': 'High',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -460,7 +432,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 720,
|
'height': 720,
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/chunked/index-muted-HM49I092CC.m3u8',
|
'url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/chunked/index-muted-HM49I092CC.m3u8',
|
||||||
'manifest_url': 'https://vod.edgecast.hls.ttvnw.net/e5da31ab49_riotgames_15001215120_261543898/chunked/index-muted-HM49I092CC.m3u8',
|
'manifest_url': 'https://usher.ttvnw.net/vod/6528877?allow_source=true&allow_audio_only=true&allow_spectre=true&player=twitchweb&nauth=%7B%22user_id%22%3Anull%2C%22vod_id%22%3A6528877%2C%22expires%22%3A1492887874%2C%22chansub%22%3A%7B%22restricted_bitrates%22%3A%5B%5D%7D%2C%22privileged%22%3Afalse%2C%22https_required%22%3Afalse%7D&nauthsig=3e29296a6824a0f48f9e731383f77a614fc79bee',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': 'Source',
|
'format_id': 'Source',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -478,16 +450,8 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'vidio',
|
'vidio',
|
||||||
'https://www.vidio.com/videos/165683/playlist.m3u8',
|
'https://www.vidio.com/videos/165683/playlist.m3u8',
|
||||||
[{
|
[{
|
||||||
'url': 'https://www.vidio.com/videos/165683/playlist.m3u8',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'format_id': 'meta',
|
|
||||||
'format_note': 'Quality selection URL',
|
|
||||||
'protocol': 'm3u8',
|
|
||||||
'preference': -100,
|
|
||||||
'resolution': 'multiple'
|
|
||||||
}, {
|
|
||||||
'url': 'https://cdn1-a.production.vidio.static6.com/uploads/165683/dj_ambred-4383-b300.mp4.m3u8',
|
'url': 'https://cdn1-a.production.vidio.static6.com/uploads/165683/dj_ambred-4383-b300.mp4.m3u8',
|
||||||
'manifest_url': 'https://cdn1-a.production.vidio.static6.com/uploads/165683/dj_ambred-4383-b300.mp4.m3u8',
|
'manifest_url': 'https://www.vidio.com/videos/165683/playlist.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '270p 3G',
|
'format_id': '270p 3G',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -496,7 +460,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 270,
|
'height': 270,
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://cdn1-a.production.vidio.static6.com/uploads/165683/dj_ambred-4383-b600.mp4.m3u8',
|
'url': 'https://cdn1-a.production.vidio.static6.com/uploads/165683/dj_ambred-4383-b600.mp4.m3u8',
|
||||||
'manifest_url': 'https://cdn1-a.production.vidio.static6.com/uploads/165683/dj_ambred-4383-b600.mp4.m3u8',
|
'manifest_url': 'https://www.vidio.com/videos/165683/playlist.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '360p SD',
|
'format_id': '360p SD',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
@@ -505,7 +469,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
|
|||||||
'height': 360,
|
'height': 360,
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://cdn1-a.production.vidio.static6.com/uploads/165683/dj_ambred-4383-b1200.mp4.m3u8',
|
'url': 'https://cdn1-a.production.vidio.static6.com/uploads/165683/dj_ambred-4383-b1200.mp4.m3u8',
|
||||||
'manifest_url': 'https://cdn1-a.production.vidio.static6.com/uploads/165683/dj_ambred-4383-b1200.mp4.m3u8',
|
'manifest_url': 'https://www.vidio.com/videos/165683/playlist.m3u8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': '720p HD',
|
'format_id': '720p HD',
|
||||||
'protocol': 'm3u8',
|
'protocol': 'm3u8',
|
||||||
|
@@ -225,7 +225,7 @@ def generator(test_case, tname):
|
|||||||
format_bytes(got_fsize)))
|
format_bytes(got_fsize)))
|
||||||
if 'md5' in tc:
|
if 'md5' in tc:
|
||||||
md5_for_file = _file_md5(tc_filename)
|
md5_for_file = _file_md5(tc_filename)
|
||||||
self.assertEqual(md5_for_file, tc['md5'])
|
self.assertEqual(tc['md5'], md5_for_file)
|
||||||
# Finally, check test cases' data again but this time against
|
# Finally, check test cases' data again but this time against
|
||||||
# extracted data from info JSON file written during processing
|
# extracted data from info JSON file written during processing
|
||||||
info_json_fn = os.path.splitext(tc_filename)[0] + '.info.json'
|
info_json_fn = os.path.splitext(tc_filename)[0] + '.info.json'
|
||||||
|
@@ -44,6 +44,7 @@ from youtube_dl.utils import (
|
|||||||
limit_length,
|
limit_length,
|
||||||
mimetype2ext,
|
mimetype2ext,
|
||||||
month_by_name,
|
month_by_name,
|
||||||
|
multipart_encode,
|
||||||
ohdave_rsa_encrypt,
|
ohdave_rsa_encrypt,
|
||||||
OnDemandPagedList,
|
OnDemandPagedList,
|
||||||
orderedSet,
|
orderedSet,
|
||||||
@@ -338,6 +339,8 @@ class TestUtil(unittest.TestCase):
|
|||||||
self.assertEqual(unified_timestamp('UNKNOWN DATE FORMAT'), None)
|
self.assertEqual(unified_timestamp('UNKNOWN DATE FORMAT'), None)
|
||||||
self.assertEqual(unified_timestamp('May 16, 2016 11:15 PM'), 1463440500)
|
self.assertEqual(unified_timestamp('May 16, 2016 11:15 PM'), 1463440500)
|
||||||
self.assertEqual(unified_timestamp('Feb 7, 2016 at 6:35 pm'), 1454870100)
|
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)
|
||||||
|
|
||||||
def test_determine_ext(self):
|
def test_determine_ext(self):
|
||||||
self.assertEqual(determine_ext('http://example.com/foo/bar.mp4/?download'), 'mp4')
|
self.assertEqual(determine_ext('http://example.com/foo/bar.mp4/?download'), 'mp4')
|
||||||
@@ -619,6 +622,16 @@ class TestUtil(unittest.TestCase):
|
|||||||
'http://example.com/path', {'test': '第二行тест'})),
|
'http://example.com/path', {'test': '第二行тест'})),
|
||||||
query_dict('http://example.com/path?test=%E7%AC%AC%E4%BA%8C%E8%A1%8C%D1%82%D0%B5%D1%81%D1%82'))
|
query_dict('http://example.com/path?test=%E7%AC%AC%E4%BA%8C%E8%A1%8C%D1%82%D0%B5%D1%81%D1%82'))
|
||||||
|
|
||||||
|
def test_multipart_encode(self):
|
||||||
|
self.assertEqual(
|
||||||
|
multipart_encode({b'field': b'value'}, boundary='AAAAAA')[0],
|
||||||
|
b'--AAAAAA\r\nContent-Disposition: form-data; name="field"\r\n\r\nvalue\r\n--AAAAAA--\r\n')
|
||||||
|
self.assertEqual(
|
||||||
|
multipart_encode({'欄位'.encode('utf-8'): '值'.encode('utf-8')}, boundary='AAAAAA')[0],
|
||||||
|
b'--AAAAAA\r\nContent-Disposition: form-data; name="\xe6\xac\x84\xe4\xbd\x8d"\r\n\r\n\xe5\x80\xbc\r\n--AAAAAA--\r\n')
|
||||||
|
self.assertRaises(
|
||||||
|
ValueError, multipart_encode, {b'field': b'value'}, boundary='value')
|
||||||
|
|
||||||
def test_dict_get(self):
|
def test_dict_get(self):
|
||||||
FALSE_VALUES = {
|
FALSE_VALUES = {
|
||||||
'none': None,
|
'none': None,
|
||||||
@@ -666,6 +679,14 @@ class TestUtil(unittest.TestCase):
|
|||||||
d = json.loads(stripped)
|
d = json.loads(stripped)
|
||||||
self.assertEqual(d, {'status': 'success'})
|
self.assertEqual(d, {'status': 'success'})
|
||||||
|
|
||||||
|
stripped = strip_jsonp('window.cb && window.cb({"status": "success"});')
|
||||||
|
d = json.loads(stripped)
|
||||||
|
self.assertEqual(d, {'status': 'success'})
|
||||||
|
|
||||||
|
stripped = strip_jsonp('window.cb && cb({"status": "success"});')
|
||||||
|
d = json.loads(stripped)
|
||||||
|
self.assertEqual(d, {'status': 'success'})
|
||||||
|
|
||||||
def test_uppercase_escape(self):
|
def test_uppercase_escape(self):
|
||||||
self.assertEqual(uppercase_escape('aä'), 'aä')
|
self.assertEqual(uppercase_escape('aä'), 'aä')
|
||||||
self.assertEqual(uppercase_escape('\\U0001d550'), '𝕐')
|
self.assertEqual(uppercase_escape('\\U0001d550'), '𝕐')
|
||||||
@@ -895,10 +916,13 @@ class TestUtil(unittest.TestCase):
|
|||||||
supports_outside_bmp = False
|
supports_outside_bmp = False
|
||||||
if supports_outside_bmp:
|
if supports_outside_bmp:
|
||||||
self.assertEqual(extract_attributes('<e x="Smile 😀!">'), {'x': 'Smile \U0001f600!'})
|
self.assertEqual(extract_attributes('<e x="Smile 😀!">'), {'x': 'Smile \U0001f600!'})
|
||||||
|
# Malformed HTML should not break attributes extraction on older Python
|
||||||
|
self.assertEqual(extract_attributes('<mal"formed/>'), {})
|
||||||
|
|
||||||
def test_clean_html(self):
|
def test_clean_html(self):
|
||||||
self.assertEqual(clean_html('a:\nb'), 'a: b')
|
self.assertEqual(clean_html('a:\nb'), 'a: b')
|
||||||
self.assertEqual(clean_html('a:\n "b"'), 'a: "b"')
|
self.assertEqual(clean_html('a:\n "b"'), 'a: "b"')
|
||||||
|
self.assertEqual(clean_html('a<br>\xa0b'), 'a\nb')
|
||||||
|
|
||||||
def test_intlist_to_bytes(self):
|
def test_intlist_to_bytes(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
275
test/test_youtube_chapters.py
Normal file
275
test/test_youtube_chapters.py
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
# Allow direct execution
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from test.helper import expect_value
|
||||||
|
from youtube_dl.extractor import YoutubeIE
|
||||||
|
|
||||||
|
|
||||||
|
class TestYoutubeChapters(unittest.TestCase):
|
||||||
|
|
||||||
|
_TEST_CASES = [
|
||||||
|
(
|
||||||
|
# https://www.youtube.com/watch?v=A22oy8dFjqc
|
||||||
|
# pattern: 00:00 - <title>
|
||||||
|
'''This is the absolute ULTIMATE experience of Queen's set at LIVE AID, this is the best video mixed to the absolutely superior stereo radio broadcast. This vastly superior audio mix takes a huge dump on all of the official mixes. Best viewed in 1080p. ENJOY! ***MAKE SURE TO READ THE DESCRIPTION***<br /><a href="#" onclick="yt.www.watch.player.seekTo(00*60+36);return false;">00:36</a> - Bohemian Rhapsody<br /><a href="#" onclick="yt.www.watch.player.seekTo(02*60+42);return false;">02:42</a> - Radio Ga Ga<br /><a href="#" onclick="yt.www.watch.player.seekTo(06*60+53);return false;">06:53</a> - Ay Oh!<br /><a href="#" onclick="yt.www.watch.player.seekTo(07*60+34);return false;">07:34</a> - Hammer To Fall<br /><a href="#" onclick="yt.www.watch.player.seekTo(12*60+08);return false;">12:08</a> - Crazy Little Thing Called Love<br /><a href="#" onclick="yt.www.watch.player.seekTo(16*60+03);return false;">16:03</a> - We Will Rock You<br /><a href="#" onclick="yt.www.watch.player.seekTo(17*60+18);return false;">17:18</a> - We Are The Champions<br /><a href="#" onclick="yt.www.watch.player.seekTo(21*60+12);return false;">21:12</a> - Is This The World We Created...?<br /><br />Short song analysis:<br /><br />- "Bohemian Rhapsody": Although it's a short medley version, it's one of the best performances of the ballad section, with Freddie nailing the Bb4s with the correct studio phrasing (for the first time ever!).<br /><br />- "Radio Ga Ga": Although it's missing one chorus, this is one of - if not the best - the best versions ever, Freddie nails all the Bb4s and sounds very clean! Spike Edney's Roland Jupiter 8 also really shines through on this mix, compared to the DVD releases!<br /><br />- "Audience Improv": A great improv, Freddie sounds strong and confident. You gotta love when he sustains that A4 for 4 seconds!<br /><br />- "Hammer To Fall": Despite missing a verse and a chorus, it's a strong version (possibly the best ever). Freddie sings the song amazingly, and even ad-libs a C#5 and a C5! Also notice how heavy Brian's guitar sounds compared to the thin DVD mixes - it roars!<br /><br />- "Crazy Little Thing Called Love": A great version, the crowd loves the song, the jam is great as well! Only downside to this is the slight feedback issues.<br /><br />- "We Will Rock You": Although cut down to the 1st verse and chorus, Freddie sounds strong. He nails the A4, and the solo from Dr. May is brilliant!<br /><br />- "We Are the Champions": Perhaps the high-light of the performance - Freddie is very daring on this version, he sustains the pre-chorus Bb4s, nails the 1st C5, belts great A4s, but most importantly: He nails the chorus Bb4s, in all 3 choruses! This is the only time he has ever done so! It has to be said though, the last one sounds a bit rough, but that's a side effect of belting high notes for the past 18 minutes, with nodules AND laryngitis!<br /><br />- "Is This The World We Created... ?": Freddie and Brian perform a beautiful version of this, and it is one of the best versions ever. It's both sad and hilarious that a couple of BBC engineers are talking over the song, one of them being completely oblivious of the fact that he is interrupting the performance, on live television... Which was being televised to almost 2 billion homes.<br /><br /><br />All rights go to their respective owners!<br />-----Copyright Disclaimer Under Section 107 of the Copyright Act 1976, allowance is made for fair use for purposes such as criticism, comment, news reporting, teaching, scholarship, and research. Fair use is a use permitted by copyright statute that might otherwise be infringing. Non-profit, educational or personal use tips the balance in favor of fair use''',
|
||||||
|
1477,
|
||||||
|
[{
|
||||||
|
'start_time': 36,
|
||||||
|
'end_time': 162,
|
||||||
|
'title': 'Bohemian Rhapsody',
|
||||||
|
}, {
|
||||||
|
'start_time': 162,
|
||||||
|
'end_time': 413,
|
||||||
|
'title': 'Radio Ga Ga',
|
||||||
|
}, {
|
||||||
|
'start_time': 413,
|
||||||
|
'end_time': 454,
|
||||||
|
'title': 'Ay Oh!',
|
||||||
|
}, {
|
||||||
|
'start_time': 454,
|
||||||
|
'end_time': 728,
|
||||||
|
'title': 'Hammer To Fall',
|
||||||
|
}, {
|
||||||
|
'start_time': 728,
|
||||||
|
'end_time': 963,
|
||||||
|
'title': 'Crazy Little Thing Called Love',
|
||||||
|
}, {
|
||||||
|
'start_time': 963,
|
||||||
|
'end_time': 1038,
|
||||||
|
'title': 'We Will Rock You',
|
||||||
|
}, {
|
||||||
|
'start_time': 1038,
|
||||||
|
'end_time': 1272,
|
||||||
|
'title': 'We Are The Champions',
|
||||||
|
}, {
|
||||||
|
'start_time': 1272,
|
||||||
|
'end_time': 1477,
|
||||||
|
'title': 'Is This The World We Created...?',
|
||||||
|
}]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
# https://www.youtube.com/watch?v=ekYlRhALiRQ
|
||||||
|
# pattern: <num>. <title> 0:00
|
||||||
|
'1. Those Beaten Paths of Confusion <a href="#" onclick="yt.www.watch.player.seekTo(0*60+00);return false;">0:00</a><br />2. Beyond the Shadows of Emptiness & Nothingness <a href="#" onclick="yt.www.watch.player.seekTo(11*60+47);return false;">11:47</a><br />3. Poison Yourself...With Thought <a href="#" onclick="yt.www.watch.player.seekTo(26*60+30);return false;">26:30</a><br />4. The Agents of Transformation <a href="#" onclick="yt.www.watch.player.seekTo(35*60+57);return false;">35:57</a><br />5. Drowning in the Pain of Consciousness <a href="#" onclick="yt.www.watch.player.seekTo(44*60+32);return false;">44:32</a><br />6. Deny the Disease of Life <a href="#" onclick="yt.www.watch.player.seekTo(53*60+07);return false;">53:07</a><br /><br />More info/Buy: http://crepusculonegro.storenvy.com/products/257645-cn-03-arizmenda-within-the-vacuum-of-infinity<br /><br />No copyright is intended. The rights to this video are assumed by the owner and its affiliates.',
|
||||||
|
4009,
|
||||||
|
[{
|
||||||
|
'start_time': 0,
|
||||||
|
'end_time': 707,
|
||||||
|
'title': '1. Those Beaten Paths of Confusion',
|
||||||
|
}, {
|
||||||
|
'start_time': 707,
|
||||||
|
'end_time': 1590,
|
||||||
|
'title': '2. Beyond the Shadows of Emptiness & Nothingness',
|
||||||
|
}, {
|
||||||
|
'start_time': 1590,
|
||||||
|
'end_time': 2157,
|
||||||
|
'title': '3. Poison Yourself...With Thought',
|
||||||
|
}, {
|
||||||
|
'start_time': 2157,
|
||||||
|
'end_time': 2672,
|
||||||
|
'title': '4. The Agents of Transformation',
|
||||||
|
}, {
|
||||||
|
'start_time': 2672,
|
||||||
|
'end_time': 3187,
|
||||||
|
'title': '5. Drowning in the Pain of Consciousness',
|
||||||
|
}, {
|
||||||
|
'start_time': 3187,
|
||||||
|
'end_time': 4009,
|
||||||
|
'title': '6. Deny the Disease of Life',
|
||||||
|
}]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
# https://www.youtube.com/watch?v=WjL4pSzog9w
|
||||||
|
# pattern: 00:00 <title>
|
||||||
|
'<a href="https://arizmenda.bandcamp.com/merch/despairs-depths-descended-cd" class="yt-uix-servicelink " data-target-new-window="True" data-servicelink="CDAQ6TgYACITCNf1raqT2dMCFdRjGAod_o0CBSj4HQ" data-url="https://arizmenda.bandcamp.com/merch/despairs-depths-descended-cd" rel="nofollow noopener" target="_blank">https://arizmenda.bandcamp.com/merch/...</a><br /><br /><a href="#" onclick="yt.www.watch.player.seekTo(00*60+00);return false;">00:00</a> Christening Unborn Deformities <br /><a href="#" onclick="yt.www.watch.player.seekTo(07*60+08);return false;">07:08</a> Taste of Purity<br /><a href="#" onclick="yt.www.watch.player.seekTo(16*60+16);return false;">16:16</a> Sculpting Sins of a Universal Tongue<br /><a href="#" onclick="yt.www.watch.player.seekTo(24*60+45);return false;">24:45</a> Birth<br /><a href="#" onclick="yt.www.watch.player.seekTo(31*60+24);return false;">31:24</a> Neves<br /><a href="#" onclick="yt.www.watch.player.seekTo(37*60+55);return false;">37:55</a> Libations in Limbo',
|
||||||
|
2705,
|
||||||
|
[{
|
||||||
|
'start_time': 0,
|
||||||
|
'end_time': 428,
|
||||||
|
'title': 'Christening Unborn Deformities',
|
||||||
|
}, {
|
||||||
|
'start_time': 428,
|
||||||
|
'end_time': 976,
|
||||||
|
'title': 'Taste of Purity',
|
||||||
|
}, {
|
||||||
|
'start_time': 976,
|
||||||
|
'end_time': 1485,
|
||||||
|
'title': 'Sculpting Sins of a Universal Tongue',
|
||||||
|
}, {
|
||||||
|
'start_time': 1485,
|
||||||
|
'end_time': 1884,
|
||||||
|
'title': 'Birth',
|
||||||
|
}, {
|
||||||
|
'start_time': 1884,
|
||||||
|
'end_time': 2275,
|
||||||
|
'title': 'Neves',
|
||||||
|
}, {
|
||||||
|
'start_time': 2275,
|
||||||
|
'end_time': 2705,
|
||||||
|
'title': 'Libations in Limbo',
|
||||||
|
}]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
# https://www.youtube.com/watch?v=o3r1sn-t3is
|
||||||
|
# pattern: <title> 00:00 <note>
|
||||||
|
'Download this show in MP3: <a href="http://sh.st/njZKK" class="yt-uix-servicelink " data-url="http://sh.st/njZKK" data-target-new-window="True" data-servicelink="CDAQ6TgYACITCK3j8_6o2dMCFVDCGAoduVAKKij4HQ" rel="nofollow noopener" target="_blank">http://sh.st/njZKK</a><br /><br />Setlist:<br />I-E-A-I-A-I-O <a href="#" onclick="yt.www.watch.player.seekTo(00*60+45);return false;">00:45</a><br />Suite-Pee <a href="#" onclick="yt.www.watch.player.seekTo(4*60+26);return false;">4:26</a> (Incomplete)<br />Attack <a href="#" onclick="yt.www.watch.player.seekTo(5*60+31);return false;">5:31</a> (First live performance since 2011)<br />Prison Song <a href="#" onclick="yt.www.watch.player.seekTo(8*60+42);return false;">8:42</a><br />Know <a href="#" onclick="yt.www.watch.player.seekTo(12*60+32);return false;">12:32</a> (First live performance since 2011)<br />Aerials <a href="#" onclick="yt.www.watch.player.seekTo(15*60+32);return false;">15:32</a><br />Soldier Side - Intro <a href="#" onclick="yt.www.watch.player.seekTo(19*60+13);return false;">19:13</a><br />B.Y.O.B. <a href="#" onclick="yt.www.watch.player.seekTo(20*60+09);return false;">20:09</a><br />Soil <a href="#" onclick="yt.www.watch.player.seekTo(24*60+32);return false;">24:32</a><br />Darts <a href="#" onclick="yt.www.watch.player.seekTo(27*60+48);return false;">27:48</a><br />Radio/Video <a href="#" onclick="yt.www.watch.player.seekTo(30*60+38);return false;">30:38</a><br />Hypnotize <a href="#" onclick="yt.www.watch.player.seekTo(35*60+05);return false;">35:05</a><br />Temper <a href="#" onclick="yt.www.watch.player.seekTo(38*60+08);return false;">38:08</a> (First live performance since 1999)<br />CUBErt <a href="#" onclick="yt.www.watch.player.seekTo(41*60+00);return false;">41:00</a><br />Needles <a href="#" onclick="yt.www.watch.player.seekTo(42*60+57);return false;">42:57</a><br />Deer Dance <a href="#" onclick="yt.www.watch.player.seekTo(46*60+27);return false;">46:27</a><br />Bounce <a href="#" onclick="yt.www.watch.player.seekTo(49*60+38);return false;">49:38</a><br />Suggestions <a href="#" onclick="yt.www.watch.player.seekTo(51*60+25);return false;">51:25</a><br />Psycho <a href="#" onclick="yt.www.watch.player.seekTo(53*60+52);return false;">53:52</a><br />Chop Suey! <a href="#" onclick="yt.www.watch.player.seekTo(58*60+13);return false;">58:13</a><br />Lonely Day <a href="#" onclick="yt.www.watch.player.seekTo(1*3600+01*60+15);return false;">1:01:15</a><br />Question! <a href="#" onclick="yt.www.watch.player.seekTo(1*3600+04*60+14);return false;">1:04:14</a><br />Lost in Hollywood <a href="#" onclick="yt.www.watch.player.seekTo(1*3600+08*60+10);return false;">1:08:10</a><br />Vicinity of Obscenity <a href="#" onclick="yt.www.watch.player.seekTo(1*3600+13*60+40);return false;">1:13:40</a>(First live performance since 2012)<br />Forest <a href="#" onclick="yt.www.watch.player.seekTo(1*3600+16*60+17);return false;">1:16:17</a><br />Cigaro <a href="#" onclick="yt.www.watch.player.seekTo(1*3600+20*60+02);return false;">1:20:02</a><br />Toxicity <a href="#" onclick="yt.www.watch.player.seekTo(1*3600+23*60+57);return false;">1:23:57</a>(with Chino Moreno)<br />Sugar <a href="#" onclick="yt.www.watch.player.seekTo(1*3600+27*60+53);return false;">1:27:53</a>',
|
||||||
|
5640,
|
||||||
|
[{
|
||||||
|
'start_time': 45,
|
||||||
|
'end_time': 266,
|
||||||
|
'title': 'I-E-A-I-A-I-O',
|
||||||
|
}, {
|
||||||
|
'start_time': 266,
|
||||||
|
'end_time': 331,
|
||||||
|
'title': 'Suite-Pee (Incomplete)',
|
||||||
|
}, {
|
||||||
|
'start_time': 331,
|
||||||
|
'end_time': 522,
|
||||||
|
'title': 'Attack (First live performance since 2011)',
|
||||||
|
}, {
|
||||||
|
'start_time': 522,
|
||||||
|
'end_time': 752,
|
||||||
|
'title': 'Prison Song',
|
||||||
|
}, {
|
||||||
|
'start_time': 752,
|
||||||
|
'end_time': 932,
|
||||||
|
'title': 'Know (First live performance since 2011)',
|
||||||
|
}, {
|
||||||
|
'start_time': 932,
|
||||||
|
'end_time': 1153,
|
||||||
|
'title': 'Aerials',
|
||||||
|
}, {
|
||||||
|
'start_time': 1153,
|
||||||
|
'end_time': 1209,
|
||||||
|
'title': 'Soldier Side - Intro',
|
||||||
|
}, {
|
||||||
|
'start_time': 1209,
|
||||||
|
'end_time': 1472,
|
||||||
|
'title': 'B.Y.O.B.',
|
||||||
|
}, {
|
||||||
|
'start_time': 1472,
|
||||||
|
'end_time': 1668,
|
||||||
|
'title': 'Soil',
|
||||||
|
}, {
|
||||||
|
'start_time': 1668,
|
||||||
|
'end_time': 1838,
|
||||||
|
'title': 'Darts',
|
||||||
|
}, {
|
||||||
|
'start_time': 1838,
|
||||||
|
'end_time': 2105,
|
||||||
|
'title': 'Radio/Video',
|
||||||
|
}, {
|
||||||
|
'start_time': 2105,
|
||||||
|
'end_time': 2288,
|
||||||
|
'title': 'Hypnotize',
|
||||||
|
}, {
|
||||||
|
'start_time': 2288,
|
||||||
|
'end_time': 2460,
|
||||||
|
'title': 'Temper (First live performance since 1999)',
|
||||||
|
}, {
|
||||||
|
'start_time': 2460,
|
||||||
|
'end_time': 2577,
|
||||||
|
'title': 'CUBErt',
|
||||||
|
}, {
|
||||||
|
'start_time': 2577,
|
||||||
|
'end_time': 2787,
|
||||||
|
'title': 'Needles',
|
||||||
|
}, {
|
||||||
|
'start_time': 2787,
|
||||||
|
'end_time': 2978,
|
||||||
|
'title': 'Deer Dance',
|
||||||
|
}, {
|
||||||
|
'start_time': 2978,
|
||||||
|
'end_time': 3085,
|
||||||
|
'title': 'Bounce',
|
||||||
|
}, {
|
||||||
|
'start_time': 3085,
|
||||||
|
'end_time': 3232,
|
||||||
|
'title': 'Suggestions',
|
||||||
|
}, {
|
||||||
|
'start_time': 3232,
|
||||||
|
'end_time': 3493,
|
||||||
|
'title': 'Psycho',
|
||||||
|
}, {
|
||||||
|
'start_time': 3493,
|
||||||
|
'end_time': 3675,
|
||||||
|
'title': 'Chop Suey!',
|
||||||
|
}, {
|
||||||
|
'start_time': 3675,
|
||||||
|
'end_time': 3854,
|
||||||
|
'title': 'Lonely Day',
|
||||||
|
}, {
|
||||||
|
'start_time': 3854,
|
||||||
|
'end_time': 4090,
|
||||||
|
'title': 'Question!',
|
||||||
|
}, {
|
||||||
|
'start_time': 4090,
|
||||||
|
'end_time': 4420,
|
||||||
|
'title': 'Lost in Hollywood',
|
||||||
|
}, {
|
||||||
|
'start_time': 4420,
|
||||||
|
'end_time': 4577,
|
||||||
|
'title': 'Vicinity of Obscenity (First live performance since 2012)',
|
||||||
|
}, {
|
||||||
|
'start_time': 4577,
|
||||||
|
'end_time': 4802,
|
||||||
|
'title': 'Forest',
|
||||||
|
}, {
|
||||||
|
'start_time': 4802,
|
||||||
|
'end_time': 5037,
|
||||||
|
'title': 'Cigaro',
|
||||||
|
}, {
|
||||||
|
'start_time': 5037,
|
||||||
|
'end_time': 5273,
|
||||||
|
'title': 'Toxicity (with Chino Moreno)',
|
||||||
|
}, {
|
||||||
|
'start_time': 5273,
|
||||||
|
'end_time': 5640,
|
||||||
|
'title': 'Sugar',
|
||||||
|
}]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
# https://www.youtube.com/watch?v=PkYLQbsqCE8
|
||||||
|
# pattern: <num> - <title> [<latinized title>] 0:00:00
|
||||||
|
'''Затемно (Zatemno) is an Obscure Black Metal Band from Russia.<br /><br />"Во прах (Vo prakh)'' Into The Ashes", Debut mini-album released may 6, 2016, by Death Knell Productions<br />Released on 6 panel digipak CD, limited to 100 copies only<br />And digital format on Bandcamp<br /><br />Tracklist<br /><br />1 - Во прах [Vo prakh] <a href="#" onclick="yt.www.watch.player.seekTo(0*3600+00*60+00);return false;">0:00:00</a><br />2 - Искупление [Iskupleniye] <a href="#" onclick="yt.www.watch.player.seekTo(0*3600+08*60+10);return false;">0:08:10</a><br />3 - Из серпов луны...[Iz serpov luny] <a href="#" onclick="yt.www.watch.player.seekTo(0*3600+14*60+30);return false;">0:14:30</a><br /><br />Links:<br /><a href="https://deathknellprod.bandcamp.com/album/--2" class="yt-uix-servicelink " data-target-new-window="True" data-url="https://deathknellprod.bandcamp.com/album/--2" data-servicelink="CC8Q6TgYACITCNP234Kr2dMCFcNxGAodQqsIwSj4HQ" target="_blank" rel="nofollow noopener">https://deathknellprod.bandcamp.com/a...</a><br /><a href="https://www.facebook.com/DeathKnellProd/" class="yt-uix-servicelink " data-target-new-window="True" data-url="https://www.facebook.com/DeathKnellProd/" data-servicelink="CC8Q6TgYACITCNP234Kr2dMCFcNxGAodQqsIwSj4HQ" target="_blank" rel="nofollow noopener">https://www.facebook.com/DeathKnellProd/</a><br /><br /><br />I don't have any right about this artifact, my only intention is to spread the music of the band, all rights are reserved to the Затемно (Zatemno) and his producers, Death Knell Productions.<br /><br />------------------------------------------------------------------<br /><br />Subscribe for more videos like this.<br />My link: <a href="https://web.facebook.com/AttackOfTheDragons" class="yt-uix-servicelink " data-target-new-window="True" data-url="https://web.facebook.com/AttackOfTheDragons" data-servicelink="CC8Q6TgYACITCNP234Kr2dMCFcNxGAodQqsIwSj4HQ" target="_blank" rel="nofollow noopener">https://web.facebook.com/AttackOfTheD...</a>''',
|
||||||
|
1138,
|
||||||
|
[{
|
||||||
|
'start_time': 0,
|
||||||
|
'end_time': 490,
|
||||||
|
'title': '1 - Во прах [Vo prakh]',
|
||||||
|
}, {
|
||||||
|
'start_time': 490,
|
||||||
|
'end_time': 870,
|
||||||
|
'title': '2 - Искупление [Iskupleniye]',
|
||||||
|
}, {
|
||||||
|
'start_time': 870,
|
||||||
|
'end_time': 1138,
|
||||||
|
'title': '3 - Из серпов луны...[Iz serpov luny]',
|
||||||
|
}]
|
||||||
|
),
|
||||||
|
(
|
||||||
|
# https://www.youtube.com/watch?v=xZW70zEasOk
|
||||||
|
# time point more than duration
|
||||||
|
'''● LCS Spring finals: Saturday and Sunday from <a href="#" onclick="yt.www.watch.player.seekTo(13*60+30);return false;">13:30</a> outside the venue! <br />● PAX East: Fri, Sat & Sun - more info in tomorrows video on the main channel!''',
|
||||||
|
283,
|
||||||
|
[]
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_youtube_chapters(self):
|
||||||
|
for description, duration, expected_chapters in self._TEST_CASES:
|
||||||
|
ie = YoutubeIE()
|
||||||
|
expect_value(
|
||||||
|
self, ie._extract_chapters(description, duration),
|
||||||
|
expected_chapters, None)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@@ -58,6 +58,7 @@ from .utils import (
|
|||||||
format_bytes,
|
format_bytes,
|
||||||
formatSeconds,
|
formatSeconds,
|
||||||
GeoRestrictedError,
|
GeoRestrictedError,
|
||||||
|
int_or_none,
|
||||||
ISO3166Utils,
|
ISO3166Utils,
|
||||||
locked_file,
|
locked_file,
|
||||||
make_HTTPS_handler,
|
make_HTTPS_handler,
|
||||||
@@ -302,6 +303,17 @@ class YoutubeDL(object):
|
|||||||
postprocessor.
|
postprocessor.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_NUMERIC_FIELDS = set((
|
||||||
|
'width', 'height', 'tbr', 'abr', 'asr', 'vbr', 'fps', 'filesize', 'filesize_approx',
|
||||||
|
'timestamp', 'upload_year', 'upload_month', 'upload_day',
|
||||||
|
'duration', 'view_count', 'like_count', 'dislike_count', 'repost_count',
|
||||||
|
'average_rating', 'comment_count', 'age_limit',
|
||||||
|
'start_time', 'end_time',
|
||||||
|
'chapter_number', 'season_number', 'episode_number',
|
||||||
|
'track_number', 'disc_number', 'release_year',
|
||||||
|
'playlist_index',
|
||||||
|
))
|
||||||
|
|
||||||
params = None
|
params = None
|
||||||
_ies = []
|
_ies = []
|
||||||
_pps = []
|
_pps = []
|
||||||
@@ -370,10 +382,10 @@ class YoutubeDL(object):
|
|||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if (sys.version_info >= (3,) and sys.platform != 'win32' and
|
if (sys.platform != 'win32' and
|
||||||
sys.getfilesystemencoding() in ['ascii', 'ANSI_X3.4-1968'] and
|
sys.getfilesystemencoding() in ['ascii', 'ANSI_X3.4-1968'] and
|
||||||
not params.get('restrictfilenames', False)):
|
not params.get('restrictfilenames', False)):
|
||||||
# On Python 3, the Unicode filesystem API will throw errors (#1474)
|
# Unicode filesystem API will throw errors (#1474, #13027)
|
||||||
self.report_warning(
|
self.report_warning(
|
||||||
'Assuming --restrict-filenames since file system encoding '
|
'Assuming --restrict-filenames since file system encoding '
|
||||||
'cannot encode all characters. '
|
'cannot encode all characters. '
|
||||||
@@ -498,24 +510,25 @@ class YoutubeDL(object):
|
|||||||
def to_console_title(self, message):
|
def to_console_title(self, message):
|
||||||
if not self.params.get('consoletitle', False):
|
if not self.params.get('consoletitle', False):
|
||||||
return
|
return
|
||||||
if compat_os_name == 'nt' and ctypes.windll.kernel32.GetConsoleWindow():
|
if compat_os_name == 'nt':
|
||||||
# c_wchar_p() might not be necessary if `message` is
|
if ctypes.windll.kernel32.GetConsoleWindow():
|
||||||
# already of type unicode()
|
# c_wchar_p() might not be necessary if `message` is
|
||||||
ctypes.windll.kernel32.SetConsoleTitleW(ctypes.c_wchar_p(message))
|
# already of type unicode()
|
||||||
|
ctypes.windll.kernel32.SetConsoleTitleW(ctypes.c_wchar_p(message))
|
||||||
elif 'TERM' in os.environ:
|
elif 'TERM' in os.environ:
|
||||||
self._write_string('\033]0;%s\007' % message, self._screen_file)
|
self._write_string('\033]0;%s\007' % message, self._screen_file)
|
||||||
|
|
||||||
def save_console_title(self):
|
def save_console_title(self):
|
||||||
if not self.params.get('consoletitle', False):
|
if not self.params.get('consoletitle', False):
|
||||||
return
|
return
|
||||||
if 'TERM' in os.environ:
|
if compat_os_name != 'nt' and 'TERM' in os.environ:
|
||||||
# Save the title on stack
|
# Save the title on stack
|
||||||
self._write_string('\033[22;0t', self._screen_file)
|
self._write_string('\033[22;0t', self._screen_file)
|
||||||
|
|
||||||
def restore_console_title(self):
|
def restore_console_title(self):
|
||||||
if not self.params.get('consoletitle', False):
|
if not self.params.get('consoletitle', False):
|
||||||
return
|
return
|
||||||
if 'TERM' in os.environ:
|
if compat_os_name != 'nt' and 'TERM' in os.environ:
|
||||||
# Restore the title from stack
|
# Restore the title from stack
|
||||||
self._write_string('\033[23;0t', self._screen_file)
|
self._write_string('\033[23;0t', self._screen_file)
|
||||||
|
|
||||||
@@ -638,22 +651,11 @@ class YoutubeDL(object):
|
|||||||
r'%%(\1)0%dd' % field_size_compat_map[mobj.group('field')],
|
r'%%(\1)0%dd' % field_size_compat_map[mobj.group('field')],
|
||||||
outtmpl)
|
outtmpl)
|
||||||
|
|
||||||
NUMERIC_FIELDS = set((
|
|
||||||
'width', 'height', 'tbr', 'abr', 'asr', 'vbr', 'fps', 'filesize', 'filesize_approx',
|
|
||||||
'timestamp', 'upload_year', 'upload_month', 'upload_day',
|
|
||||||
'duration', 'view_count', 'like_count', 'dislike_count', 'repost_count',
|
|
||||||
'average_rating', 'comment_count', 'age_limit',
|
|
||||||
'start_time', 'end_time',
|
|
||||||
'chapter_number', 'season_number', 'episode_number',
|
|
||||||
'track_number', 'disc_number', 'release_year',
|
|
||||||
'playlist_index',
|
|
||||||
))
|
|
||||||
|
|
||||||
# Missing numeric fields used together with integer presentation types
|
# Missing numeric fields used together with integer presentation types
|
||||||
# in format specification will break the argument substitution since
|
# in format specification will break the argument substitution since
|
||||||
# string 'NA' is returned for missing fields. We will patch output
|
# string 'NA' is returned for missing fields. We will patch output
|
||||||
# template for missing fields to meet string presentation type.
|
# template for missing fields to meet string presentation type.
|
||||||
for numeric_field in NUMERIC_FIELDS:
|
for numeric_field in self._NUMERIC_FIELDS:
|
||||||
if numeric_field not in template_dict:
|
if numeric_field not in template_dict:
|
||||||
# As of [1] format syntax is:
|
# As of [1] format syntax is:
|
||||||
# %[mapping_key][conversion_flags][minimum_width][.precision][length_modifier]type
|
# %[mapping_key][conversion_flags][minimum_width][.precision][length_modifier]type
|
||||||
@@ -1344,9 +1346,28 @@ class YoutubeDL(object):
|
|||||||
if 'title' not in info_dict:
|
if 'title' not in info_dict:
|
||||||
raise ExtractorError('Missing "title" field in extractor result')
|
raise ExtractorError('Missing "title" field in extractor result')
|
||||||
|
|
||||||
if not isinstance(info_dict['id'], compat_str):
|
def report_force_conversion(field, field_not, conversion):
|
||||||
self.report_warning('"id" field is not a string - forcing string conversion')
|
self.report_warning(
|
||||||
info_dict['id'] = compat_str(info_dict['id'])
|
'"%s" field is not %s - forcing %s conversion, there is an error in extractor'
|
||||||
|
% (field, field_not, conversion))
|
||||||
|
|
||||||
|
def sanitize_string_field(info, string_field):
|
||||||
|
field = info.get(string_field)
|
||||||
|
if field is None or isinstance(field, compat_str):
|
||||||
|
return
|
||||||
|
report_force_conversion(string_field, 'a string', 'string')
|
||||||
|
info[string_field] = compat_str(field)
|
||||||
|
|
||||||
|
def sanitize_numeric_fields(info):
|
||||||
|
for numeric_field in self._NUMERIC_FIELDS:
|
||||||
|
field = info.get(numeric_field)
|
||||||
|
if field is None or isinstance(field, compat_numeric_types):
|
||||||
|
continue
|
||||||
|
report_force_conversion(numeric_field, 'numeric', 'int')
|
||||||
|
info[numeric_field] = int_or_none(field)
|
||||||
|
|
||||||
|
sanitize_string_field(info_dict, 'id')
|
||||||
|
sanitize_numeric_fields(info_dict)
|
||||||
|
|
||||||
if 'playlist' not in info_dict:
|
if 'playlist' not in info_dict:
|
||||||
# It isn't part of a playlist
|
# It isn't part of a playlist
|
||||||
@@ -1434,6 +1455,8 @@ class YoutubeDL(object):
|
|||||||
if 'url' not in format:
|
if 'url' not in format:
|
||||||
raise ExtractorError('Missing "url" key in result (index %d)' % i)
|
raise ExtractorError('Missing "url" key in result (index %d)' % i)
|
||||||
|
|
||||||
|
sanitize_string_field(format, 'format_id')
|
||||||
|
sanitize_numeric_fields(format)
|
||||||
format['url'] = sanitize_url(format['url'])
|
format['url'] = sanitize_url(format['url'])
|
||||||
|
|
||||||
if format.get('format_id') is None:
|
if format.get('format_id') is None:
|
||||||
|
@@ -2322,6 +2322,19 @@ try:
|
|||||||
except ImportError: # Python 2
|
except ImportError: # Python 2
|
||||||
from HTMLParser import HTMLParser as compat_HTMLParser
|
from HTMLParser import HTMLParser as compat_HTMLParser
|
||||||
|
|
||||||
|
try: # Python 2
|
||||||
|
from HTMLParser import HTMLParseError as compat_HTMLParseError
|
||||||
|
except ImportError: # Python <3.4
|
||||||
|
try:
|
||||||
|
from html.parser import HTMLParseError as compat_HTMLParseError
|
||||||
|
except ImportError: # Python >3.4
|
||||||
|
|
||||||
|
# HTMLParseError has been deprecated in Python 3.3 and removed in
|
||||||
|
# Python 3.5. Introducing dummy exception for Python >3.5 for compatible
|
||||||
|
# and uniform cross-version exceptiong handling
|
||||||
|
class compat_HTMLParseError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from subprocess import DEVNULL
|
from subprocess import DEVNULL
|
||||||
compat_subprocess_get_DEVNULL = lambda: DEVNULL
|
compat_subprocess_get_DEVNULL = lambda: DEVNULL
|
||||||
@@ -2604,14 +2617,22 @@ except ImportError: # Python 2
|
|||||||
parsed_result[name] = [value]
|
parsed_result[name] = [value]
|
||||||
return parsed_result
|
return parsed_result
|
||||||
|
|
||||||
try:
|
|
||||||
from shlex import quote as compat_shlex_quote
|
compat_os_name = os._name if os.name == 'java' else os.name
|
||||||
except ImportError: # Python < 3.3
|
|
||||||
|
|
||||||
|
if compat_os_name == 'nt':
|
||||||
def compat_shlex_quote(s):
|
def compat_shlex_quote(s):
|
||||||
if re.match(r'^[-_\w./]+$', s):
|
return s if re.match(r'^[-_\w./]+$', s) else '"%s"' % s.replace('"', '\\"')
|
||||||
return s
|
else:
|
||||||
else:
|
try:
|
||||||
return "'" + s.replace("'", "'\"'\"'") + "'"
|
from shlex import quote as compat_shlex_quote
|
||||||
|
except ImportError: # Python < 3.3
|
||||||
|
def compat_shlex_quote(s):
|
||||||
|
if re.match(r'^[-_\w./]+$', s):
|
||||||
|
return s
|
||||||
|
else:
|
||||||
|
return "'" + s.replace("'", "'\"'\"'") + "'"
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -2636,9 +2657,6 @@ def compat_ord(c):
|
|||||||
return ord(c)
|
return ord(c)
|
||||||
|
|
||||||
|
|
||||||
compat_os_name = os._name if os.name == 'java' else os.name
|
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info >= (3, 0):
|
if sys.version_info >= (3, 0):
|
||||||
compat_getenv = os.getenv
|
compat_getenv = os.getenv
|
||||||
compat_expanduser = os.path.expanduser
|
compat_expanduser = os.path.expanduser
|
||||||
@@ -2882,6 +2900,7 @@ else:
|
|||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
'compat_HTMLParseError',
|
||||||
'compat_HTMLParser',
|
'compat_HTMLParser',
|
||||||
'compat_HTTPError',
|
'compat_HTTPError',
|
||||||
'compat_basestring',
|
'compat_basestring',
|
||||||
|
@@ -8,10 +8,11 @@ import random
|
|||||||
|
|
||||||
from ..compat import compat_os_name
|
from ..compat import compat_os_name
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
decodeArgument,
|
||||||
encodeFilename,
|
encodeFilename,
|
||||||
error_to_compat_str,
|
error_to_compat_str,
|
||||||
decodeArgument,
|
|
||||||
format_bytes,
|
format_bytes,
|
||||||
|
shell_quote,
|
||||||
timeconvert,
|
timeconvert,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -381,10 +382,5 @@ class FileDownloader(object):
|
|||||||
if exe is None:
|
if exe is None:
|
||||||
exe = os.path.basename(str_args[0])
|
exe = os.path.basename(str_args[0])
|
||||||
|
|
||||||
try:
|
|
||||||
import pipes
|
|
||||||
shell_quote = lambda args: ' '.join(map(pipes.quote, str_args))
|
|
||||||
except ImportError:
|
|
||||||
shell_quote = repr
|
|
||||||
self.to_screen('[debug] %s command line: %s' % (
|
self.to_screen('[debug] %s command line: %s' % (
|
||||||
exe, shell_quote(str_args)))
|
exe, shell_quote(str_args)))
|
||||||
|
@@ -29,7 +29,17 @@ class ExternalFD(FileDownloader):
|
|||||||
self.report_destination(filename)
|
self.report_destination(filename)
|
||||||
tmpfilename = self.temp_name(filename)
|
tmpfilename = self.temp_name(filename)
|
||||||
|
|
||||||
retval = self._call_downloader(tmpfilename, info_dict)
|
try:
|
||||||
|
retval = self._call_downloader(tmpfilename, info_dict)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
if not info_dict.get('is_live'):
|
||||||
|
raise
|
||||||
|
# Live stream downloading cancellation should be considered as
|
||||||
|
# correct and expected termination thus all postprocessing
|
||||||
|
# should take place
|
||||||
|
retval = 0
|
||||||
|
self.to_screen('[%s] Interrupted by user' % self.get_basename())
|
||||||
|
|
||||||
if retval == 0:
|
if retval == 0:
|
||||||
fsize = os.path.getsize(encodeFilename(tmpfilename))
|
fsize = os.path.getsize(encodeFilename(tmpfilename))
|
||||||
self.to_screen('\r[%s] Downloaded %s bytes' % (self.get_basename(), fsize))
|
self.to_screen('\r[%s] Downloaded %s bytes' % (self.get_basename(), fsize))
|
||||||
@@ -202,6 +212,11 @@ class FFmpegFD(ExternalFD):
|
|||||||
|
|
||||||
args = [ffpp.executable, '-y']
|
args = [ffpp.executable, '-y']
|
||||||
|
|
||||||
|
for log_level in ('quiet', 'verbose'):
|
||||||
|
if self.params.get(log_level, False):
|
||||||
|
args += ['-loglevel', log_level]
|
||||||
|
break
|
||||||
|
|
||||||
seekable = info_dict.get('_seekable')
|
seekable = info_dict.get('_seekable')
|
||||||
if seekable is not None:
|
if seekable is not None:
|
||||||
# setting -seekable prevents ffmpeg from guessing if the server
|
# setting -seekable prevents ffmpeg from guessing if the server
|
||||||
|
@@ -49,7 +49,7 @@ class FragmentFD(FileDownloader):
|
|||||||
index: 0-based index of current fragment among all fragments
|
index: 0-based index of current fragment among all fragments
|
||||||
fragment_count:
|
fragment_count:
|
||||||
Total count of fragments
|
Total count of fragments
|
||||||
|
|
||||||
This feature is experimental and file format may change in future.
|
This feature is experimental and file format may change in future.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -155,8 +155,6 @@ class FragmentFD(FileDownloader):
|
|||||||
self._write_ytdl_file(ctx)
|
self._write_ytdl_file(ctx)
|
||||||
if ctx['fragment_index'] > 0:
|
if ctx['fragment_index'] > 0:
|
||||||
assert resume_len > 0
|
assert resume_len > 0
|
||||||
else:
|
|
||||||
assert resume_len == 0
|
|
||||||
|
|
||||||
dest_stream, tmpfilename = sanitize_open(tmpfilename, open_mode)
|
dest_stream, tmpfilename = sanitize_open(tmpfilename, open_mode)
|
||||||
|
|
||||||
|
@@ -12,7 +12,15 @@ from ..compat import compat_urlparse
|
|||||||
|
|
||||||
class AbcNewsVideoIE(AMPIE):
|
class AbcNewsVideoIE(AMPIE):
|
||||||
IE_NAME = 'abcnews:video'
|
IE_NAME = 'abcnews:video'
|
||||||
_VALID_URL = r'https?://abcnews\.go\.com/[^/]+/video/(?P<display_id>[0-9a-z-]+)-(?P<id>\d+)'
|
_VALID_URL = r'''(?x)
|
||||||
|
https?://
|
||||||
|
abcnews\.go\.com/
|
||||||
|
(?:
|
||||||
|
[^/]+/video/(?P<display_id>[0-9a-z-]+)-|
|
||||||
|
video/embed\?.*?\bid=
|
||||||
|
)
|
||||||
|
(?P<id>\d+)
|
||||||
|
'''
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://abcnews.go.com/ThisWeek/video/week-exclusive-irans-foreign-minister-zarif-20411932',
|
'url': 'http://abcnews.go.com/ThisWeek/video/week-exclusive-irans-foreign-minister-zarif-20411932',
|
||||||
@@ -29,6 +37,9 @@ class AbcNewsVideoIE(AMPIE):
|
|||||||
# m3u8 download
|
# m3u8 download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'http://abcnews.go.com/video/embed?id=46979033',
|
||||||
|
'only_matching': True,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://abcnews.go.com/2020/video/2020-husband-stands-teacher-jail-student-affairs-26119478',
|
'url': 'http://abcnews.go.com/2020/video/2020-husband-stands-teacher-jail-student-affairs-26119478',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
@@ -22,7 +22,7 @@ class ABCOTVSIE(InfoExtractor):
|
|||||||
'display_id': 'east-bay-museum-celebrates-vintage-synthesizers',
|
'display_id': 'east-bay-museum-celebrates-vintage-synthesizers',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'East Bay museum celebrates vintage synthesizers',
|
'title': 'East Bay museum celebrates vintage synthesizers',
|
||||||
'description': 'md5:a4f10fb2f2a02565c1749d4adbab4b10',
|
'description': 'md5:24ed2bd527096ec2a5c67b9d5a9005f3',
|
||||||
'thumbnail': r're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'timestamp': 1421123075,
|
'timestamp': 1421123075,
|
||||||
'upload_date': '20150113',
|
'upload_date': '20150113',
|
||||||
|
@@ -15,6 +15,7 @@ from ..utils import (
|
|||||||
intlist_to_bytes,
|
intlist_to_bytes,
|
||||||
srt_subtitles_timecode,
|
srt_subtitles_timecode,
|
||||||
strip_or_none,
|
strip_or_none,
|
||||||
|
urljoin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -31,25 +32,28 @@ class ADNIE(InfoExtractor):
|
|||||||
'description': 'md5:2f7b5aa76edbc1a7a92cedcda8a528d5',
|
'description': 'md5:2f7b5aa76edbc1a7a92cedcda8a528d5',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_BASE_URL = 'http://animedigitalnetwork.fr'
|
||||||
|
|
||||||
def _get_subtitles(self, sub_path, video_id):
|
def _get_subtitles(self, sub_path, video_id):
|
||||||
if not sub_path:
|
if not sub_path:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
enc_subtitles = self._download_webpage(
|
enc_subtitles = self._download_webpage(
|
||||||
'http://animedigitalnetwork.fr/' + sub_path,
|
urljoin(self._BASE_URL, sub_path),
|
||||||
video_id, fatal=False)
|
video_id, fatal=False, headers={
|
||||||
|
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0',
|
||||||
|
})
|
||||||
if not enc_subtitles:
|
if not enc_subtitles:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# http://animedigitalnetwork.fr/components/com_vodvideo/videojs/adn-vjs.min.js
|
# http://animedigitalnetwork.fr/components/com_vodvideo/videojs/adn-vjs.min.js
|
||||||
dec_subtitles = intlist_to_bytes(aes_cbc_decrypt(
|
dec_subtitles = intlist_to_bytes(aes_cbc_decrypt(
|
||||||
bytes_to_intlist(base64.b64decode(enc_subtitles[24:])),
|
bytes_to_intlist(base64.b64decode(enc_subtitles[24:])),
|
||||||
bytes_to_intlist(b'\nd\xaf\xd2J\xd0\xfc\xe1\xfc\xdf\xb61\xe8\xe1\xf0\xcc'),
|
bytes_to_intlist(b'\x1b\xe0\x29\x61\x38\x94\x24\x00\x12\xbd\xc5\x80\xac\xce\xbe\xb0'),
|
||||||
bytes_to_intlist(base64.b64decode(enc_subtitles[:24]))
|
bytes_to_intlist(base64.b64decode(enc_subtitles[:24]))
|
||||||
))
|
))
|
||||||
subtitles_json = self._parse_json(
|
subtitles_json = self._parse_json(
|
||||||
dec_subtitles[:-compat_ord(dec_subtitles[-1])],
|
dec_subtitles[:-compat_ord(dec_subtitles[-1])].decode(),
|
||||||
None, fatal=False)
|
None, fatal=False)
|
||||||
if not subtitles_json:
|
if not subtitles_json:
|
||||||
return None
|
return None
|
||||||
@@ -103,9 +107,16 @@ class ADNIE(InfoExtractor):
|
|||||||
metas = options.get('metas') or {}
|
metas = options.get('metas') or {}
|
||||||
title = metas.get('title') or video_info['title']
|
title = metas.get('title') or video_info['title']
|
||||||
links = player_config.get('links') or {}
|
links = player_config.get('links') or {}
|
||||||
|
if not links:
|
||||||
|
links_url = player_config['linksurl']
|
||||||
|
links_data = self._download_json(urljoin(
|
||||||
|
self._BASE_URL, links_url), video_id)
|
||||||
|
links = links_data.get('links') or {}
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for format_id, qualities in links.items():
|
for format_id, qualities in links.items():
|
||||||
|
if not isinstance(qualities, dict):
|
||||||
|
continue
|
||||||
for load_balancer_url in qualities.values():
|
for load_balancer_url in qualities.values():
|
||||||
load_balancer_data = self._download_json(
|
load_balancer_data = self._download_json(
|
||||||
load_balancer_url, video_id, fatal=False) or {}
|
load_balancer_url, video_id, fatal=False) or {}
|
||||||
|
@@ -6,7 +6,10 @@ import time
|
|||||||
import xml.etree.ElementTree as etree
|
import xml.etree.ElementTree as etree
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_urlparse
|
from ..compat import (
|
||||||
|
compat_kwargs,
|
||||||
|
compat_urlparse,
|
||||||
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
urlencode_postdata,
|
urlencode_postdata,
|
||||||
@@ -36,6 +39,11 @@ MSO_INFO = {
|
|||||||
'username_field': 'Ecom_User_ID',
|
'username_field': 'Ecom_User_ID',
|
||||||
'password_field': 'Ecom_Password',
|
'password_field': 'Ecom_Password',
|
||||||
},
|
},
|
||||||
|
'Brighthouse': {
|
||||||
|
'name': 'Bright House Networks | Spectrum',
|
||||||
|
'username_field': 'j_username',
|
||||||
|
'password_field': 'j_password',
|
||||||
|
},
|
||||||
'Charter_Direct': {
|
'Charter_Direct': {
|
||||||
'name': 'Charter Spectrum',
|
'name': 'Charter Spectrum',
|
||||||
'username_field': 'IDToken1',
|
'username_field': 'IDToken1',
|
||||||
@@ -1308,6 +1316,13 @@ class AdobePassIE(InfoExtractor):
|
|||||||
_USER_AGENT = 'Mozilla/5.0 (X11; Linux i686; rv:47.0) Gecko/20100101 Firefox/47.0'
|
_USER_AGENT = 'Mozilla/5.0 (X11; Linux i686; rv:47.0) Gecko/20100101 Firefox/47.0'
|
||||||
_MVPD_CACHE = 'ap-mvpd'
|
_MVPD_CACHE = 'ap-mvpd'
|
||||||
|
|
||||||
|
def _download_webpage_handle(self, *args, **kwargs):
|
||||||
|
headers = kwargs.get('headers', {})
|
||||||
|
headers.update(self.geo_verification_headers())
|
||||||
|
kwargs['headers'] = headers
|
||||||
|
return super(AdobePassIE, self)._download_webpage_handle(
|
||||||
|
*args, **compat_kwargs(kwargs))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_mvpd_resource(provider_id, title, guid, rating):
|
def _get_mvpd_resource(provider_id, title, guid, rating):
|
||||||
channel = etree.Element('channel')
|
channel = etree.Element('channel')
|
||||||
|
@@ -5,91 +5,52 @@ import re
|
|||||||
|
|
||||||
from .turner import TurnerBaseIE
|
from .turner import TurnerBaseIE
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
strip_or_none,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AdultSwimIE(TurnerBaseIE):
|
class AdultSwimIE(TurnerBaseIE):
|
||||||
_VALID_URL = r'https?://(?:www\.)?adultswim\.com/videos/(?P<is_playlist>playlists/)?(?P<show_path>[^/]+)/(?P<episode_path>[^/?#]+)/?'
|
_VALID_URL = r'https?://(?:www\.)?adultswim\.com/videos/(?P<show_path>[^/?#]+)(?:/(?P<episode_path>[^/?#]+))?'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://adultswim.com/videos/rick-and-morty/pilot',
|
'url': 'http://adultswim.com/videos/rick-and-morty/pilot',
|
||||||
'playlist': [
|
|
||||||
{
|
|
||||||
'md5': '247572debc75c7652f253c8daa51a14d',
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'rQxZvXQ4ROaSOqq-or2Mow-0',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'Rick and Morty - Pilot Part 1',
|
|
||||||
'description': "Rick moves in with his daughter's family and establishes himself as a bad influence on his grandson, Morty. "
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'md5': '77b0e037a4b20ec6b98671c4c379f48d',
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'rQxZvXQ4ROaSOqq-or2Mow-3',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'Rick and Morty - Pilot Part 4',
|
|
||||||
'description': "Rick moves in with his daughter's family and establishes himself as a bad influence on his grandson, Morty. "
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'rQxZvXQ4ROaSOqq-or2Mow',
|
'id': 'rQxZvXQ4ROaSOqq-or2Mow',
|
||||||
|
'ext': 'mp4',
|
||||||
'title': 'Rick and Morty - Pilot',
|
'title': 'Rick and Morty - Pilot',
|
||||||
'description': "Rick moves in with his daughter's family and establishes himself as a bad influence on his grandson, Morty. "
|
'description': 'Rick moves in with his daughter\'s family and establishes himself as a bad influence on his grandson, Morty.',
|
||||||
},
|
'timestamp': 1493267400,
|
||||||
'skip': 'This video is only available for registered users',
|
'upload_date': '20170427',
|
||||||
}, {
|
|
||||||
'url': 'http://www.adultswim.com/videos/playlists/american-parenting/putting-francine-out-of-business/',
|
|
||||||
'playlist': [
|
|
||||||
{
|
|
||||||
'md5': '2eb5c06d0f9a1539da3718d897f13ec5',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '-t8CamQlQ2aYZ49ItZCFog-0',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'American Dad - Putting Francine Out of Business',
|
|
||||||
'description': 'Stan hatches a plan to get Francine out of the real estate business.Watch more American Dad on [adult swim].'
|
|
||||||
},
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'info_dict': {
|
|
||||||
'id': '-t8CamQlQ2aYZ49ItZCFog',
|
|
||||||
'title': 'American Dad - Putting Francine Out of Business',
|
|
||||||
'description': 'Stan hatches a plan to get Francine out of the real estate business.Watch more American Dad on [adult swim].'
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
'url': 'http://www.adultswim.com/videos/tim-and-eric-awesome-show-great-job/dr-steve-brule-for-your-wine/',
|
|
||||||
'playlist': [
|
|
||||||
{
|
|
||||||
'md5': '3e346a2ab0087d687a05e1e7f3b3e529',
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'sY3cMUR_TbuE4YmdjzbIcQ-0',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Tim and Eric Awesome Show Great Job! - Dr. Steve Brule, For Your Wine',
|
|
||||||
'description': 'Dr. Brule reports live from Wine Country with a special report on wines. \r\nWatch Tim and Eric Awesome Show Great Job! episode #20, "Embarrassed" on Adult Swim.\r\n\r\n',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'sY3cMUR_TbuE4YmdjzbIcQ',
|
|
||||||
'title': 'Tim and Eric Awesome Show Great Job! - Dr. Steve Brule, For Your Wine',
|
|
||||||
'description': 'Dr. Brule reports live from Wine Country with a special report on wines. \r\nWatch Tim and Eric Awesome Show Great Job! episode #20, "Embarrassed" on Adult Swim.\r\n\r\n',
|
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
# m3u8 download
|
# m3u8 download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
}
|
},
|
||||||
|
'expected_warnings': ['Unable to download f4m manifest'],
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.adultswim.com/videos/tim-and-eric-awesome-show-great-job/dr-steve-brule-for-your-wine/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'sY3cMUR_TbuE4YmdjzbIcQ',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Tim and Eric Awesome Show Great Job! - Dr. Steve Brule, For Your Wine',
|
||||||
|
'description': 'Dr. Brule reports live from Wine Country with a special report on wines. \nWatch Tim and Eric Awesome Show Great Job! episode #20, "Embarrassed" on Adult Swim.',
|
||||||
|
'upload_date': '20080124',
|
||||||
|
'timestamp': 1201150800,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
# heroMetadata.trailer
|
|
||||||
'url': 'http://www.adultswim.com/videos/decker/inside-decker-a-new-hero/',
|
'url': 'http://www.adultswim.com/videos/decker/inside-decker-a-new-hero/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'I0LQFQkaSUaFp8PnAWHhoQ',
|
'id': 'I0LQFQkaSUaFp8PnAWHhoQ',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Decker - Inside Decker: A New Hero',
|
'title': 'Decker - Inside Decker: A New Hero',
|
||||||
'description': 'md5:c916df071d425d62d70c86d4399d3ee0',
|
'description': 'The guys recap the conclusion of the season. They announce a new hero, take a peek into the Victorville Film Archive and welcome back the talented James Dean.',
|
||||||
'duration': 249.008,
|
'timestamp': 1469480460,
|
||||||
|
'upload_date': '20160725',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
# m3u8 download
|
# m3u8 download
|
||||||
@@ -97,136 +58,102 @@ class AdultSwimIE(TurnerBaseIE):
|
|||||||
},
|
},
|
||||||
'expected_warnings': ['Unable to download f4m manifest'],
|
'expected_warnings': ['Unable to download f4m manifest'],
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.adultswim.com/videos/toonami/friday-october-14th-2016/',
|
'url': 'http://www.adultswim.com/videos/attack-on-titan',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'eYiLsKVgQ6qTC6agD67Sig',
|
'id': 'b7A69dzfRzuaXIECdxW8XQ',
|
||||||
'title': 'Toonami - Friday, October 14th, 2016',
|
'title': 'Attack on Titan',
|
||||||
'description': 'md5:99892c96ffc85e159a428de85c30acde',
|
'description': 'md5:6c8e003ea0777b47013e894767f5e114',
|
||||||
|
},
|
||||||
|
'playlist_mincount': 12,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.adultswim.com/videos/streams/williams-stream',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'd8DEBj7QRfetLsRgFnGEyg',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': r're:^Williams Stream \d{4}-\d{2}-\d{2} \d{2}:\d{2}$',
|
||||||
|
'description': 'original programming',
|
||||||
},
|
},
|
||||||
'playlist': [{
|
|
||||||
'md5': '',
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'eYiLsKVgQ6qTC6agD67Sig',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Toonami - Friday, October 14th, 2016',
|
|
||||||
'description': 'md5:99892c96ffc85e159a428de85c30acde',
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
'params': {
|
'params': {
|
||||||
# m3u8 download
|
# m3u8 download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
'expected_warnings': ['Unable to download f4m manifest'],
|
|
||||||
}]
|
}]
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def find_video_info(collection, slug):
|
|
||||||
for video in collection.get('videos'):
|
|
||||||
if video.get('slug') == slug:
|
|
||||||
return video
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def find_collection_by_linkURL(collections, linkURL):
|
|
||||||
for collection in collections:
|
|
||||||
if collection.get('linkURL') == linkURL:
|
|
||||||
return collection
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def find_collection_containing_video(collections, slug):
|
|
||||||
for collection in collections:
|
|
||||||
for video in collection.get('videos'):
|
|
||||||
if video.get('slug') == slug:
|
|
||||||
return collection, video
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
show_path, episode_path = re.match(self._VALID_URL, url).groups()
|
||||||
show_path = mobj.group('show_path')
|
display_id = episode_path or show_path
|
||||||
episode_path = mobj.group('episode_path')
|
webpage = self._download_webpage(url, display_id)
|
||||||
is_playlist = True if mobj.group('is_playlist') else False
|
initial_data = self._parse_json(self._search_regex(
|
||||||
|
r'AS_INITIAL_DATA(?:__)?\s*=\s*({.+?});',
|
||||||
|
webpage, 'initial data'), display_id)
|
||||||
|
|
||||||
webpage = self._download_webpage(url, episode_path)
|
is_stream = show_path == 'streams'
|
||||||
|
if is_stream:
|
||||||
|
if not episode_path:
|
||||||
|
episode_path = 'live-stream'
|
||||||
|
|
||||||
# Extract the value of `bootstrappedData` from the Javascript in the page.
|
video_data = next(stream for stream_path, stream in initial_data['streams'].items() if stream_path == episode_path)
|
||||||
bootstrapped_data = self._parse_json(self._search_regex(
|
video_id = video_data.get('stream')
|
||||||
r'var bootstrappedData = ({.*});', webpage, 'bootstraped data'), episode_path)
|
|
||||||
|
|
||||||
# Downloading videos from a /videos/playlist/ URL needs to be handled differently.
|
if not video_id:
|
||||||
# NOTE: We are only downloading one video (the current one) not the playlist
|
entries = []
|
||||||
if is_playlist:
|
for episode in video_data.get('archiveEpisodes', []):
|
||||||
collections = bootstrapped_data['playlists']['collections']
|
episode_url = episode.get('url')
|
||||||
collection = self.find_collection_by_linkURL(collections, show_path)
|
if not episode_url:
|
||||||
video_info = self.find_video_info(collection, episode_path)
|
continue
|
||||||
|
entries.append(self.url_result(
|
||||||
show_title = video_info['showTitle']
|
episode_url, 'AdultSwim', episode.get('id')))
|
||||||
segment_ids = [video_info['videoPlaybackID']]
|
return self.playlist_result(
|
||||||
|
entries, video_data.get('id'), video_data.get('title'),
|
||||||
|
strip_or_none(video_data.get('description')))
|
||||||
else:
|
else:
|
||||||
collections = bootstrapped_data['show']['collections']
|
show_data = initial_data['show']
|
||||||
collection, video_info = self.find_collection_containing_video(collections, episode_path)
|
|
||||||
# Video wasn't found in the collections, let's try `slugged_video`.
|
|
||||||
if video_info is None:
|
|
||||||
if bootstrapped_data.get('slugged_video', {}).get('slug') == episode_path:
|
|
||||||
video_info = bootstrapped_data['slugged_video']
|
|
||||||
if not video_info:
|
|
||||||
video_info = bootstrapped_data.get(
|
|
||||||
'heroMetadata', {}).get('trailer', {}).get('video')
|
|
||||||
if not video_info:
|
|
||||||
video_info = bootstrapped_data.get('onlineOriginals', [None])[0]
|
|
||||||
if not video_info:
|
|
||||||
raise ExtractorError('Unable to find video info')
|
|
||||||
|
|
||||||
show = bootstrapped_data['show']
|
if not episode_path:
|
||||||
show_title = show['title']
|
entries = []
|
||||||
stream = video_info.get('stream')
|
for video in show_data.get('videos', []):
|
||||||
if stream and stream.get('videoPlaybackID'):
|
slug = video.get('slug')
|
||||||
segment_ids = [stream['videoPlaybackID']]
|
if not slug:
|
||||||
elif video_info.get('clips'):
|
continue
|
||||||
segment_ids = [clip['videoPlaybackID'] for clip in video_info['clips']]
|
entries.append(self.url_result(
|
||||||
elif video_info.get('videoPlaybackID'):
|
'http://adultswim.com/videos/%s/%s' % (show_path, slug),
|
||||||
segment_ids = [video_info['videoPlaybackID']]
|
'AdultSwim', video.get('id')))
|
||||||
elif video_info.get('id'):
|
return self.playlist_result(
|
||||||
segment_ids = [video_info['id']]
|
entries, show_data.get('id'), show_data.get('title'),
|
||||||
else:
|
strip_or_none(show_data.get('metadata', {}).get('description')))
|
||||||
if video_info.get('auth') is True:
|
|
||||||
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)
|
|
||||||
else:
|
|
||||||
raise ExtractorError('Unable to find stream or clips')
|
|
||||||
|
|
||||||
episode_id = video_info['id']
|
video_data = show_data['sluggedVideo']
|
||||||
episode_title = video_info['title']
|
video_id = video_data['id']
|
||||||
episode_description = video_info.get('description')
|
|
||||||
episode_duration = int_or_none(video_info.get('duration'))
|
|
||||||
view_count = int_or_none(video_info.get('views'))
|
|
||||||
|
|
||||||
entries = []
|
info = self._extract_cvp_info(
|
||||||
for part_num, segment_id in enumerate(segment_ids):
|
'http://www.adultswim.com/videos/api/v0/assets?platform=desktop&id=' + video_id,
|
||||||
segement_info = self._extract_cvp_info(
|
video_id, {
|
||||||
'http://www.adultswim.com/videos/api/v0/assets?id=%s&platform=desktop' % segment_id,
|
'secure': {
|
||||||
segment_id, {
|
'media_src': 'http://androidhls-secure.cdn.turner.com/adultswim/big',
|
||||||
'secure': {
|
'tokenizer_src': 'http://www.adultswim.com/astv/mvpd/processors/services/token_ipadAdobe.do',
|
||||||
'media_src': 'http://androidhls-secure.cdn.turner.com/adultswim/big',
|
},
|
||||||
'tokenizer_src': 'http://www.adultswim.com/astv/mvpd/processors/services/token_ipadAdobe.do',
|
}, {
|
||||||
},
|
'url': url,
|
||||||
})
|
'site_name': 'AdultSwim',
|
||||||
segment_title = '%s - %s' % (show_title, episode_title)
|
'auth_required': video_data.get('auth'),
|
||||||
if len(segment_ids) > 1:
|
|
||||||
segment_title += ' Part %d' % (part_num + 1)
|
|
||||||
segement_info.update({
|
|
||||||
'id': segment_id,
|
|
||||||
'title': segment_title,
|
|
||||||
'description': episode_description,
|
|
||||||
})
|
})
|
||||||
entries.append(segement_info)
|
|
||||||
|
|
||||||
return {
|
info.update({
|
||||||
'_type': 'playlist',
|
'id': video_id,
|
||||||
'id': episode_id,
|
'display_id': display_id,
|
||||||
'display_id': episode_path,
|
'description': info.get('description') or strip_or_none(video_data.get('description')),
|
||||||
'entries': entries,
|
})
|
||||||
'title': '%s - %s' % (show_title, episode_title),
|
if not is_stream:
|
||||||
'description': episode_description,
|
info.update({
|
||||||
'duration': episode_duration,
|
'duration': info.get('duration') or int_or_none(video_data.get('duration')),
|
||||||
'view_count': view_count,
|
'timestamp': info.get('timestamp') or int_or_none(video_data.get('launch_date')),
|
||||||
}
|
'season_number': info.get('season_number') or int_or_none(video_data.get('season_number')),
|
||||||
|
'episode': info['title'],
|
||||||
|
'episode_number': info.get('episode_number') or int_or_none(video_data.get('episode_number')),
|
||||||
|
})
|
||||||
|
|
||||||
|
info['series'] = video_data.get('collection_title') or info.get('series')
|
||||||
|
if info['series'] and info['series'] != info['title']:
|
||||||
|
info['title'] = '%s - %s' % (info['series'], info['title'])
|
||||||
|
|
||||||
|
return info
|
||||||
|
@@ -101,10 +101,14 @@ class AENetworksIE(AENetworksBaseIE):
|
|||||||
for season_url_path in re.findall(r'(?s)<li[^>]+data-href="(/shows/%s/season-\d+)"' % url_parts[0], webpage):
|
for season_url_path in re.findall(r'(?s)<li[^>]+data-href="(/shows/%s/season-\d+)"' % url_parts[0], webpage):
|
||||||
entries.append(self.url_result(
|
entries.append(self.url_result(
|
||||||
compat_urlparse.urljoin(url, season_url_path), 'AENetworks'))
|
compat_urlparse.urljoin(url, season_url_path), 'AENetworks'))
|
||||||
return self.playlist_result(
|
if entries:
|
||||||
entries, self._html_search_meta('aetn:SeriesId', webpage),
|
return self.playlist_result(
|
||||||
self._html_search_meta('aetn:SeriesTitle', webpage))
|
entries, self._html_search_meta('aetn:SeriesId', webpage),
|
||||||
elif url_parts_len == 2:
|
self._html_search_meta('aetn:SeriesTitle', webpage))
|
||||||
|
else:
|
||||||
|
# single season
|
||||||
|
url_parts_len = 2
|
||||||
|
if url_parts_len == 2:
|
||||||
entries = []
|
entries = []
|
||||||
for episode_item in re.findall(r'(?s)<[^>]+class="[^"]*(?:episode|program)-item[^"]*"[^>]*>', webpage):
|
for episode_item in re.findall(r'(?s)<[^>]+class="[^"]*(?:episode|program)-item[^"]*"[^>]*>', webpage):
|
||||||
episode_attributes = extract_attributes(episode_item)
|
episode_attributes = extract_attributes(episode_item)
|
||||||
@@ -112,7 +116,7 @@ class AENetworksIE(AENetworksBaseIE):
|
|||||||
url, episode_attributes['data-canonical'])
|
url, episode_attributes['data-canonical'])
|
||||||
entries.append(self.url_result(
|
entries.append(self.url_result(
|
||||||
episode_url, 'AENetworks',
|
episode_url, 'AENetworks',
|
||||||
episode_attributes['data-videoid']))
|
episode_attributes.get('data-videoid') or episode_attributes.get('data-video-id')))
|
||||||
return self.playlist_result(
|
return self.playlist_result(
|
||||||
entries, self._html_search_meta('aetn:SeasonId', webpage))
|
entries, self._html_search_meta('aetn:SeasonId', webpage))
|
||||||
|
|
||||||
|
@@ -207,11 +207,10 @@ class AfreecaTVIE(InfoExtractor):
|
|||||||
file_url, video_id, 'mp4', entry_protocol='m3u8_native',
|
file_url, video_id, 'mp4', entry_protocol='m3u8_native',
|
||||||
m3u8_id='hls',
|
m3u8_id='hls',
|
||||||
note='Downloading part %d m3u8 information' % file_num)
|
note='Downloading part %d m3u8 information' % file_num)
|
||||||
title = title if one else '%s (part %d)' % (title, file_num)
|
|
||||||
file_info = common_entry.copy()
|
file_info = common_entry.copy()
|
||||||
file_info.update({
|
file_info.update({
|
||||||
'id': format_id,
|
'id': format_id,
|
||||||
'title': title,
|
'title': title if one else '%s (part %d)' % (title, file_num),
|
||||||
'upload_date': upload_date,
|
'upload_date': upload_date,
|
||||||
'duration': file_duration,
|
'duration': file_duration,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
|
@@ -4,9 +4,9 @@ from .common import InfoExtractor
|
|||||||
|
|
||||||
|
|
||||||
class AlJazeeraIE(InfoExtractor):
|
class AlJazeeraIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?aljazeera\.com/programmes/.*?/(?P<id>[^/]+)\.html'
|
_VALID_URL = r'https?://(?:www\.)?aljazeera\.com/(?:programmes|video)/.*?/(?P<id>[^/]+)\.html'
|
||||||
|
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
'url': 'http://www.aljazeera.com/programmes/the-slum/2014/08/deliverance-201482883754237240.html',
|
'url': 'http://www.aljazeera.com/programmes/the-slum/2014/08/deliverance-201482883754237240.html',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '3792260579001',
|
'id': '3792260579001',
|
||||||
@@ -19,7 +19,10 @@ class AlJazeeraIE(InfoExtractor):
|
|||||||
},
|
},
|
||||||
'add_ie': ['BrightcoveNew'],
|
'add_ie': ['BrightcoveNew'],
|
||||||
'skip': 'Not accessible from Travis CI server',
|
'skip': 'Not accessible from Travis CI server',
|
||||||
}
|
}, {
|
||||||
|
'url': 'http://www.aljazeera.com/video/news/2017/05/sierra-leone-709-carat-diamond-auctioned-170511100111930.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/665003303001/default_default/index.html?videoId=%s'
|
BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/665003303001/default_default/index.html?videoId=%s'
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
@@ -34,9 +34,12 @@ class AMPIE(InfoExtractor):
|
|||||||
if isinstance(media_thumbnail, dict):
|
if isinstance(media_thumbnail, dict):
|
||||||
media_thumbnail = [media_thumbnail]
|
media_thumbnail = [media_thumbnail]
|
||||||
for thumbnail_data in media_thumbnail:
|
for thumbnail_data in media_thumbnail:
|
||||||
thumbnail = thumbnail_data['@attributes']
|
thumbnail = thumbnail_data.get('@attributes', {})
|
||||||
|
thumbnail_url = thumbnail.get('url')
|
||||||
|
if not thumbnail_url:
|
||||||
|
continue
|
||||||
thumbnails.append({
|
thumbnails.append({
|
||||||
'url': self._proto_relative_url(thumbnail['url'], 'http:'),
|
'url': self._proto_relative_url(thumbnail_url, 'http:'),
|
||||||
'width': int_or_none(thumbnail.get('width')),
|
'width': int_or_none(thumbnail.get('width')),
|
||||||
'height': int_or_none(thumbnail.get('height')),
|
'height': int_or_none(thumbnail.get('height')),
|
||||||
})
|
})
|
||||||
@@ -47,9 +50,14 @@ class AMPIE(InfoExtractor):
|
|||||||
if isinstance(media_subtitle, dict):
|
if isinstance(media_subtitle, dict):
|
||||||
media_subtitle = [media_subtitle]
|
media_subtitle = [media_subtitle]
|
||||||
for subtitle_data in media_subtitle:
|
for subtitle_data in media_subtitle:
|
||||||
subtitle = subtitle_data['@attributes']
|
subtitle = subtitle_data.get('@attributes', {})
|
||||||
lang = subtitle.get('lang') or 'en'
|
subtitle_href = subtitle.get('href')
|
||||||
subtitles[lang] = [{'url': subtitle['href']}]
|
if not subtitle_href:
|
||||||
|
continue
|
||||||
|
subtitles.setdefault(subtitle.get('lang') or 'en', []).append({
|
||||||
|
'url': subtitle_href,
|
||||||
|
'ext': mimetype2ext(subtitle.get('type')) or determine_ext(subtitle_href),
|
||||||
|
})
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
media_content = get_media_node('content')
|
media_content = get_media_node('content')
|
||||||
|
@@ -5,6 +5,7 @@ import base64
|
|||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
@@ -16,6 +17,7 @@ from ..utils import (
|
|||||||
intlist_to_bytes,
|
intlist_to_bytes,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
strip_jsonp,
|
strip_jsonp,
|
||||||
|
unescapeHTML,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -26,6 +28,8 @@ def md5_text(s):
|
|||||||
|
|
||||||
|
|
||||||
class AnvatoIE(InfoExtractor):
|
class AnvatoIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'anvato:(?P<access_key_or_mcp>[^:]+):(?P<id>\d+)'
|
||||||
|
|
||||||
# Copied from anvplayer.min.js
|
# Copied from anvplayer.min.js
|
||||||
_ANVACK_TABLE = {
|
_ANVACK_TABLE = {
|
||||||
'nbcu_nbcd_desktop_web_prod_93d8ead38ce2024f8f544b78306fbd15895ae5e6': 'NNemUkySjxLyPTKvZRiGntBIjEyK8uqicjMakIaQ',
|
'nbcu_nbcd_desktop_web_prod_93d8ead38ce2024f8f544b78306fbd15895ae5e6': 'NNemUkySjxLyPTKvZRiGntBIjEyK8uqicjMakIaQ',
|
||||||
@@ -114,6 +118,22 @@ class AnvatoIE(InfoExtractor):
|
|||||||
'nbcu_nbcd_desktop_web_prod_93d8ead38ce2024f8f544b78306fbd15895ae5e6_secure': 'NNemUkySjxLyPTKvZRiGntBIjEyK8uqicjMakIaQ'
|
'nbcu_nbcd_desktop_web_prod_93d8ead38ce2024f8f544b78306fbd15895ae5e6_secure': 'NNemUkySjxLyPTKvZRiGntBIjEyK8uqicjMakIaQ'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_MCP_TO_ACCESS_KEY_TABLE = {
|
||||||
|
'qa': 'anvato_mcpqa_demo_web_stage_18b55e00db5a13faa8d03ae6e41f6f5bcb15b922',
|
||||||
|
'lin': 'anvato_mcp_lin_web_prod_4c36fbfd4d8d8ecae6488656e21ac6d1ac972749',
|
||||||
|
'univison': 'anvato_mcp_univision_web_prod_37fe34850c99a3b5cdb71dab10a417dd5cdecafa',
|
||||||
|
'uni': 'anvato_mcp_univision_web_prod_37fe34850c99a3b5cdb71dab10a417dd5cdecafa',
|
||||||
|
'dev': 'anvato_mcp_fs2go_web_prod_c7b90a93e171469cdca00a931211a2f556370d0a',
|
||||||
|
'sps': 'anvato_mcp_sps_web_prod_54bdc90dd6ba21710e9f7074338365bba28da336',
|
||||||
|
'spsstg': 'anvato_mcp_sps_web_prod_54bdc90dd6ba21710e9f7074338365bba28da336',
|
||||||
|
'anv': 'anvato_mcp_anv_web_prod_791407490f4c1ef2a4bcb21103e0cb1bcb3352b3',
|
||||||
|
'gray': 'anvato_mcp_gray_web_prod_4c10f067c393ed8fc453d3930f8ab2b159973900',
|
||||||
|
'hearst': 'anvato_mcp_hearst_web_prod_5356c3de0fc7c90a3727b4863ca7fec3a4524a99',
|
||||||
|
'cbs': 'anvato_mcp_cbs_web_prod_02f26581ff80e5bda7aad28226a8d369037f2cbe',
|
||||||
|
'telemundo': 'anvato_mcp_telemundo_web_prod_c5278d51ad46fda4b6ca3d0ea44a7846a054f582'
|
||||||
|
}
|
||||||
|
|
||||||
|
_ANVP_RE = r'<script[^>]+\bdata-anvp\s*=\s*(["\'])(?P<anvp>(?:(?!\1).)+)\1'
|
||||||
_AUTH_KEY = b'\x31\xc2\x42\x84\x9e\x73\xa0\xce'
|
_AUTH_KEY = b'\x31\xc2\x42\x84\x9e\x73\xa0\xce'
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@@ -178,12 +198,7 @@ class AnvatoIE(InfoExtractor):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ext == 'm3u8' or media_format in ('m3u8', 'm3u8-variant'):
|
if ext == 'm3u8' or media_format in ('m3u8', 'm3u8-variant'):
|
||||||
# Not using _extract_m3u8_formats here as individual media
|
if tbr is not None:
|
||||||
# playlists are also included in published_urls.
|
|
||||||
if tbr is None:
|
|
||||||
formats.append(self._m3u8_meta_format(video_url, ext='mp4', m3u8_id='hls'))
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
a_format.update({
|
a_format.update({
|
||||||
'format_id': '-'.join(filter(None, ['hls', compat_str(tbr)])),
|
'format_id': '-'.join(filter(None, ['hls', compat_str(tbr)])),
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
@@ -222,9 +237,42 @@ class AnvatoIE(InfoExtractor):
|
|||||||
'subtitles': subtitles,
|
'subtitles': subtitles,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_urls(ie, webpage, video_id):
|
||||||
|
entries = []
|
||||||
|
for mobj in re.finditer(AnvatoIE._ANVP_RE, webpage):
|
||||||
|
anvplayer_data = ie._parse_json(
|
||||||
|
mobj.group('anvp'), video_id, transform_source=unescapeHTML,
|
||||||
|
fatal=False)
|
||||||
|
if not anvplayer_data:
|
||||||
|
continue
|
||||||
|
video = anvplayer_data.get('video')
|
||||||
|
if not isinstance(video, compat_str) or not video.isdigit():
|
||||||
|
continue
|
||||||
|
access_key = anvplayer_data.get('accessKey')
|
||||||
|
if not access_key:
|
||||||
|
mcp = anvplayer_data.get('mcp')
|
||||||
|
if mcp:
|
||||||
|
access_key = AnvatoIE._MCP_TO_ACCESS_KEY_TABLE.get(
|
||||||
|
mcp.lower())
|
||||||
|
if not access_key:
|
||||||
|
continue
|
||||||
|
entries.append(ie.url_result(
|
||||||
|
'anvato:%s:%s' % (access_key, video), ie=AnvatoIE.ie_key(),
|
||||||
|
video_id=video))
|
||||||
|
return entries
|
||||||
|
|
||||||
def _extract_anvato_videos(self, webpage, video_id):
|
def _extract_anvato_videos(self, webpage, video_id):
|
||||||
anvplayer_data = self._parse_json(self._html_search_regex(
|
anvplayer_data = self._parse_json(
|
||||||
r'<script[^>]+data-anvp=\'([^\']+)\'', webpage,
|
self._html_search_regex(
|
||||||
'Anvato player data'), video_id)
|
self._ANVP_RE, webpage, 'Anvato player data', group='anvp'),
|
||||||
|
video_id)
|
||||||
return self._get_anvato_videos(
|
return self._get_anvato_videos(
|
||||||
anvplayer_data['accessKey'], anvplayer_data['video'])
|
anvplayer_data['accessKey'], anvplayer_data['video'])
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
mobj = re.match(self._VALID_URL, url)
|
||||||
|
access_key, video_id = mobj.group('access_key_or_mcp', 'id')
|
||||||
|
if access_key not in self._ANVACK_TABLE:
|
||||||
|
access_key = self._MCP_TO_ACCESS_KEY_TABLE[access_key]
|
||||||
|
return self._get_anvato_videos(access_key, video_id)
|
||||||
|
@@ -12,13 +12,13 @@ class AppleConnectIE(InfoExtractor):
|
|||||||
_VALID_URL = r'https?://itunes\.apple\.com/\w{0,2}/?post/idsa\.(?P<id>[\w-]+)'
|
_VALID_URL = r'https?://itunes\.apple\.com/\w{0,2}/?post/idsa\.(?P<id>[\w-]+)'
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'https://itunes.apple.com/us/post/idsa.4ab17a39-2720-11e5-96c5-a5b38f6c42d3',
|
'url': 'https://itunes.apple.com/us/post/idsa.4ab17a39-2720-11e5-96c5-a5b38f6c42d3',
|
||||||
'md5': '10d0f2799111df4cb1c924520ca78f98',
|
'md5': 'e7c38568a01ea45402570e6029206723',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '4ab17a39-2720-11e5-96c5-a5b38f6c42d3',
|
'id': '4ab17a39-2720-11e5-96c5-a5b38f6c42d3',
|
||||||
'ext': 'm4v',
|
'ext': 'm4v',
|
||||||
'title': 'Energy',
|
'title': 'Energy',
|
||||||
'uploader': 'Drake',
|
'uploader': 'Drake',
|
||||||
'thumbnail': 'http://is5.mzstatic.com/image/thumb/Video5/v4/78/61/c5/7861c5fa-ad6d-294b-1464-cf7605b911d6/source/1920x1080sr.jpg',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'upload_date': '20150710',
|
'upload_date': '20150710',
|
||||||
'timestamp': 1436545535,
|
'timestamp': 1436545535,
|
||||||
},
|
},
|
||||||
|
@@ -70,7 +70,8 @@ class AppleTrailersIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://trailers.apple.com/trailers/magnolia/blackthorn/',
|
'url': 'http://trailers.apple.com/trailers/magnolia/blackthorn/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'blackthorn',
|
'id': '4489',
|
||||||
|
'title': 'Blackthorn',
|
||||||
},
|
},
|
||||||
'playlist_mincount': 2,
|
'playlist_mincount': 2,
|
||||||
'expected_warnings': ['Unable to download JSON metadata'],
|
'expected_warnings': ['Unable to download JSON metadata'],
|
||||||
@@ -261,7 +262,7 @@ class AppleTrailersSectionIE(InfoExtractor):
|
|||||||
'title': 'Most Popular',
|
'title': 'Most Popular',
|
||||||
'id': 'mostpopular',
|
'id': 'mostpopular',
|
||||||
},
|
},
|
||||||
'playlist_mincount': 80,
|
'playlist_mincount': 30,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://trailers.apple.com/#section=moviestudios',
|
'url': 'http://trailers.apple.com/#section=moviestudios',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
|
@@ -24,12 +24,12 @@ class ArchiveOrgIE(InfoExtractor):
|
|||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://archive.org/details/Cops1922',
|
'url': 'https://archive.org/details/Cops1922',
|
||||||
'md5': 'bc73c8ab3838b5a8fc6c6651fa7b58ba',
|
'md5': '0869000b4ce265e8ca62738b336b268a',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'Cops1922',
|
'id': 'Cops1922',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Buster Keaton\'s "Cops" (1922)',
|
'title': 'Buster Keaton\'s "Cops" (1922)',
|
||||||
'description': 'md5:b4544662605877edd99df22f9620d858',
|
'description': 'md5:89e7c77bf5d965dd5c0372cfb49470f6',
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://archive.org/embed/XD300-23_68HighlightsAResearchCntAugHumanIntellect',
|
'url': 'http://archive.org/embed/XD300-23_68HighlightsAResearchCntAugHumanIntellect',
|
||||||
|
@@ -180,7 +180,7 @@ class ArteTVBaseIE(InfoExtractor):
|
|||||||
|
|
||||||
class ArteTVPlus7IE(ArteTVBaseIE):
|
class ArteTVPlus7IE(ArteTVBaseIE):
|
||||||
IE_NAME = 'arte.tv:+7'
|
IE_NAME = 'arte.tv:+7'
|
||||||
_VALID_URL = r'https?://(?:(?:www|sites)\.)?arte\.tv/[^/]+/(?P<lang>fr|de|en|es)/(?:[^/]+/)*(?P<id>[^/?#&]+)'
|
_VALID_URL = r'https?://(?:(?:www|sites)\.)?arte\.tv/(?:[^/]+/)?(?P<lang>fr|de|en|es)/(?:videos/)?(?:[^/]+/)*(?P<id>[^/?#&]+)'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.arte.tv/guide/de/sendungen/XEN/xenius/?vid=055918-015_PLUS7-D',
|
'url': 'http://www.arte.tv/guide/de/sendungen/XEN/xenius/?vid=055918-015_PLUS7-D',
|
||||||
@@ -188,6 +188,9 @@ class ArteTVPlus7IE(ArteTVBaseIE):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://sites.arte.tv/karambolage/de/video/karambolage-22',
|
'url': 'http://sites.arte.tv/karambolage/de/video/karambolage-22',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.arte.tv/de/videos/048696-000-A/der-kluge-bauch-unser-zweites-gehirn',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
93
youtube_dl/extractor/asiancrush.py
Normal file
93
youtube_dl/extractor/asiancrush.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from .kaltura import KalturaIE
|
||||||
|
from ..utils import (
|
||||||
|
extract_attributes,
|
||||||
|
remove_end,
|
||||||
|
urlencode_postdata,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AsianCrushIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?asiancrush\.com/video/(?:[^/]+/)?0+(?P<id>\d+)v\b'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://www.asiancrush.com/video/012869v/women-who-flirt/',
|
||||||
|
'md5': 'c3b740e48d0ba002a42c0b72857beae6',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1_y4tmjm5r',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Women Who Flirt',
|
||||||
|
'description': 'md5:3db14e9186197857e7063522cb89a805',
|
||||||
|
'timestamp': 1496936429,
|
||||||
|
'upload_date': '20170608',
|
||||||
|
'uploader_id': 'craig@crifkin.com',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.asiancrush.com/video/she-was-pretty/011886v-pretty-episode-3/',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
|
data = self._download_json(
|
||||||
|
'https://www.asiancrush.com/wp-admin/admin-ajax.php', video_id,
|
||||||
|
data=urlencode_postdata({
|
||||||
|
'postid': video_id,
|
||||||
|
'action': 'get_channel_kaltura_vars',
|
||||||
|
}))
|
||||||
|
|
||||||
|
entry_id = data['entry_id']
|
||||||
|
|
||||||
|
return self.url_result(
|
||||||
|
'kaltura:%s:%s' % (data['partner_id'], entry_id),
|
||||||
|
ie=KalturaIE.ie_key(), video_id=entry_id,
|
||||||
|
video_title=data.get('vid_label'))
|
||||||
|
|
||||||
|
|
||||||
|
class AsianCrushPlaylistIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?asiancrush\.com/series/0+(?P<id>\d+)s\b'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'https://www.asiancrush.com/series/012481s/scholar-walks-night/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '12481',
|
||||||
|
'title': 'Scholar Who Walks the Night',
|
||||||
|
'description': 'md5:7addd7c5132a09fd4741152d96cce886',
|
||||||
|
},
|
||||||
|
'playlist_count': 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
playlist_id = self._match_id(url)
|
||||||
|
|
||||||
|
webpage = self._download_webpage(url, playlist_id)
|
||||||
|
|
||||||
|
entries = []
|
||||||
|
|
||||||
|
for mobj in re.finditer(
|
||||||
|
r'<a[^>]+href=(["\'])(?P<url>%s.*?)\1[^>]*>' % AsianCrushIE._VALID_URL,
|
||||||
|
webpage):
|
||||||
|
attrs = extract_attributes(mobj.group(0))
|
||||||
|
if attrs.get('class') == 'clearfix':
|
||||||
|
entries.append(self.url_result(
|
||||||
|
mobj.group('url'), ie=AsianCrushIE.ie_key()))
|
||||||
|
|
||||||
|
title = remove_end(
|
||||||
|
self._html_search_regex(
|
||||||
|
r'(?s)<h1\b[^>]\bid=["\']movieTitle[^>]+>(.+?)</h1>', webpage,
|
||||||
|
'title', default=None) or self._og_search_title(
|
||||||
|
webpage, default=None) or self._html_search_meta(
|
||||||
|
'twitter:title', webpage, 'title',
|
||||||
|
default=None) or self._search_regex(
|
||||||
|
r'<title>([^<]+)</title>', webpage, 'title', fatal=False),
|
||||||
|
' | AsianCrush')
|
||||||
|
|
||||||
|
description = self._og_search_description(
|
||||||
|
webpage, default=None) or self._html_search_meta(
|
||||||
|
'twitter:description', webpage, 'description', fatal=False)
|
||||||
|
|
||||||
|
return self.playlist_result(entries, playlist_id, title, description)
|
@@ -36,7 +36,7 @@ class AtresPlayerIE(InfoExtractor):
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': 'http://www.atresplayer.com/television/especial/videoencuentros/temporada-1/capitulo-112-david-bustamante_2014121600375.html',
|
'url': 'http://www.atresplayer.com/television/especial/videoencuentros/temporada-1/capitulo-112-david-bustamante_2014121600375.html',
|
||||||
'md5': '0d0e918533bbd4b263f2de4d197d4aac',
|
'md5': '6e52cbb513c405e403dbacb7aacf8747',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'capitulo-112-david-bustamante',
|
'id': 'capitulo-112-david-bustamante',
|
||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
|
@@ -16,7 +16,7 @@ class AudioBoomIE(InfoExtractor):
|
|||||||
'title': '3/09/2016 Czaban Hour 3',
|
'title': '3/09/2016 Czaban Hour 3',
|
||||||
'description': 'Guest: Nate Davis - NFL free agency, Guest: Stan Gans',
|
'description': 'Guest: Nate Davis - NFL free agency, Guest: Stan Gans',
|
||||||
'duration': 2245.72,
|
'duration': 2245.72,
|
||||||
'uploader': 'Steve Czaban',
|
'uploader': 'SB Nation A.M.',
|
||||||
'uploader_url': r're:https?://(?:www\.)?audioboom\.com/channel/steveczabanyahoosportsradio',
|
'uploader_url': r're:https?://(?:www\.)?audioboom\.com/channel/steveczabanyahoosportsradio',
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
@@ -14,14 +14,16 @@ from ..utils import (
|
|||||||
ExtractorError,
|
ExtractorError,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
KNOWN_EXTENSIONS,
|
||||||
parse_filesize,
|
parse_filesize,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
update_url_query,
|
update_url_query,
|
||||||
|
unified_strdate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BandcampIE(InfoExtractor):
|
class BandcampIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://.*?\.bandcamp\.com/track/(?P<title>.*)'
|
_VALID_URL = r'https?://.*?\.bandcamp\.com/track/(?P<title>[^/?#&]+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://youtube-dl.bandcamp.com/track/youtube-dl-test-song',
|
'url': 'http://youtube-dl.bandcamp.com/track/youtube-dl-test-song',
|
||||||
'md5': 'c557841d5e50261777a6585648adf439',
|
'md5': 'c557841d5e50261777a6585648adf439',
|
||||||
@@ -34,12 +36,12 @@ class BandcampIE(InfoExtractor):
|
|||||||
'_skip': 'There is a limit of 200 free downloads / month for the test song'
|
'_skip': 'There is a limit of 200 free downloads / month for the test song'
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://benprunty.bandcamp.com/track/lanius-battle',
|
'url': 'http://benprunty.bandcamp.com/track/lanius-battle',
|
||||||
'md5': '73d0b3171568232574e45652f8720b5c',
|
'md5': '0369ace6b939f0927e62c67a1a8d9fa7',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '2650410135',
|
'id': '2650410135',
|
||||||
'ext': 'mp3',
|
'ext': 'aiff',
|
||||||
'title': 'Lanius (Battle)',
|
'title': 'Ben Prunty - Lanius (Battle)',
|
||||||
'uploader': 'Ben Prunty Music',
|
'uploader': 'Ben Prunty',
|
||||||
},
|
},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
@@ -47,6 +49,7 @@ class BandcampIE(InfoExtractor):
|
|||||||
mobj = re.match(self._VALID_URL, url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
title = mobj.group('title')
|
title = mobj.group('title')
|
||||||
webpage = self._download_webpage(url, title)
|
webpage = self._download_webpage(url, title)
|
||||||
|
thumbnail = self._html_search_meta('og:image', webpage, default=None)
|
||||||
m_download = re.search(r'freeDownloadPage: "(.*?)"', webpage)
|
m_download = re.search(r'freeDownloadPage: "(.*?)"', webpage)
|
||||||
if not m_download:
|
if not m_download:
|
||||||
m_trackinfo = re.search(r'trackinfo: (.+),\s*?\n', webpage)
|
m_trackinfo = re.search(r'trackinfo: (.+),\s*?\n', webpage)
|
||||||
@@ -75,6 +78,7 @@ class BandcampIE(InfoExtractor):
|
|||||||
return {
|
return {
|
||||||
'id': track_id,
|
'id': track_id,
|
||||||
'title': data['title'],
|
'title': data['title'],
|
||||||
|
'thumbnail': thumbnail,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
'duration': float_or_none(data.get('duration')),
|
'duration': float_or_none(data.get('duration')),
|
||||||
}
|
}
|
||||||
@@ -143,7 +147,7 @@ class BandcampIE(InfoExtractor):
|
|||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': title,
|
'title': title,
|
||||||
'thumbnail': info.get('thumb_url'),
|
'thumbnail': info.get('thumb_url') or thumbnail,
|
||||||
'uploader': info.get('artist'),
|
'uploader': info.get('artist'),
|
||||||
'artist': artist,
|
'artist': artist,
|
||||||
'track': track,
|
'track': track,
|
||||||
@@ -153,7 +157,7 @@ class BandcampIE(InfoExtractor):
|
|||||||
|
|
||||||
class BandcampAlbumIE(InfoExtractor):
|
class BandcampAlbumIE(InfoExtractor):
|
||||||
IE_NAME = 'Bandcamp:album'
|
IE_NAME = 'Bandcamp:album'
|
||||||
_VALID_URL = r'https?://(?:(?P<subdomain>[^.]+)\.)?bandcamp\.com(?:/album/(?P<album_id>[^?#]+)|/?(?:$|[?#]))'
|
_VALID_URL = r'https?://(?:(?P<subdomain>[^.]+)\.)?bandcamp\.com(?:/album/(?P<album_id>[^/?#&]+))?'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://blazo.bandcamp.com/album/jazz-format-mixtape-vol-1',
|
'url': 'http://blazo.bandcamp.com/album/jazz-format-mixtape-vol-1',
|
||||||
@@ -220,6 +224,12 @@ class BandcampAlbumIE(InfoExtractor):
|
|||||||
'playlist_count': 2,
|
'playlist_count': 2,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def suitable(cls, url):
|
||||||
|
return (False
|
||||||
|
if BandcampWeeklyIE.suitable(url) or BandcampIE.suitable(url)
|
||||||
|
else super(BandcampAlbumIE, cls).suitable(url))
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
uploader_id = mobj.group('subdomain')
|
uploader_id = mobj.group('subdomain')
|
||||||
@@ -248,3 +258,92 @@ class BandcampAlbumIE(InfoExtractor):
|
|||||||
'title': title,
|
'title': title,
|
||||||
'entries': entries,
|
'entries': entries,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BandcampWeeklyIE(InfoExtractor):
|
||||||
|
IE_NAME = 'Bandcamp:weekly'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?bandcamp\.com/?\?(?:.*?&)?show=(?P<id>\d+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://bandcamp.com/?show=224',
|
||||||
|
'md5': 'b00df799c733cf7e0c567ed187dea0fd',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '224',
|
||||||
|
'ext': 'opus',
|
||||||
|
'title': 'BC Weekly April 4th 2017 - Magic Moments',
|
||||||
|
'description': 'md5:5d48150916e8e02d030623a48512c874',
|
||||||
|
'duration': 5829.77,
|
||||||
|
'release_date': '20170404',
|
||||||
|
'series': 'Bandcamp Weekly',
|
||||||
|
'episode': 'Magic Moments',
|
||||||
|
'episode_number': 208,
|
||||||
|
'episode_id': '224',
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'url': 'https://bandcamp.com/?blah/blah@&show=228',
|
||||||
|
'only_matching': True
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
|
blob = self._parse_json(
|
||||||
|
self._search_regex(
|
||||||
|
r'data-blob=(["\'])(?P<blob>{.+?})\1', webpage,
|
||||||
|
'blob', group='blob'),
|
||||||
|
video_id, transform_source=unescapeHTML)
|
||||||
|
|
||||||
|
show = blob['bcw_show']
|
||||||
|
|
||||||
|
# This is desired because any invalid show id redirects to `bandcamp.com`
|
||||||
|
# which happens to expose the latest Bandcamp Weekly episode.
|
||||||
|
show_id = int_or_none(show.get('show_id')) or int_or_none(video_id)
|
||||||
|
|
||||||
|
formats = []
|
||||||
|
for format_id, format_url in show['audio_stream'].items():
|
||||||
|
if not isinstance(format_url, compat_str):
|
||||||
|
continue
|
||||||
|
for known_ext in KNOWN_EXTENSIONS:
|
||||||
|
if known_ext in format_id:
|
||||||
|
ext = known_ext
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
ext = None
|
||||||
|
formats.append({
|
||||||
|
'format_id': format_id,
|
||||||
|
'url': format_url,
|
||||||
|
'ext': ext,
|
||||||
|
'vcodec': 'none',
|
||||||
|
})
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
title = show.get('audio_title') or 'Bandcamp Weekly'
|
||||||
|
subtitle = show.get('subtitle')
|
||||||
|
if subtitle:
|
||||||
|
title += ' - %s' % subtitle
|
||||||
|
|
||||||
|
episode_number = None
|
||||||
|
seq = blob.get('bcw_seq')
|
||||||
|
|
||||||
|
if seq and isinstance(seq, list):
|
||||||
|
try:
|
||||||
|
episode_number = next(
|
||||||
|
int_or_none(e.get('episode_number'))
|
||||||
|
for e in seq
|
||||||
|
if isinstance(e, dict) and int_or_none(e.get('id')) == show_id)
|
||||||
|
except StopIteration:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': title,
|
||||||
|
'description': show.get('desc') or show.get('short_desc'),
|
||||||
|
'duration': float_or_none(show.get('audio_duration')),
|
||||||
|
'is_live': False,
|
||||||
|
'release_date': unified_strdate(show.get('published_date')),
|
||||||
|
'series': 'Bandcamp Weekly',
|
||||||
|
'episode': show.get('subtitle'),
|
||||||
|
'episode_number': episode_number,
|
||||||
|
'episode_id': compat_str(video_id),
|
||||||
|
'formats': formats
|
||||||
|
}
|
||||||
|
@@ -6,14 +6,18 @@ import itertools
|
|||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
clean_html,
|
||||||
dict_get,
|
dict_get,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
|
get_element_by_class,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_duration,
|
parse_duration,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
try_get,
|
try_get,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
|
urlencode_postdata,
|
||||||
|
urljoin,
|
||||||
)
|
)
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_etree_fromstring,
|
compat_etree_fromstring,
|
||||||
@@ -38,6 +42,9 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
(?P<id>%s)(?!/(?:episodes|broadcasts|clips))
|
(?P<id>%s)(?!/(?:episodes|broadcasts|clips))
|
||||||
''' % _ID_REGEX
|
''' % _ID_REGEX
|
||||||
|
|
||||||
|
_LOGIN_URL = 'https://account.bbc.com/signin'
|
||||||
|
_NETRC_MACHINE = 'bbc'
|
||||||
|
|
||||||
_MEDIASELECTOR_URLS = [
|
_MEDIASELECTOR_URLS = [
|
||||||
# Provides HQ HLS streams with even better quality that pc mediaset but fails
|
# Provides HQ HLS streams with even better quality that pc mediaset but fails
|
||||||
# with geolocation in some cases when it's even not geo restricted at all (e.g.
|
# with geolocation in some cases when it's even not geo restricted at all (e.g.
|
||||||
@@ -227,6 +234,39 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
|
|
||||||
_USP_RE = r'/([^/]+?)\.ism(?:\.hlsv2\.ism)?/[^/]+\.m3u8'
|
_USP_RE = r'/([^/]+?)\.ism(?:\.hlsv2\.ism)?/[^/]+\.m3u8'
|
||||||
|
|
||||||
|
def _login(self):
|
||||||
|
username, password = self._get_login_info()
|
||||||
|
if username is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
login_page = self._download_webpage(
|
||||||
|
self._LOGIN_URL, None, 'Downloading signin page')
|
||||||
|
|
||||||
|
login_form = self._hidden_inputs(login_page)
|
||||||
|
|
||||||
|
login_form.update({
|
||||||
|
'username': username,
|
||||||
|
'password': password,
|
||||||
|
})
|
||||||
|
|
||||||
|
post_url = urljoin(self._LOGIN_URL, self._search_regex(
|
||||||
|
r'<form[^>]+action=(["\'])(?P<url>.+?)\1', login_page,
|
||||||
|
'post url', default=self._LOGIN_URL, group='url'))
|
||||||
|
|
||||||
|
response, urlh = self._download_webpage_handle(
|
||||||
|
post_url, None, 'Logging in', data=urlencode_postdata(login_form),
|
||||||
|
headers={'Referer': self._LOGIN_URL})
|
||||||
|
|
||||||
|
if self._LOGIN_URL in urlh.geturl():
|
||||||
|
error = clean_html(get_element_by_class('form-message', response))
|
||||||
|
if error:
|
||||||
|
raise ExtractorError(
|
||||||
|
'Unable to login: %s' % error, expected=True)
|
||||||
|
raise ExtractorError('Unable to log in')
|
||||||
|
|
||||||
|
def _real_initialize(self):
|
||||||
|
self._login()
|
||||||
|
|
||||||
class MediaSelectionError(Exception):
|
class MediaSelectionError(Exception):
|
||||||
def __init__(self, id):
|
def __init__(self, id):
|
||||||
self.id = id
|
self.id = id
|
||||||
|
@@ -6,18 +6,33 @@ from ..utils import (
|
|||||||
ExtractorError,
|
ExtractorError,
|
||||||
clean_html,
|
clean_html,
|
||||||
compat_str,
|
compat_str,
|
||||||
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
try_get,
|
try_get,
|
||||||
|
urljoin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BeamProLiveIE(InfoExtractor):
|
class BeamProBaseIE(InfoExtractor):
|
||||||
IE_NAME = 'Beam:live'
|
_API_BASE = 'https://mixer.com/api/v1'
|
||||||
_VALID_URL = r'https?://(?:\w+\.)?beam\.pro/(?P<id>[^/?#&]+)'
|
|
||||||
_RATINGS = {'family': 0, 'teen': 13, '18+': 18}
|
_RATINGS = {'family': 0, 'teen': 13, '18+': 18}
|
||||||
|
|
||||||
|
def _extract_channel_info(self, chan):
|
||||||
|
user_id = chan.get('userId') or try_get(chan, lambda x: x['user']['id'])
|
||||||
|
return {
|
||||||
|
'uploader': chan.get('token') or try_get(
|
||||||
|
chan, lambda x: x['user']['username'], compat_str),
|
||||||
|
'uploader_id': compat_str(user_id) if user_id else None,
|
||||||
|
'age_limit': self._RATINGS.get(chan.get('audience')),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BeamProLiveIE(BeamProBaseIE):
|
||||||
|
IE_NAME = 'Mixer:live'
|
||||||
|
_VALID_URL = r'https?://(?:\w+\.)?(?:beam\.pro|mixer\.com)/(?P<id>[^/?#&]+)'
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://www.beam.pro/niterhayven',
|
'url': 'http://mixer.com/niterhayven',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '261562',
|
'id': '261562',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
@@ -38,11 +53,17 @@ class BeamProLiveIE(InfoExtractor):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_MANIFEST_URL_TEMPLATE = '%s/channels/%%s/manifest.%%s' % BeamProBaseIE._API_BASE
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def suitable(cls, url):
|
||||||
|
return False if BeamProVodIE.suitable(url) else super(BeamProLiveIE, cls).suitable(url)
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
channel_name = self._match_id(url)
|
channel_name = self._match_id(url)
|
||||||
|
|
||||||
chan = self._download_json(
|
chan = self._download_json(
|
||||||
'https://beam.pro/api/v1/channels/%s' % channel_name, channel_name)
|
'%s/channels/%s' % (self._API_BASE, channel_name), channel_name)
|
||||||
|
|
||||||
if chan.get('online') is False:
|
if chan.get('online') is False:
|
||||||
raise ExtractorError(
|
raise ExtractorError(
|
||||||
@@ -50,24 +71,118 @@ class BeamProLiveIE(InfoExtractor):
|
|||||||
|
|
||||||
channel_id = chan['id']
|
channel_id = chan['id']
|
||||||
|
|
||||||
|
def manifest_url(kind):
|
||||||
|
return self._MANIFEST_URL_TEMPLATE % (channel_id, kind)
|
||||||
|
|
||||||
formats = self._extract_m3u8_formats(
|
formats = self._extract_m3u8_formats(
|
||||||
'https://beam.pro/api/v1/channels/%s/manifest.m3u8' % channel_id,
|
manifest_url('m3u8'), channel_name, ext='mp4', m3u8_id='hls',
|
||||||
channel_name, ext='mp4', m3u8_id='hls', fatal=False)
|
fatal=False)
|
||||||
|
formats.extend(self._extract_smil_formats(
|
||||||
|
manifest_url('smil'), channel_name, fatal=False))
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
user_id = chan.get('userId') or try_get(chan, lambda x: x['user']['id'])
|
info = {
|
||||||
|
|
||||||
return {
|
|
||||||
'id': compat_str(chan.get('id') or channel_name),
|
'id': compat_str(chan.get('id') or channel_name),
|
||||||
'title': self._live_title(chan.get('name') or channel_name),
|
'title': self._live_title(chan.get('name') or channel_name),
|
||||||
'description': clean_html(chan.get('description')),
|
'description': clean_html(chan.get('description')),
|
||||||
'thumbnail': try_get(chan, lambda x: x['thumbnail']['url'], compat_str),
|
'thumbnail': try_get(
|
||||||
|
chan, lambda x: x['thumbnail']['url'], compat_str),
|
||||||
'timestamp': parse_iso8601(chan.get('updatedAt')),
|
'timestamp': parse_iso8601(chan.get('updatedAt')),
|
||||||
'uploader': chan.get('token') or try_get(
|
|
||||||
chan, lambda x: x['user']['username'], compat_str),
|
|
||||||
'uploader_id': compat_str(user_id) if user_id else None,
|
|
||||||
'age_limit': self._RATINGS.get(chan.get('audience')),
|
|
||||||
'is_live': True,
|
'is_live': True,
|
||||||
'view_count': int_or_none(chan.get('viewersTotal')),
|
'view_count': int_or_none(chan.get('viewersTotal')),
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
}
|
}
|
||||||
|
info.update(self._extract_channel_info(chan))
|
||||||
|
|
||||||
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
class BeamProVodIE(BeamProBaseIE):
|
||||||
|
IE_NAME = 'Mixer:vod'
|
||||||
|
_VALID_URL = r'https?://(?:\w+\.)?(?:beam\.pro|mixer\.com)/[^/?#&]+\?.*?\bvod=(?P<id>\d+)'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'https://mixer.com/willow8714?vod=2259830',
|
||||||
|
'md5': 'b2431e6e8347dc92ebafb565d368b76b',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '2259830',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'willow8714\'s Channel',
|
||||||
|
'duration': 6828.15,
|
||||||
|
'thumbnail': r're:https://.*source\.png$',
|
||||||
|
'timestamp': 1494046474,
|
||||||
|
'upload_date': '20170506',
|
||||||
|
'uploader': 'willow8714',
|
||||||
|
'uploader_id': '6085379',
|
||||||
|
'age_limit': 13,
|
||||||
|
'view_count': int,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_format(vod, vod_type):
|
||||||
|
if not vod.get('baseUrl'):
|
||||||
|
return []
|
||||||
|
|
||||||
|
if vod_type == 'hls':
|
||||||
|
filename, protocol = 'manifest.m3u8', 'm3u8_native'
|
||||||
|
elif vod_type == 'raw':
|
||||||
|
filename, protocol = 'source.mp4', 'https'
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
data = vod.get('data') if isinstance(vod.get('data'), dict) else {}
|
||||||
|
|
||||||
|
format_id = [vod_type]
|
||||||
|
if isinstance(data.get('Height'), compat_str):
|
||||||
|
format_id.append('%sp' % data['Height'])
|
||||||
|
|
||||||
|
return [{
|
||||||
|
'url': urljoin(vod['baseUrl'], filename),
|
||||||
|
'format_id': '-'.join(format_id),
|
||||||
|
'ext': 'mp4',
|
||||||
|
'protocol': protocol,
|
||||||
|
'width': int_or_none(data.get('Width')),
|
||||||
|
'height': int_or_none(data.get('Height')),
|
||||||
|
'fps': int_or_none(data.get('Fps')),
|
||||||
|
'tbr': int_or_none(data.get('Bitrate'), 1000),
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
vod_id = self._match_id(url)
|
||||||
|
|
||||||
|
vod_info = self._download_json(
|
||||||
|
'%s/recordings/%s' % (self._API_BASE, vod_id), vod_id)
|
||||||
|
|
||||||
|
state = vod_info.get('state')
|
||||||
|
if state != 'AVAILABLE':
|
||||||
|
raise ExtractorError(
|
||||||
|
'VOD %s is not available (state: %s)' % (vod_id, state),
|
||||||
|
expected=True)
|
||||||
|
|
||||||
|
formats = []
|
||||||
|
thumbnail_url = None
|
||||||
|
|
||||||
|
for vod in vod_info['vods']:
|
||||||
|
vod_type = vod.get('format')
|
||||||
|
if vod_type in ('hls', 'raw'):
|
||||||
|
formats.extend(self._extract_format(vod, vod_type))
|
||||||
|
elif vod_type == 'thumbnail':
|
||||||
|
thumbnail_url = urljoin(vod.get('baseUrl'), 'source.png')
|
||||||
|
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
info = {
|
||||||
|
'id': vod_id,
|
||||||
|
'title': vod_info.get('name') or vod_id,
|
||||||
|
'duration': float_or_none(vod_info.get('duration')),
|
||||||
|
'thumbnail': thumbnail_url,
|
||||||
|
'timestamp': parse_iso8601(vod_info.get('createdAt')),
|
||||||
|
'view_count': int_or_none(vod_info.get('viewsTotal')),
|
||||||
|
'formats': formats,
|
||||||
|
}
|
||||||
|
info.update(self._extract_channel_info(vod_info.get('channel') or {}))
|
||||||
|
|
||||||
|
return info
|
||||||
|
@@ -16,7 +16,7 @@ class BeegIE(InfoExtractor):
|
|||||||
_VALID_URL = r'https?://(?:www\.)?beeg\.com/(?P<id>\d+)'
|
_VALID_URL = r'https?://(?:www\.)?beeg\.com/(?P<id>\d+)'
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://beeg.com/5416503',
|
'url': 'http://beeg.com/5416503',
|
||||||
'md5': '46c384def73b33dbc581262e5ee67cef',
|
'md5': 'a1a1b1a8bc70a89e49ccfd113aed0820',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '5416503',
|
'id': '5416503',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
|
@@ -54,6 +54,22 @@ class BiliBiliIE(InfoExtractor):
|
|||||||
'description': '如果你是神明,并且能够让妄想成为现实。那你会进行怎么样的妄想?是淫靡的世界?独裁社会?毁灭性的制裁?还是……2015年,涩谷。从6年前发生的大灾害“涩谷地震”之后复兴了的这个街区里新设立的私立高中...',
|
'description': '如果你是神明,并且能够让妄想成为现实。那你会进行怎么样的妄想?是淫靡的世界?独裁社会?毁灭性的制裁?还是……2015年,涩谷。从6年前发生的大灾害“涩谷地震”之后复兴了的这个街区里新设立的私立高中...',
|
||||||
},
|
},
|
||||||
'skip': 'Geo-restricted to China',
|
'skip': 'Geo-restricted to China',
|
||||||
|
}, {
|
||||||
|
# Title with double quotes
|
||||||
|
'url': 'http://www.bilibili.com/video/av8903802/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '8903802',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': '阿滴英文|英文歌分享#6 "Closer',
|
||||||
|
'description': '滴妹今天唱Closer給你聽! 有史以来,被推最多次也是最久的歌曲,其实歌词跟我原本想像差蛮多的,不过还是好听! 微博@阿滴英文',
|
||||||
|
'uploader': '阿滴英文',
|
||||||
|
'uploader_id': '65880958',
|
||||||
|
'timestamp': 1488382620,
|
||||||
|
'upload_date': '20170301',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True, # Test metadata only
|
||||||
|
},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
_APP_KEY = '84956560bc028eb7'
|
_APP_KEY = '84956560bc028eb7'
|
||||||
@@ -122,6 +138,11 @@ class BiliBiliIE(InfoExtractor):
|
|||||||
'preference': -2 if 'hd.mp4' in backup_url else -3,
|
'preference': -2 if 'hd.mp4' in backup_url else -3,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
for a_format in formats:
|
||||||
|
a_format.setdefault('http_headers', {}).update({
|
||||||
|
'Referer': url,
|
||||||
|
})
|
||||||
|
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
entries.append({
|
entries.append({
|
||||||
@@ -130,7 +151,7 @@ class BiliBiliIE(InfoExtractor):
|
|||||||
'formats': formats,
|
'formats': formats,
|
||||||
})
|
})
|
||||||
|
|
||||||
title = self._html_search_regex('<h1[^>]+title="([^"]+)">', webpage, 'title')
|
title = self._html_search_regex('<h1[^>]*>([^<]+)</h1>', webpage, 'title')
|
||||||
description = self._html_search_meta('description', webpage)
|
description = self._html_search_meta('description', webpage)
|
||||||
timestamp = unified_timestamp(self._html_search_regex(
|
timestamp = unified_timestamp(self._html_search_regex(
|
||||||
r'<time[^>]+datetime="([^"]+)"', webpage, 'upload time', default=None))
|
r'<time[^>]+datetime="([^"]+)"', webpage, 'upload time', default=None))
|
||||||
|
@@ -35,7 +35,7 @@ class BleacherReportIE(InfoExtractor):
|
|||||||
'title': 'Aussie Golfers Get Fright of Their Lives After Being Chased by Angry Kangaroo',
|
'title': 'Aussie Golfers Get Fright of Their Lives After Being Chased by Angry Kangaroo',
|
||||||
'timestamp': 1446839961,
|
'timestamp': 1446839961,
|
||||||
'uploader': 'Sean Fay',
|
'uploader': 'Sean Fay',
|
||||||
'description': 'md5:825e94e0f3521df52fa83b2ed198fa20',
|
'description': 'md5:b1601e2314c4d8eec23b6eafe086a757',
|
||||||
'uploader_id': 6466954,
|
'uploader_id': 6466954,
|
||||||
'upload_date': '20151011',
|
'upload_date': '20151011',
|
||||||
},
|
},
|
||||||
@@ -90,17 +90,13 @@ class BleacherReportCMSIE(AMPIE):
|
|||||||
_VALID_URL = r'https?://(?:www\.)?bleacherreport\.com/video_embed\?id=(?P<id>[0-9a-f-]{36})'
|
_VALID_URL = r'https?://(?:www\.)?bleacherreport\.com/video_embed\?id=(?P<id>[0-9a-f-]{36})'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://bleacherreport.com/video_embed?id=8fd44c2f-3dc5-4821-9118-2c825a98c0e1',
|
'url': 'http://bleacherreport.com/video_embed?id=8fd44c2f-3dc5-4821-9118-2c825a98c0e1',
|
||||||
'md5': '8c2c12e3af7805152675446c905d159b',
|
'md5': '2e4b0a997f9228ffa31fada5c53d1ed1',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '8fd44c2f-3dc5-4821-9118-2c825a98c0e1',
|
'id': '8fd44c2f-3dc5-4821-9118-2c825a98c0e1',
|
||||||
'ext': 'mp4',
|
'ext': 'flv',
|
||||||
'title': 'Cena vs. Rollins Would Expose the Heavyweight Division',
|
'title': 'Cena vs. Rollins Would Expose the Heavyweight Division',
|
||||||
'description': 'md5:984afb4ade2f9c0db35f3267ed88b36e',
|
'description': 'md5:984afb4ade2f9c0db35f3267ed88b36e',
|
||||||
},
|
},
|
||||||
'params': {
|
|
||||||
# m3u8 download
|
|
||||||
'skip_download': True,
|
|
||||||
},
|
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
@@ -77,7 +77,7 @@ class BRIE(InfoExtractor):
|
|||||||
'description': 'md5:bb659990e9e59905c3d41e369db1fbe3',
|
'description': 'md5:bb659990e9e59905c3d41e369db1fbe3',
|
||||||
'duration': 893,
|
'duration': 893,
|
||||||
'uploader': 'Eva Maria Steimle',
|
'uploader': 'Eva Maria Steimle',
|
||||||
'upload_date': '20140117',
|
'upload_date': '20170208',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@@ -5,6 +5,7 @@ import re
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from .adobepass import AdobePassIE
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_etree_fromstring,
|
compat_etree_fromstring,
|
||||||
compat_parse_qs,
|
compat_parse_qs,
|
||||||
@@ -448,7 +449,7 @@ class BrightcoveLegacyIE(InfoExtractor):
|
|||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
class BrightcoveNewIE(InfoExtractor):
|
class BrightcoveNewIE(AdobePassIE):
|
||||||
IE_NAME = 'brightcove:new'
|
IE_NAME = 'brightcove:new'
|
||||||
_VALID_URL = r'https?://players\.brightcove\.net/(?P<account_id>\d+)/(?P<player_id>[^/]+)_(?P<embed>[^/]+)/index\.html\?.*videoId=(?P<video_id>\d+|ref:[^&]+)'
|
_VALID_URL = r'https?://players\.brightcove\.net/(?P<account_id>\d+)/(?P<player_id>[^/]+)_(?P<embed>[^/]+)/index\.html\?.*videoId=(?P<video_id>\d+|ref:[^&]+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
@@ -522,7 +523,7 @@ class BrightcoveNewIE(InfoExtractor):
|
|||||||
# [2] looks like:
|
# [2] looks like:
|
||||||
for video, script_tag, account_id, player_id, embed in re.findall(
|
for video, script_tag, account_id, player_id, embed in re.findall(
|
||||||
r'''(?isx)
|
r'''(?isx)
|
||||||
(<video\s+[^>]*data-video-id=['"]?[^>]+>)
|
(<video\s+[^>]*\bdata-video-id\s*=\s*['"]?[^>]+>)
|
||||||
(?:.*?
|
(?:.*?
|
||||||
(<script[^>]+
|
(<script[^>]+
|
||||||
src=["\'](?:https?:)?//players\.brightcove\.net/
|
src=["\'](?:https?:)?//players\.brightcove\.net/
|
||||||
@@ -602,6 +603,20 @@ class BrightcoveNewIE(InfoExtractor):
|
|||||||
raise ExtractorError(message, expected=True)
|
raise ExtractorError(message, expected=True)
|
||||||
raise
|
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,
|
||||||
|
})
|
||||||
|
|
||||||
title = json_data['name'].strip()
|
title = json_data['name'].strip()
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
@@ -667,7 +682,6 @@ class BrightcoveNewIE(InfoExtractor):
|
|||||||
})
|
})
|
||||||
formats.append(f)
|
formats.append(f)
|
||||||
|
|
||||||
errors = json_data.get('errors')
|
|
||||||
if not formats and errors:
|
if not formats and errors:
|
||||||
error = errors[0]
|
error = errors[0]
|
||||||
raise ExtractorError(
|
raise ExtractorError(
|
||||||
@@ -684,7 +698,7 @@ class BrightcoveNewIE(InfoExtractor):
|
|||||||
|
|
||||||
is_live = False
|
is_live = False
|
||||||
duration = float_or_none(json_data.get('duration'), 1000)
|
duration = float_or_none(json_data.get('duration'), 1000)
|
||||||
if duration and duration < 0:
|
if duration is not None and duration <= 0:
|
||||||
is_live = True
|
is_live = True
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -16,13 +16,10 @@ class Canalc2IE(InfoExtractor):
|
|||||||
'md5': '060158428b650f896c542dfbb3d6487f',
|
'md5': '060158428b650f896c542dfbb3d6487f',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '12163',
|
'id': '12163',
|
||||||
'ext': 'flv',
|
'ext': 'mp4',
|
||||||
'title': 'Terrasses du Numérique',
|
'title': 'Terrasses du Numérique',
|
||||||
'duration': 122,
|
'duration': 122,
|
||||||
},
|
},
|
||||||
'params': {
|
|
||||||
'skip_download': True, # Requires rtmpdump
|
|
||||||
}
|
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://archives-canalc2.u-strasbg.fr/video.asp?idVideo=11427&voir=oui',
|
'url': 'http://archives-canalc2.u-strasbg.fr/video.asp?idVideo=11427&voir=oui',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
@@ -96,6 +96,7 @@ class CBCIE(InfoExtractor):
|
|||||||
'info_dict': {
|
'info_dict': {
|
||||||
'title': 'Keep Rover active during the deep freeze with doggie pushups and other fun indoor tasks',
|
'title': 'Keep Rover active during the deep freeze with doggie pushups and other fun indoor tasks',
|
||||||
'id': 'dog-indoor-exercise-winter-1.3928238',
|
'id': 'dog-indoor-exercise-winter-1.3928238',
|
||||||
|
'description': 'md5:c18552e41726ee95bd75210d1ca9194c',
|
||||||
},
|
},
|
||||||
'playlist_mincount': 6,
|
'playlist_mincount': 6,
|
||||||
}]
|
}]
|
||||||
@@ -165,12 +166,11 @@ class CBCPlayerIE(InfoExtractor):
|
|||||||
'uploader': 'CBCC-NEW',
|
'uploader': 'CBCC-NEW',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
# available only when we add `formats=MPEG4,FLV,MP3` to theplatform url
|
|
||||||
'url': 'http://www.cbc.ca/player/play/2164402062',
|
'url': 'http://www.cbc.ca/player/play/2164402062',
|
||||||
'md5': '17a61eb813539abea40618d6323a7f82',
|
'md5': '33fcd8f6719b9dd60a5e73adcb83b9f6',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '2164402062',
|
'id': '2164402062',
|
||||||
'ext': 'flv',
|
'ext': 'mp4',
|
||||||
'title': 'Cancer survivor four times over',
|
'title': 'Cancer survivor four times over',
|
||||||
'description': 'Tim Mayer has beaten three different forms of cancer four times in five years.',
|
'description': 'Tim Mayer has beaten three different forms of cancer four times in five years.',
|
||||||
'timestamp': 1320410746,
|
'timestamp': 1320410746,
|
||||||
|
@@ -49,13 +49,13 @@ class CBSIE(CBSBaseIE):
|
|||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _extract_video_info(self, content_id):
|
def _extract_video_info(self, content_id, site='cbs', mpx_acc=2198311517):
|
||||||
items_data = self._download_xml(
|
items_data = self._download_xml(
|
||||||
'http://can.cbs.com/thunder/player/videoPlayerService.php',
|
'http://can.cbs.com/thunder/player/videoPlayerService.php',
|
||||||
content_id, query={'partner': 'cbs', 'contentId': content_id})
|
content_id, query={'partner': site, 'contentId': content_id})
|
||||||
video_data = xpath_element(items_data, './/item')
|
video_data = xpath_element(items_data, './/item')
|
||||||
title = xpath_text(video_data, 'videoTitle', 'title', True)
|
title = xpath_text(video_data, 'videoTitle', 'title', True)
|
||||||
tp_path = 'dJ5BDC/media/guid/2198311517/%s' % content_id
|
tp_path = 'dJ5BDC/media/guid/%d/%s' % (mpx_acc, content_id)
|
||||||
tp_release_url = 'http://link.theplatform.com/s/' + tp_path
|
tp_release_url = 'http://link.theplatform.com/s/' + tp_path
|
||||||
|
|
||||||
asset_types = []
|
asset_types = []
|
||||||
|
@@ -3,17 +3,18 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .theplatform import ThePlatformIE
|
from .cbs import CBSIE
|
||||||
from ..utils import int_or_none
|
from ..utils import int_or_none
|
||||||
|
|
||||||
|
|
||||||
class CBSInteractiveIE(ThePlatformIE):
|
class CBSInteractiveIE(CBSIE):
|
||||||
_VALID_URL = r'https?://(?:www\.)?(?P<site>cnet|zdnet)\.com/(?:videos|video/share)/(?P<id>[^/?]+)'
|
_VALID_URL = r'https?://(?:www\.)?(?P<site>cnet|zdnet)\.com/(?:videos|video(?:/share)?)/(?P<id>[^/?]+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.cnet.com/videos/hands-on-with-microsofts-windows-8-1-update/',
|
'url': 'http://www.cnet.com/videos/hands-on-with-microsofts-windows-8-1-update/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '56f4ea68-bd21-4852-b08c-4de5b8354c60',
|
'id': 'R49SYt__yAfmlXR85z4f7gNmCBDcN_00',
|
||||||
'ext': 'flv',
|
'display_id': 'hands-on-with-microsofts-windows-8-1-update',
|
||||||
|
'ext': 'mp4',
|
||||||
'title': 'Hands-on with Microsoft Windows 8.1 Update',
|
'title': 'Hands-on with Microsoft Windows 8.1 Update',
|
||||||
'description': 'The new update to the Windows 8 OS brings improved performance for mouse and keyboard users.',
|
'description': 'The new update to the Windows 8 OS brings improved performance for mouse and keyboard users.',
|
||||||
'uploader_id': '6085384d-619e-11e3-b231-14feb5ca9861',
|
'uploader_id': '6085384d-619e-11e3-b231-14feb5ca9861',
|
||||||
@@ -22,13 +23,19 @@ class CBSInteractiveIE(ThePlatformIE):
|
|||||||
'timestamp': 1396479627,
|
'timestamp': 1396479627,
|
||||||
'upload_date': '20140402',
|
'upload_date': '20140402',
|
||||||
},
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.cnet.com/videos/whiny-pothole-tweets-at-local-government-when-hit-by-cars-tomorrow-daily-187/',
|
'url': 'http://www.cnet.com/videos/whiny-pothole-tweets-at-local-government-when-hit-by-cars-tomorrow-daily-187/',
|
||||||
|
'md5': 'f11d27b2fa18597fbf92444d2a9ed386',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '56527b93-d25d-44e3-b738-f989ce2e49ba',
|
'id': 'kjOJd_OoVJqbg_ZD8MZCOk8Wekb9QccK',
|
||||||
'ext': 'flv',
|
'display_id': 'whiny-pothole-tweets-at-local-government-when-hit-by-cars-tomorrow-daily-187',
|
||||||
|
'ext': 'mp4',
|
||||||
'title': 'Whiny potholes tweet at local government when hit by cars (Tomorrow Daily 187)',
|
'title': 'Whiny potholes tweet at local government when hit by cars (Tomorrow Daily 187)',
|
||||||
'description': 'Khail and Ashley wonder what other civic woes can be solved by self-tweeting objects, investigate a new kind of VR camera and watch an origami robot self-assemble, walk, climb, dig and dissolve. #TDPothole',
|
'description': 'md5:d2b9a95a5ffe978ae6fbd4cf944d618f',
|
||||||
'uploader_id': 'b163284d-6b73-44fc-b3e6-3da66c392d40',
|
'uploader_id': 'b163284d-6b73-44fc-b3e6-3da66c392d40',
|
||||||
'uploader': 'Ashley Esqueda',
|
'uploader': 'Ashley Esqueda',
|
||||||
'duration': 1482,
|
'duration': 1482,
|
||||||
@@ -38,23 +45,28 @@ class CBSInteractiveIE(ThePlatformIE):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://www.zdnet.com/video/share/video-keeping-android-smartphones-and-tablets-secure/',
|
'url': 'http://www.zdnet.com/video/share/video-keeping-android-smartphones-and-tablets-secure/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'bc1af9f0-a2b5-4e54-880d-0d95525781c0',
|
'id': 'k0r4T_ehht4xW_hAOqiVQPuBDPZ8SRjt',
|
||||||
|
'display_id': 'video-keeping-android-smartphones-and-tablets-secure',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Video: Keeping Android smartphones and tablets secure',
|
'title': 'Video: Keeping Android smartphones and tablets secure',
|
||||||
'description': 'Here\'s the best way to keep Android devices secure, and what you do when they\'ve come to the end of their lives.',
|
'description': 'Here\'s the best way to keep Android devices secure, and what you do when they\'ve come to the end of their lives.',
|
||||||
'uploader_id': 'f2d97ea2-8175-11e2-9d12-0018fe8a00b0',
|
'uploader_id': 'f2d97ea2-8175-11e2-9d12-0018fe8a00b0',
|
||||||
'uploader': 'Adrian Kingsley-Hughes',
|
'uploader': 'Adrian Kingsley-Hughes',
|
||||||
'timestamp': 1448961720,
|
'duration': 731,
|
||||||
'upload_date': '20151201',
|
'timestamp': 1449129925,
|
||||||
|
'upload_date': '20151203',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
# m3u8 download
|
# m3u8 download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
}
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.zdnet.com/video/huawei-matebook-x-video/',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
TP_RELEASE_URL_TEMPLATE = 'http://link.theplatform.com/s/kYEXFC/%s?mbr=true'
|
|
||||||
MPX_ACCOUNTS = {
|
MPX_ACCOUNTS = {
|
||||||
'cnet': 2288573011,
|
'cnet': 2198311517,
|
||||||
'zdnet': 2387448114,
|
'zdnet': 2387448114,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +80,8 @@ class CBSInteractiveIE(ThePlatformIE):
|
|||||||
data = self._parse_json(data_json, display_id)
|
data = self._parse_json(data_json, display_id)
|
||||||
vdata = data.get('video') or data['videos'][0]
|
vdata = data.get('video') or data['videos'][0]
|
||||||
|
|
||||||
video_id = vdata['id']
|
video_id = vdata['mpxRefId']
|
||||||
|
|
||||||
title = vdata['title']
|
title = vdata['title']
|
||||||
author = vdata.get('author')
|
author = vdata.get('author')
|
||||||
if author:
|
if author:
|
||||||
@@ -78,20 +91,7 @@ class CBSInteractiveIE(ThePlatformIE):
|
|||||||
uploader = None
|
uploader = None
|
||||||
uploader_id = None
|
uploader_id = None
|
||||||
|
|
||||||
media_guid_path = 'media/guid/%d/%s' % (self.MPX_ACCOUNTS[site], vdata['mpxRefId'])
|
info = self._extract_video_info(video_id, site, self.MPX_ACCOUNTS[site])
|
||||||
formats, subtitles = [], {}
|
|
||||||
for (fkey, vid) in vdata['files'].items():
|
|
||||||
if fkey == 'hls_phone' and 'hls_tablet' in vdata['files']:
|
|
||||||
continue
|
|
||||||
release_url = self.TP_RELEASE_URL_TEMPLATE % vid
|
|
||||||
if fkey == 'hds':
|
|
||||||
release_url += '&manifest=f4m'
|
|
||||||
tp_formats, tp_subtitles = self._extract_theplatform_smil(release_url, video_id, 'Downloading %s SMIL data' % fkey)
|
|
||||||
formats.extend(tp_formats)
|
|
||||||
subtitles = self._merge_subtitles(subtitles, tp_subtitles)
|
|
||||||
self._sort_formats(formats)
|
|
||||||
|
|
||||||
info = self._extract_theplatform_metadata('kYEXFC/%s' % media_guid_path, video_id)
|
|
||||||
info.update({
|
info.update({
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'display_id': display_id,
|
'display_id': display_id,
|
||||||
@@ -99,7 +99,5 @@ class CBSInteractiveIE(ThePlatformIE):
|
|||||||
'duration': int_or_none(vdata.get('duration')),
|
'duration': int_or_none(vdata.get('duration')),
|
||||||
'uploader': uploader,
|
'uploader': uploader,
|
||||||
'uploader_id': uploader_id,
|
'uploader_id': uploader_id,
|
||||||
'subtitles': subtitles,
|
|
||||||
'formats': formats,
|
|
||||||
})
|
})
|
||||||
return info
|
return info
|
||||||
|
@@ -60,8 +60,8 @@ class CBSLocalIE(AnvatoIE):
|
|||||||
'title': 'A Very Blue Anniversary',
|
'title': 'A Very Blue Anniversary',
|
||||||
'description': 'CBS2’s Cindy Hsu has more.',
|
'description': 'CBS2’s Cindy Hsu has more.',
|
||||||
'thumbnail': 're:^https?://.*',
|
'thumbnail': 're:^https?://.*',
|
||||||
'timestamp': 1479962220,
|
'timestamp': int,
|
||||||
'upload_date': '20161124',
|
'upload_date': r're:^\d{8}$',
|
||||||
'uploader': 'CBS',
|
'uploader': 'CBS',
|
||||||
'subtitles': {
|
'subtitles': {
|
||||||
'en': 'mincount:5',
|
'en': 'mincount:5',
|
||||||
|
@@ -61,11 +61,17 @@ class CBSNewsIE(CBSIE):
|
|||||||
|
|
||||||
video_info = self._parse_json(self._html_search_regex(
|
video_info = self._parse_json(self._html_search_regex(
|
||||||
r'(?:<ul class="media-list items" id="media-related-items"><li data-video-info|<div id="cbsNewsVideoPlayer" data-video-player-options)=\'({.+?})\'',
|
r'(?:<ul class="media-list items" id="media-related-items"><li data-video-info|<div id="cbsNewsVideoPlayer" data-video-player-options)=\'({.+?})\'',
|
||||||
webpage, 'video JSON info'), video_id)
|
webpage, 'video JSON info', default='{}'), video_id, fatal=False)
|
||||||
|
|
||||||
item = video_info['item'] if 'item' in video_info else video_info
|
if video_info:
|
||||||
guid = item['mpxRefId']
|
item = video_info['item'] if 'item' in video_info else video_info
|
||||||
return self._extract_video_info(guid)
|
else:
|
||||||
|
state = self._parse_json(self._search_regex(
|
||||||
|
r'data-cbsvideoui-options=(["\'])(?P<json>{.+?})\1', webpage,
|
||||||
|
'playlist JSON info', group='json'), video_id)['state']
|
||||||
|
item = state['playlist'][state['pid']]
|
||||||
|
|
||||||
|
return self._extract_video_info(item['mpxRefId'], 'cbsnews')
|
||||||
|
|
||||||
|
|
||||||
class CBSNewsLiveVideoIE(InfoExtractor):
|
class CBSNewsLiveVideoIE(InfoExtractor):
|
||||||
|
@@ -9,7 +9,10 @@ from ..utils import (
|
|||||||
ExtractorError,
|
ExtractorError,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
multipart_encode,
|
||||||
parse_duration,
|
parse_duration,
|
||||||
|
random_birthday,
|
||||||
|
urljoin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -27,7 +30,8 @@ class CDAIE(InfoExtractor):
|
|||||||
'description': 'md5:269ccd135d550da90d1662651fcb9772',
|
'description': 'md5:269ccd135d550da90d1662651fcb9772',
|
||||||
'thumbnail': r're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'average_rating': float,
|
'average_rating': float,
|
||||||
'duration': 39
|
'duration': 39,
|
||||||
|
'age_limit': 0,
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.cda.pl/video/57413289',
|
'url': 'http://www.cda.pl/video/57413289',
|
||||||
@@ -41,13 +45,41 @@ class CDAIE(InfoExtractor):
|
|||||||
'uploader': 'crash404',
|
'uploader': 'crash404',
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
'average_rating': float,
|
'average_rating': float,
|
||||||
'duration': 137
|
'duration': 137,
|
||||||
|
'age_limit': 0,
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
# Age-restricted
|
||||||
|
'url': 'http://www.cda.pl/video/1273454c4',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1273454c4',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Bronson (2008) napisy HD 1080p',
|
||||||
|
'description': 'md5:1b6cb18508daf2dc4e0fa4db77fec24c',
|
||||||
|
'height': 1080,
|
||||||
|
'uploader': 'boniek61',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
'duration': 5554,
|
||||||
|
'age_limit': 18,
|
||||||
|
'view_count': int,
|
||||||
|
'average_rating': float,
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://ebd.cda.pl/0x0/5749950c',
|
'url': 'http://ebd.cda.pl/0x0/5749950c',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
def _download_age_confirm_page(self, url, video_id, *args, **kwargs):
|
||||||
|
form_data = random_birthday('rok', 'miesiac', 'dzien')
|
||||||
|
form_data.update({'return': url, 'module': 'video', 'module_id': video_id})
|
||||||
|
data, content_type = multipart_encode(form_data)
|
||||||
|
return self._download_webpage(
|
||||||
|
urljoin(url, '/a/validatebirth'), video_id, *args,
|
||||||
|
data=data, headers={
|
||||||
|
'Referer': url,
|
||||||
|
'Content-Type': content_type,
|
||||||
|
}, **kwargs)
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
self._set_cookie('cda.pl', 'cda.player', 'html5')
|
self._set_cookie('cda.pl', 'cda.player', 'html5')
|
||||||
@@ -57,6 +89,13 @@ class CDAIE(InfoExtractor):
|
|||||||
if 'Ten film jest dostępny dla użytkowników premium' in webpage:
|
if 'Ten film jest dostępny dla użytkowników premium' in webpage:
|
||||||
raise ExtractorError('This video is only available for premium users.', expected=True)
|
raise ExtractorError('This video is only available for premium users.', expected=True)
|
||||||
|
|
||||||
|
need_confirm_age = False
|
||||||
|
if self._html_search_regex(r'(<form[^>]+action="/a/validatebirth")',
|
||||||
|
webpage, 'birthday validate form', default=None):
|
||||||
|
webpage = self._download_age_confirm_page(
|
||||||
|
url, video_id, note='Confirming age')
|
||||||
|
need_confirm_age = True
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
|
|
||||||
uploader = self._search_regex(r'''(?x)
|
uploader = self._search_regex(r'''(?x)
|
||||||
@@ -81,6 +120,7 @@ class CDAIE(InfoExtractor):
|
|||||||
'thumbnail': self._og_search_thumbnail(webpage),
|
'thumbnail': self._og_search_thumbnail(webpage),
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
'duration': None,
|
'duration': None,
|
||||||
|
'age_limit': 18 if need_confirm_age else 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
def extract_format(page, version):
|
def extract_format(page, version):
|
||||||
@@ -121,7 +161,12 @@ class CDAIE(InfoExtractor):
|
|||||||
for href, resolution in re.findall(
|
for href, resolution in re.findall(
|
||||||
r'<a[^>]+data-quality="[^"]+"[^>]+href="([^"]+)"[^>]+class="quality-btn"[^>]*>([0-9]+p)',
|
r'<a[^>]+data-quality="[^"]+"[^>]+href="([^"]+)"[^>]+class="quality-btn"[^>]*>([0-9]+p)',
|
||||||
webpage):
|
webpage):
|
||||||
webpage = self._download_webpage(
|
if need_confirm_age:
|
||||||
|
handler = self._download_age_confirm_page
|
||||||
|
else:
|
||||||
|
handler = self._download_webpage
|
||||||
|
|
||||||
|
webpage = handler(
|
||||||
self._BASE_URL + href, video_id,
|
self._BASE_URL + href, video_id,
|
||||||
'Downloading %s version information' % resolution, fatal=False)
|
'Downloading %s version information' % resolution, fatal=False)
|
||||||
if not webpage:
|
if not webpage:
|
||||||
@@ -129,6 +174,7 @@ class CDAIE(InfoExtractor):
|
|||||||
# invalid version is requested.
|
# invalid version is requested.
|
||||||
self.report_warning('Unable to download %s version information' % resolution)
|
self.report_warning('Unable to download %s version information' % resolution)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
extract_format(webpage, resolution)
|
extract_format(webpage, resolution)
|
||||||
|
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
@@ -12,7 +12,7 @@ class ClipfishIE(InfoExtractor):
|
|||||||
_VALID_URL = r'https?://(?:www\.)?clipfish\.de/(?:[^/]+/)+video/(?P<id>[0-9]+)'
|
_VALID_URL = r'https?://(?:www\.)?clipfish\.de/(?:[^/]+/)+video/(?P<id>[0-9]+)'
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://www.clipfish.de/special/ugly-americans/video/4343170/s01-e01-ugly-americans-date-in-der-hoelle/',
|
'url': 'http://www.clipfish.de/special/ugly-americans/video/4343170/s01-e01-ugly-americans-date-in-der-hoelle/',
|
||||||
'md5': '720563e467b86374c194bdead08d207d',
|
'md5': 'b9a5dc46294154c1193e2d10e0c95693',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '4343170',
|
'id': '4343170',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
|
@@ -21,7 +21,7 @@ class CollegeRamaIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Een nieuwe wereld: waarden, bewustzijn en techniek van de mensheid 2.0.',
|
'title': 'Een nieuwe wereld: waarden, bewustzijn en techniek van de mensheid 2.0.',
|
||||||
'description': '',
|
'description': '',
|
||||||
'thumbnail': r're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg(?:\?.*?)?$',
|
||||||
'duration': 7713.088,
|
'duration': 7713.088,
|
||||||
'timestamp': 1413309600,
|
'timestamp': 1413309600,
|
||||||
'upload_date': '20141014',
|
'upload_date': '20141014',
|
||||||
@@ -35,6 +35,7 @@ class CollegeRamaIE(InfoExtractor):
|
|||||||
'ext': 'wmv',
|
'ext': 'wmv',
|
||||||
'title': '64ste Vakantiecursus: Afvalwater',
|
'title': '64ste Vakantiecursus: Afvalwater',
|
||||||
'description': 'md5:7fd774865cc69d972f542b157c328305',
|
'description': 'md5:7fd774865cc69d972f542b157c328305',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg(?:\?.*?)?$',
|
||||||
'duration': 10853,
|
'duration': 10853,
|
||||||
'timestamp': 1326446400,
|
'timestamp': 1326446400,
|
||||||
'upload_date': '20120113',
|
'upload_date': '20120113',
|
||||||
|
@@ -245,6 +245,10 @@ class InfoExtractor(object):
|
|||||||
specified in the URL.
|
specified in the URL.
|
||||||
end_time: Time in seconds where the reproduction should end, as
|
end_time: Time in seconds where the reproduction should end, as
|
||||||
specified in the URL.
|
specified in the URL.
|
||||||
|
chapters: A list of dictionaries, with the following entries:
|
||||||
|
* "start_time" - The start time of the chapter in seconds
|
||||||
|
* "end_time" - The end time of the chapter in seconds
|
||||||
|
* "title" (optional, string)
|
||||||
|
|
||||||
The following fields should only be used when the video belongs to some logical
|
The following fields should only be used when the video belongs to some logical
|
||||||
chapter or section:
|
chapter or section:
|
||||||
@@ -372,7 +376,7 @@ class InfoExtractor(object):
|
|||||||
cls._VALID_URL_RE = re.compile(cls._VALID_URL)
|
cls._VALID_URL_RE = re.compile(cls._VALID_URL)
|
||||||
m = cls._VALID_URL_RE.match(url)
|
m = cls._VALID_URL_RE.match(url)
|
||||||
assert m
|
assert m
|
||||||
return m.group('id')
|
return compat_str(m.group('id'))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def working(cls):
|
def working(cls):
|
||||||
@@ -416,7 +420,7 @@ class InfoExtractor(object):
|
|||||||
if country_code:
|
if country_code:
|
||||||
self._x_forwarded_for_ip = GeoUtils.random_ipv4(country_code)
|
self._x_forwarded_for_ip = GeoUtils.random_ipv4(country_code)
|
||||||
if self._downloader.params.get('verbose', False):
|
if self._downloader.params.get('verbose', False):
|
||||||
self._downloader.to_stdout(
|
self._downloader.to_screen(
|
||||||
'[debug] Using fake IP %s (%s) as X-Forwarded-For.'
|
'[debug] Using fake IP %s (%s) as X-Forwarded-For.'
|
||||||
% (self._x_forwarded_for_ip, country_code.upper()))
|
% (self._x_forwarded_for_ip, country_code.upper()))
|
||||||
|
|
||||||
@@ -990,6 +994,7 @@ class InfoExtractor(object):
|
|||||||
'tbr': int_or_none(e.get('bitrate')),
|
'tbr': int_or_none(e.get('bitrate')),
|
||||||
'width': int_or_none(e.get('width')),
|
'width': int_or_none(e.get('width')),
|
||||||
'height': int_or_none(e.get('height')),
|
'height': int_or_none(e.get('height')),
|
||||||
|
'view_count': int_or_none(e.get('interactionCount')),
|
||||||
})
|
})
|
||||||
|
|
||||||
for e in json_ld:
|
for e in json_ld:
|
||||||
@@ -1334,7 +1339,7 @@ class InfoExtractor(object):
|
|||||||
if '#EXT-X-FAXS-CM:' in m3u8_doc: # Adobe Flash Access
|
if '#EXT-X-FAXS-CM:' in m3u8_doc: # Adobe Flash Access
|
||||||
return []
|
return []
|
||||||
|
|
||||||
formats = [self._m3u8_meta_format(m3u8_url, ext, preference, m3u8_id)]
|
formats = []
|
||||||
|
|
||||||
format_url = lambda u: (
|
format_url = lambda u: (
|
||||||
u
|
u
|
||||||
@@ -1386,6 +1391,7 @@ class InfoExtractor(object):
|
|||||||
f = {
|
f = {
|
||||||
'format_id': '-'.join(format_id),
|
'format_id': '-'.join(format_id),
|
||||||
'url': format_url(media_url),
|
'url': format_url(media_url),
|
||||||
|
'manifest_url': m3u8_url,
|
||||||
'language': media.get('LANGUAGE'),
|
'language': media.get('LANGUAGE'),
|
||||||
'ext': ext,
|
'ext': ext,
|
||||||
'protocol': entry_protocol,
|
'protocol': entry_protocol,
|
||||||
@@ -1438,7 +1444,7 @@ class InfoExtractor(object):
|
|||||||
f = {
|
f = {
|
||||||
'format_id': '-'.join(format_id),
|
'format_id': '-'.join(format_id),
|
||||||
'url': manifest_url,
|
'url': manifest_url,
|
||||||
'manifest_url': manifest_url,
|
'manifest_url': m3u8_url,
|
||||||
'tbr': tbr,
|
'tbr': tbr,
|
||||||
'ext': ext,
|
'ext': ext,
|
||||||
'fps': float_or_none(last_stream_inf.get('FRAME-RATE')),
|
'fps': float_or_none(last_stream_inf.get('FRAME-RATE')),
|
||||||
@@ -1995,6 +2001,12 @@ class InfoExtractor(object):
|
|||||||
compat_etree_fromstring(ism.encode('utf-8')), urlh.geturl(), ism_id)
|
compat_etree_fromstring(ism.encode('utf-8')), urlh.geturl(), ism_id)
|
||||||
|
|
||||||
def _parse_ism_formats(self, ism_doc, ism_url, ism_id=None):
|
def _parse_ism_formats(self, ism_doc, ism_url, ism_id=None):
|
||||||
|
"""
|
||||||
|
Parse formats from ISM manifest.
|
||||||
|
References:
|
||||||
|
1. [MS-SSTR]: Smooth Streaming Protocol,
|
||||||
|
https://msdn.microsoft.com/en-us/library/ff469518.aspx
|
||||||
|
"""
|
||||||
if ism_doc.get('IsLive') == 'TRUE' or ism_doc.find('Protection') is not None:
|
if ism_doc.get('IsLive') == 'TRUE' or ism_doc.find('Protection') is not None:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -2016,8 +2028,11 @@ class InfoExtractor(object):
|
|||||||
self.report_warning('%s is not a supported codec' % fourcc)
|
self.report_warning('%s is not a supported codec' % fourcc)
|
||||||
continue
|
continue
|
||||||
tbr = int(track.attrib['Bitrate']) // 1000
|
tbr = int(track.attrib['Bitrate']) // 1000
|
||||||
width = int_or_none(track.get('MaxWidth'))
|
# [1] does not mention Width and Height attributes. However,
|
||||||
height = int_or_none(track.get('MaxHeight'))
|
# they're often present while MaxWidth and MaxHeight are
|
||||||
|
# missing, so should be used as fallbacks
|
||||||
|
width = int_or_none(track.get('MaxWidth') or track.get('Width'))
|
||||||
|
height = int_or_none(track.get('MaxHeight') or track.get('Height'))
|
||||||
sampling_rate = int_or_none(track.get('SamplingRate'))
|
sampling_rate = int_or_none(track.get('SamplingRate'))
|
||||||
|
|
||||||
track_url_pattern = re.sub(r'{[Bb]itrate}', track.attrib['Bitrate'], url_pattern)
|
track_url_pattern = re.sub(r'{[Bb]itrate}', track.attrib['Bitrate'], url_pattern)
|
||||||
@@ -2168,7 +2183,7 @@ class InfoExtractor(object):
|
|||||||
def _extract_akamai_formats(self, manifest_url, video_id, hosts={}):
|
def _extract_akamai_formats(self, manifest_url, video_id, hosts={}):
|
||||||
formats = []
|
formats = []
|
||||||
hdcore_sign = 'hdcore=3.7.0'
|
hdcore_sign = 'hdcore=3.7.0'
|
||||||
f4m_url = re.sub(r'(https?://[^/+])/i/', r'\1/z/', manifest_url).replace('/master.m3u8', '/manifest.f4m')
|
f4m_url = re.sub(r'(https?://[^/]+)/i/', r'\1/z/', manifest_url).replace('/master.m3u8', '/manifest.f4m')
|
||||||
hds_host = hosts.get('hds')
|
hds_host = hosts.get('hds')
|
||||||
if hds_host:
|
if hds_host:
|
||||||
f4m_url = re.sub(r'(https?://)[^/]+', r'\1' + hds_host, f4m_url)
|
f4m_url = re.sub(r'(https?://)[^/]+', r'\1' + hds_host, f4m_url)
|
||||||
@@ -2190,8 +2205,9 @@ class InfoExtractor(object):
|
|||||||
|
|
||||||
def _extract_wowza_formats(self, url, video_id, m3u8_entry_protocol='m3u8_native', skip_protocols=[]):
|
def _extract_wowza_formats(self, url, video_id, m3u8_entry_protocol='m3u8_native', skip_protocols=[]):
|
||||||
url = re.sub(r'/(?:manifest|playlist|jwplayer)\.(?:m3u8|f4m|mpd|smil)', '', url)
|
url = re.sub(r'/(?:manifest|playlist|jwplayer)\.(?:m3u8|f4m|mpd|smil)', '', url)
|
||||||
url_base = self._search_regex(r'(?:https?|rtmp|rtsp)(://[^?]+)', url, 'format url')
|
url_base = self._search_regex(
|
||||||
http_base_url = 'http' + url_base
|
r'(?:(?:https?|rtmp|rtsp):)?(//[^?]+)', url, 'format url')
|
||||||
|
http_base_url = '%s:%s' % ('http', url_base)
|
||||||
formats = []
|
formats = []
|
||||||
if 'm3u8' not in skip_protocols:
|
if 'm3u8' not in skip_protocols:
|
||||||
formats.extend(self._extract_m3u8_formats(
|
formats.extend(self._extract_m3u8_formats(
|
||||||
@@ -2225,7 +2241,7 @@ class InfoExtractor(object):
|
|||||||
for protocol in ('rtmp', 'rtsp'):
|
for protocol in ('rtmp', 'rtsp'):
|
||||||
if protocol not in skip_protocols:
|
if protocol not in skip_protocols:
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': protocol + url_base,
|
'url': '%s:%s' % (protocol, url_base),
|
||||||
'format_id': protocol,
|
'format_id': protocol,
|
||||||
'protocol': protocol,
|
'protocol': protocol,
|
||||||
})
|
})
|
||||||
@@ -2283,6 +2299,8 @@ class InfoExtractor(object):
|
|||||||
tracks = video_data.get('tracks')
|
tracks = video_data.get('tracks')
|
||||||
if tracks and isinstance(tracks, list):
|
if tracks and isinstance(tracks, list):
|
||||||
for track in tracks:
|
for track in tracks:
|
||||||
|
if not isinstance(track, dict):
|
||||||
|
continue
|
||||||
if track.get('kind') != 'captions':
|
if track.get('kind') != 'captions':
|
||||||
continue
|
continue
|
||||||
track_url = urljoin(base_url, track.get('file'))
|
track_url = urljoin(base_url, track.get('file'))
|
||||||
@@ -2312,6 +2330,8 @@ class InfoExtractor(object):
|
|||||||
urls = []
|
urls = []
|
||||||
formats = []
|
formats = []
|
||||||
for source in jwplayer_sources_data:
|
for source in jwplayer_sources_data:
|
||||||
|
if not isinstance(source, dict):
|
||||||
|
continue
|
||||||
source_url = self._proto_relative_url(source.get('file'))
|
source_url = self._proto_relative_url(source.get('file'))
|
||||||
if not source_url:
|
if not source_url:
|
||||||
continue
|
continue
|
||||||
|
@@ -16,7 +16,6 @@ from ..utils import (
|
|||||||
mimetype2ext,
|
mimetype2ext,
|
||||||
orderedSet,
|
orderedSet,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
remove_end,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -50,10 +49,17 @@ class CondeNastIE(InfoExtractor):
|
|||||||
'wmagazine': 'W Magazine',
|
'wmagazine': 'W Magazine',
|
||||||
}
|
}
|
||||||
|
|
||||||
_VALID_URL = r'https?://(?:video|www|player)\.(?P<site>%s)\.com/(?P<type>watch|series|video|embed(?:js)?)/(?P<id>[^/?#]+)' % '|'.join(_SITES.keys())
|
_VALID_URL = r'''(?x)https?://(?:video|www|player(?:-backend)?)\.(?:%s)\.com/
|
||||||
|
(?:
|
||||||
|
(?:
|
||||||
|
embed(?:js)?|
|
||||||
|
(?:script|inline)/video
|
||||||
|
)/(?P<id>[0-9a-f]{24})(?:/(?P<player_id>[0-9a-f]{24}))?(?:.+?\btarget=(?P<target>[^&]+))?|
|
||||||
|
(?P<type>watch|series|video)/(?P<display_id>[^/?#]+)
|
||||||
|
)''' % '|'.join(_SITES.keys())
|
||||||
IE_DESC = 'Condé Nast media group: %s' % ', '.join(sorted(_SITES.values()))
|
IE_DESC = 'Condé Nast media group: %s' % ', '.join(sorted(_SITES.values()))
|
||||||
|
|
||||||
EMBED_URL = r'(?:https?:)?//player\.(?P<site>%s)\.com/(?P<type>embed(?:js)?)/.+?' % '|'.join(_SITES.keys())
|
EMBED_URL = r'(?:https?:)?//player(?:-backend)?\.(?:%s)\.com/(?:embed(?:js)?|(?:script|inline)/video)/.+?' % '|'.join(_SITES.keys())
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://video.wired.com/watch/3d-printed-speakers-lit-with-led',
|
'url': 'http://video.wired.com/watch/3d-printed-speakers-lit-with-led',
|
||||||
@@ -89,6 +95,12 @@ class CondeNastIE(InfoExtractor):
|
|||||||
'upload_date': '20150916',
|
'upload_date': '20150916',
|
||||||
'timestamp': 1442434955,
|
'timestamp': 1442434955,
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
'url': 'https://player.cnevids.com/inline/video/59138decb57ac36b83000005.js?target=js-cne-player',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://player-backend.cnevids.com/script/video/59138decb57ac36b83000005.js',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _extract_series(self, url, webpage):
|
def _extract_series(self, url, webpage):
|
||||||
@@ -104,7 +116,7 @@ class CondeNastIE(InfoExtractor):
|
|||||||
entries = [self.url_result(build_url(path), 'CondeNast') for path in paths]
|
entries = [self.url_result(build_url(path), 'CondeNast') for path in paths]
|
||||||
return self.playlist_result(entries, playlist_title=title)
|
return self.playlist_result(entries, playlist_title=title)
|
||||||
|
|
||||||
def _extract_video(self, webpage, url_type):
|
def _extract_video_params(self, webpage):
|
||||||
query = {}
|
query = {}
|
||||||
params = self._search_regex(
|
params = self._search_regex(
|
||||||
r'(?s)var params = {(.+?)}[;,]', webpage, 'player params', default=None)
|
r'(?s)var params = {(.+?)}[;,]', webpage, 'player params', default=None)
|
||||||
@@ -123,17 +135,30 @@ class CondeNastIE(InfoExtractor):
|
|||||||
'playerId': params['data-player'],
|
'playerId': params['data-player'],
|
||||||
'target': params['id'],
|
'target': params['id'],
|
||||||
})
|
})
|
||||||
video_id = query['videoId']
|
return query
|
||||||
|
|
||||||
|
def _extract_video(self, params):
|
||||||
|
video_id = params['videoId']
|
||||||
|
|
||||||
video_info = None
|
video_info = None
|
||||||
info_page = self._download_json(
|
if params.get('playerId'):
|
||||||
'http://player.cnevids.com/player/video.js',
|
info_page = self._download_json(
|
||||||
video_id, 'Downloading video info', fatal=False, query=query)
|
'http://player.cnevids.com/player/video.js',
|
||||||
if info_page:
|
video_id, 'Downloading video info', fatal=False, query=params)
|
||||||
video_info = info_page.get('video')
|
if info_page:
|
||||||
if not video_info:
|
video_info = info_page.get('video')
|
||||||
|
if not video_info:
|
||||||
|
info_page = self._download_webpage(
|
||||||
|
'http://player.cnevids.com/player/loader.js',
|
||||||
|
video_id, 'Downloading loader info', query=params)
|
||||||
|
else:
|
||||||
info_page = self._download_webpage(
|
info_page = self._download_webpage(
|
||||||
'http://player.cnevids.com/player/loader.js',
|
'https://player.cnevids.com/inline/video/%s.js' % video_id,
|
||||||
video_id, 'Downloading loader info', query=query)
|
video_id, 'Downloading inline info', query={
|
||||||
|
'target': params.get('target', 'embedplayer')
|
||||||
|
})
|
||||||
|
|
||||||
|
if not video_info:
|
||||||
video_info = self._parse_json(
|
video_info = self._parse_json(
|
||||||
self._search_regex(
|
self._search_regex(
|
||||||
r'(?s)var\s+config\s*=\s*({.+?});', info_page, 'config'),
|
r'(?s)var\s+config\s*=\s*({.+?});', info_page, 'config'),
|
||||||
@@ -161,9 +186,7 @@ class CondeNastIE(InfoExtractor):
|
|||||||
})
|
})
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
info = self._search_json_ld(
|
return {
|
||||||
webpage, video_id, fatal=False) if url_type != 'embed' else {}
|
|
||||||
info.update({
|
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
'title': title,
|
'title': title,
|
||||||
@@ -174,22 +197,26 @@ class CondeNastIE(InfoExtractor):
|
|||||||
'series': video_info.get('series_title'),
|
'series': video_info.get('series_title'),
|
||||||
'season': video_info.get('season_title'),
|
'season': video_info.get('season_title'),
|
||||||
'timestamp': parse_iso8601(video_info.get('premiere_date')),
|
'timestamp': parse_iso8601(video_info.get('premiere_date')),
|
||||||
})
|
'categories': video_info.get('categories'),
|
||||||
return info
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
site, url_type, item_id = re.match(self._VALID_URL, url).groups()
|
video_id, player_id, target, url_type, display_id = re.match(self._VALID_URL, url).groups()
|
||||||
|
|
||||||
# Convert JS embed to regular embed
|
if video_id:
|
||||||
if url_type == 'embedjs':
|
return self._extract_video({
|
||||||
parsed_url = compat_urlparse.urlparse(url)
|
'videoId': video_id,
|
||||||
url = compat_urlparse.urlunparse(parsed_url._replace(
|
'playerId': player_id,
|
||||||
path=remove_end(parsed_url.path, '.js').replace('/embedjs/', '/embed/')))
|
'target': target,
|
||||||
url_type = 'embed'
|
})
|
||||||
|
|
||||||
webpage = self._download_webpage(url, item_id)
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
|
||||||
if url_type == 'series':
|
if url_type == 'series':
|
||||||
return self._extract_series(url, webpage)
|
return self._extract_series(url, webpage)
|
||||||
else:
|
else:
|
||||||
return self._extract_video(webpage, url_type)
|
params = self._extract_video_params(webpage)
|
||||||
|
info = self._search_json_ld(
|
||||||
|
webpage, display_id, fatal=False)
|
||||||
|
info.update(self._extract_video(params))
|
||||||
|
return info
|
||||||
|
@@ -8,7 +8,16 @@ from ..utils import int_or_none
|
|||||||
|
|
||||||
|
|
||||||
class CorusIE(ThePlatformFeedIE):
|
class CorusIE(ThePlatformFeedIE):
|
||||||
_VALID_URL = r'https?://(?:www\.)?(?P<domain>(?:globaltv|etcanada)\.com|(?:hgtv|foodnetwork|slice)\.ca)/(?:video/|(?:[^/]+/)+(?:videos/[a-z0-9-]+-|video\.html\?.*?\bv=))(?P<id>\d+)'
|
_VALID_URL = r'''(?x)
|
||||||
|
https?://
|
||||||
|
(?:www\.)?
|
||||||
|
(?P<domain>
|
||||||
|
(?:globaltv|etcanada)\.com|
|
||||||
|
(?:hgtv|foodnetwork|slice|history|showcase)\.ca
|
||||||
|
)
|
||||||
|
/(?:video/|(?:[^/]+/)+(?:videos/[a-z0-9-]+-|video\.html\?.*?\bv=))
|
||||||
|
(?P<id>\d+)
|
||||||
|
'''
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.hgtv.ca/shows/bryan-inc/videos/movie-night-popcorn-with-bryan-870923331648/',
|
'url': 'http://www.hgtv.ca/shows/bryan-inc/videos/movie-night-popcorn-with-bryan-870923331648/',
|
||||||
'md5': '05dcbca777bf1e58c2acbb57168ad3a6',
|
'md5': '05dcbca777bf1e58c2acbb57168ad3a6',
|
||||||
@@ -27,6 +36,12 @@ class CorusIE(ThePlatformFeedIE):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://etcanada.com/video/873675331955/meet-the-survivor-game-changers-castaways-part-2/',
|
'url': 'http://etcanada.com/video/873675331955/meet-the-survivor-game-changers-castaways-part-2/',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.history.ca/the-world-without-canada/video/full-episodes/natural-resources/video.html?v=955054659646#video',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.showcase.ca/eyewitness/video/eyewitness++106/video.html?v=955070531919&p=1&s=da#video',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
_TP_FEEDS = {
|
_TP_FEEDS = {
|
||||||
@@ -50,6 +65,14 @@ class CorusIE(ThePlatformFeedIE):
|
|||||||
'feed_id': '5tUJLgV2YNJ5',
|
'feed_id': '5tUJLgV2YNJ5',
|
||||||
'account_id': 2414427935,
|
'account_id': 2414427935,
|
||||||
},
|
},
|
||||||
|
'history': {
|
||||||
|
'feed_id': 'tQFx_TyyEq4J',
|
||||||
|
'account_id': 2369613659,
|
||||||
|
},
|
||||||
|
'showcase': {
|
||||||
|
'feed_id': '9H6qyshBZU3E',
|
||||||
|
'account_id': 2414426607,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
@@ -24,12 +24,11 @@ class CoubIE(InfoExtractor):
|
|||||||
'duration': 4.6,
|
'duration': 4.6,
|
||||||
'timestamp': 1428527772,
|
'timestamp': 1428527772,
|
||||||
'upload_date': '20150408',
|
'upload_date': '20150408',
|
||||||
'uploader': 'Артём Лоскутников',
|
'uploader': 'Artyom Loskutnikov',
|
||||||
'uploader_id': 'artyom.loskutnikov',
|
'uploader_id': 'artyom.loskutnikov',
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
'like_count': int,
|
'like_count': int,
|
||||||
'repost_count': int,
|
'repost_count': int,
|
||||||
'comment_count': int,
|
|
||||||
'age_limit': 0,
|
'age_limit': 0,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
@@ -118,7 +117,6 @@ class CoubIE(InfoExtractor):
|
|||||||
view_count = int_or_none(coub.get('views_count') or coub.get('views_increase_count'))
|
view_count = int_or_none(coub.get('views_count') or coub.get('views_increase_count'))
|
||||||
like_count = int_or_none(coub.get('likes_count'))
|
like_count = int_or_none(coub.get('likes_count'))
|
||||||
repost_count = int_or_none(coub.get('recoubs_count'))
|
repost_count = int_or_none(coub.get('recoubs_count'))
|
||||||
comment_count = int_or_none(coub.get('comments_count'))
|
|
||||||
|
|
||||||
age_restricted = coub.get('age_restricted', coub.get('age_restricted_by_admin'))
|
age_restricted = coub.get('age_restricted', coub.get('age_restricted_by_admin'))
|
||||||
if age_restricted is not None:
|
if age_restricted is not None:
|
||||||
@@ -137,7 +135,6 @@ class CoubIE(InfoExtractor):
|
|||||||
'view_count': view_count,
|
'view_count': view_count,
|
||||||
'like_count': like_count,
|
'like_count': like_count,
|
||||||
'repost_count': repost_count,
|
'repost_count': repost_count,
|
||||||
'comment_count': comment_count,
|
|
||||||
'age_limit': age_limit,
|
'age_limit': age_limit,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
}
|
}
|
||||||
|
@@ -21,9 +21,10 @@ class CrackleIE(InfoExtractor):
|
|||||||
'season_number': 8,
|
'season_number': 8,
|
||||||
'episode_number': 4,
|
'episode_number': 4,
|
||||||
'subtitles': {
|
'subtitles': {
|
||||||
'en-US': [{
|
'en-US': [
|
||||||
'ext': 'ttml',
|
{'ext': 'vtt'},
|
||||||
}]
|
{'ext': 'tt'},
|
||||||
|
]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
|
@@ -171,7 +171,7 @@ class CrunchyrollIE(CrunchyrollBaseIE):
|
|||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '727589',
|
'id': '727589',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': "KONOSUBA -God's blessing on this wonderful world! 2 Episode 1 – Give Me Deliverance from this Judicial Injustice!",
|
'title': "KONOSUBA -God's blessing on this wonderful world! 2 Episode 1 – Give Me Deliverance From This Judicial Injustice!",
|
||||||
'description': 'md5:cbcf05e528124b0f3a0a419fc805ea7d',
|
'description': 'md5:cbcf05e528124b0f3a0a419fc805ea7d',
|
||||||
'thumbnail': r're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'uploader': 'Kadokawa Pictures Inc.',
|
'uploader': 'Kadokawa Pictures Inc.',
|
||||||
@@ -179,7 +179,7 @@ class CrunchyrollIE(CrunchyrollBaseIE):
|
|||||||
'series': "KONOSUBA -God's blessing on this wonderful world!",
|
'series': "KONOSUBA -God's blessing on this wonderful world!",
|
||||||
'season': "KONOSUBA -God's blessing on this wonderful world! 2",
|
'season': "KONOSUBA -God's blessing on this wonderful world! 2",
|
||||||
'season_number': 2,
|
'season_number': 2,
|
||||||
'episode': 'Give Me Deliverance from this Judicial Injustice!',
|
'episode': 'Give Me Deliverance From This Judicial Injustice!',
|
||||||
'episode_number': 1,
|
'episode_number': 1,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
|
@@ -10,6 +10,7 @@ from ..utils import (
|
|||||||
smuggle_url,
|
smuggle_url,
|
||||||
determine_ext,
|
determine_ext,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
|
extract_attributes,
|
||||||
)
|
)
|
||||||
from .senateisvp import SenateISVPIE
|
from .senateisvp import SenateISVPIE
|
||||||
from .ustream import UstreamIE
|
from .ustream import UstreamIE
|
||||||
@@ -68,6 +69,7 @@ class CSpanIE(InfoExtractor):
|
|||||||
'uploader_id': '12987475',
|
'uploader_id': '12987475',
|
||||||
},
|
},
|
||||||
}]
|
}]
|
||||||
|
BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/%s/%s_%s/index.html?videoId=%s'
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
@@ -78,6 +80,19 @@ class CSpanIE(InfoExtractor):
|
|||||||
if ustream_url:
|
if ustream_url:
|
||||||
return self.url_result(ustream_url, UstreamIE.ie_key())
|
return self.url_result(ustream_url, UstreamIE.ie_key())
|
||||||
|
|
||||||
|
if '&vod' not in url:
|
||||||
|
bc = self._search_regex(
|
||||||
|
r"(<[^>]+id='brightcove-player-embed'[^>]+>)",
|
||||||
|
webpage, 'brightcove embed', default=None)
|
||||||
|
if bc:
|
||||||
|
bc_attr = extract_attributes(bc)
|
||||||
|
bc_url = self.BRIGHTCOVE_URL_TEMPLATE % (
|
||||||
|
bc_attr.get('data-bcaccountid', '3162030207001'),
|
||||||
|
bc_attr.get('data-noprebcplayerid', 'SyGGpuJy3g'),
|
||||||
|
bc_attr.get('data-newbcplayerid', 'default'),
|
||||||
|
bc_attr['data-bcid'])
|
||||||
|
return self.url_result(smuggle_url(bc_url, {'source_url': url}))
|
||||||
|
|
||||||
# We first look for clipid, because clipprog always appears before
|
# We first look for clipid, because clipprog always appears before
|
||||||
patterns = [r'id=\'clip(%s)\'\s*value=\'([0-9]+)\'' % t for t in ('id', 'prog')]
|
patterns = [r'id=\'clip(%s)\'\s*value=\'([0-9]+)\'' % t for t in ('id', 'prog')]
|
||||||
results = list(filter(None, (re.search(p, webpage) for p in patterns)))
|
results = list(filter(None, (re.search(p, webpage) for p in patterns)))
|
||||||
|
@@ -2,9 +2,11 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..compat import compat_str
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
int_or_none,
|
int_or_none,
|
||||||
determine_protocol,
|
determine_protocol,
|
||||||
|
try_get,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,8 +30,14 @@ class DailyMailIE(InfoExtractor):
|
|||||||
video_data = self._parse_json(self._search_regex(
|
video_data = self._parse_json(self._search_regex(
|
||||||
r"data-opts='({.+?})'", webpage, 'video data'), video_id)
|
r"data-opts='({.+?})'", webpage, 'video data'), video_id)
|
||||||
title = unescapeHTML(video_data['title'])
|
title = unescapeHTML(video_data['title'])
|
||||||
video_sources = self._download_json(video_data.get(
|
|
||||||
'sources', {}).get('url') or 'http://www.dailymail.co.uk/api/player/%s/video-sources.json' % video_id, video_id)
|
sources_url = (try_get(
|
||||||
|
video_data,
|
||||||
|
(lambda x: x['plugins']['sources']['url'],
|
||||||
|
lambda x: x['sources']['url']), compat_str) or
|
||||||
|
'http://www.dailymail.co.uk/api/player/%s/video-sources.json' % video_id)
|
||||||
|
|
||||||
|
video_sources = self._download_json(sources_url, video_id)
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for rendition in video_sources['renditions']:
|
for rendition in video_sources['renditions']:
|
||||||
|
@@ -38,7 +38,7 @@ class DailymotionBaseInfoExtractor(InfoExtractor):
|
|||||||
|
|
||||||
|
|
||||||
class DailymotionIE(DailymotionBaseInfoExtractor):
|
class DailymotionIE(DailymotionBaseInfoExtractor):
|
||||||
_VALID_URL = r'(?i)(?:https?://)?(?:(www|touch)\.)?dailymotion\.[a-z]{2,3}/(?:(?:embed|swf|#)/)?video/(?P<id>[^/?_]+)'
|
_VALID_URL = r'(?i)https?://(?:(www|touch)\.)?dailymotion\.[a-z]{2,3}/(?:(?:(?:embed|swf|#)/)?video|swf)/(?P<id>[^/?_]+)'
|
||||||
IE_NAME = 'dailymotion'
|
IE_NAME = 'dailymotion'
|
||||||
|
|
||||||
_FORMATS = [
|
_FORMATS = [
|
||||||
@@ -49,68 +49,82 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|||||||
('stream_h264_hd1080_url', 'hd180'),
|
('stream_h264_hd1080_url', 'hd180'),
|
||||||
]
|
]
|
||||||
|
|
||||||
_TESTS = [
|
_TESTS = [{
|
||||||
{
|
'url': 'http://www.dailymotion.com/video/x5kesuj_office-christmas-party-review-jason-bateman-olivia-munn-t-j-miller_news',
|
||||||
'url': 'https://www.dailymotion.com/video/x2iuewm_steam-machine-models-pricing-listed-on-steam-store-ign-news_videogames',
|
'md5': '074b95bdee76b9e3654137aee9c79dfe',
|
||||||
'md5': '2137c41a8e78554bb09225b8eb322406',
|
'info_dict': {
|
||||||
'info_dict': {
|
'id': 'x5kesuj',
|
||||||
'id': 'x2iuewm',
|
'ext': 'mp4',
|
||||||
'ext': 'mp4',
|
'title': 'Office Christmas Party Review – Jason Bateman, Olivia Munn, T.J. Miller',
|
||||||
'title': 'Steam Machine Models, Pricing Listed on Steam Store - IGN News',
|
'description': 'Office Christmas Party Review - Jason Bateman, Olivia Munn, T.J. Miller',
|
||||||
'description': 'Several come bundled with the Steam Controller.',
|
'thumbnail': r're:^https?:.*\.(?:jpg|png)$',
|
||||||
'thumbnail': r're:^https?:.*\.(?:jpg|png)$',
|
'duration': 187,
|
||||||
'duration': 74,
|
'timestamp': 1493651285,
|
||||||
'timestamp': 1425657362,
|
'upload_date': '20170501',
|
||||||
'upload_date': '20150306',
|
'uploader': 'Deadline',
|
||||||
'uploader': 'IGN',
|
'uploader_id': 'x1xm8ri',
|
||||||
'uploader_id': 'xijv66',
|
'age_limit': 0,
|
||||||
'age_limit': 0,
|
'view_count': int,
|
||||||
'view_count': int,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.dailymotion.com/video/x2iuewm_steam-machine-models-pricing-listed-on-steam-store-ign-news_videogames',
|
||||||
|
'md5': '2137c41a8e78554bb09225b8eb322406',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'x2iuewm',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Steam Machine Models, Pricing Listed on Steam Store - IGN News',
|
||||||
|
'description': 'Several come bundled with the Steam Controller.',
|
||||||
|
'thumbnail': r're:^https?:.*\.(?:jpg|png)$',
|
||||||
|
'duration': 74,
|
||||||
|
'timestamp': 1425657362,
|
||||||
|
'upload_date': '20150306',
|
||||||
|
'uploader': 'IGN',
|
||||||
|
'uploader_id': 'xijv66',
|
||||||
|
'age_limit': 0,
|
||||||
|
'view_count': int,
|
||||||
|
},
|
||||||
|
'skip': 'video gone',
|
||||||
|
}, {
|
||||||
# Vevo video
|
# Vevo video
|
||||||
{
|
'url': 'http://www.dailymotion.com/video/x149uew_katy-perry-roar-official_musi',
|
||||||
'url': 'http://www.dailymotion.com/video/x149uew_katy-perry-roar-official_musi',
|
'info_dict': {
|
||||||
'info_dict': {
|
'title': 'Roar (Official)',
|
||||||
'title': 'Roar (Official)',
|
'id': 'USUV71301934',
|
||||||
'id': 'USUV71301934',
|
'ext': 'mp4',
|
||||||
'ext': 'mp4',
|
'uploader': 'Katy Perry',
|
||||||
'uploader': 'Katy Perry',
|
'upload_date': '20130905',
|
||||||
'upload_date': '20130905',
|
|
||||||
},
|
|
||||||
'params': {
|
|
||||||
'skip_download': True,
|
|
||||||
},
|
|
||||||
'skip': 'VEVO is only available in some countries',
|
|
||||||
},
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
'skip': 'VEVO is only available in some countries',
|
||||||
|
}, {
|
||||||
# age-restricted video
|
# age-restricted video
|
||||||
{
|
'url': 'http://www.dailymotion.com/video/xyh2zz_leanna-decker-cyber-girl-of-the-year-desires-nude-playboy-plus_redband',
|
||||||
'url': 'http://www.dailymotion.com/video/xyh2zz_leanna-decker-cyber-girl-of-the-year-desires-nude-playboy-plus_redband',
|
'md5': '0d667a7b9cebecc3c89ee93099c4159d',
|
||||||
'md5': '0d667a7b9cebecc3c89ee93099c4159d',
|
'info_dict': {
|
||||||
'info_dict': {
|
'id': 'xyh2zz',
|
||||||
'id': 'xyh2zz',
|
'ext': 'mp4',
|
||||||
'ext': 'mp4',
|
'title': 'Leanna Decker - Cyber Girl Of The Year Desires Nude [Playboy Plus]',
|
||||||
'title': 'Leanna Decker - Cyber Girl Of The Year Desires Nude [Playboy Plus]',
|
'uploader': 'HotWaves1012',
|
||||||
'uploader': 'HotWaves1012',
|
'age_limit': 18,
|
||||||
'age_limit': 18,
|
|
||||||
},
|
|
||||||
'skip': 'video gone',
|
|
||||||
},
|
},
|
||||||
|
'skip': 'video gone',
|
||||||
|
}, {
|
||||||
# geo-restricted, player v5
|
# geo-restricted, player v5
|
||||||
{
|
'url': 'http://www.dailymotion.com/video/xhza0o',
|
||||||
'url': 'http://www.dailymotion.com/video/xhza0o',
|
'only_matching': True,
|
||||||
'only_matching': True,
|
}, {
|
||||||
},
|
|
||||||
# with subtitles
|
# with subtitles
|
||||||
{
|
'url': 'http://www.dailymotion.com/video/x20su5f_the-power-of-nightmares-1-the-rise-of-the-politics-of-fear-bbc-2004_news',
|
||||||
'url': 'http://www.dailymotion.com/video/x20su5f_the-power-of-nightmares-1-the-rise-of-the-politics-of-fear-bbc-2004_news',
|
'only_matching': True,
|
||||||
'only_matching': True,
|
}, {
|
||||||
},
|
'url': 'http://www.dailymotion.com/swf/video/x3n92nf',
|
||||||
{
|
'only_matching': True,
|
||||||
'url': 'http://www.dailymotion.com/swf/video/x3n92nf',
|
}, {
|
||||||
'only_matching': True,
|
'url': 'http://www.dailymotion.com/swf/x3ss1m_funny-magic-trick-barry-and-stuart_fun',
|
||||||
}
|
'only_matching': True,
|
||||||
]
|
}]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _extract_urls(webpage):
|
def _extract_urls(webpage):
|
||||||
|
@@ -21,7 +21,8 @@ class DemocracynowIE(InfoExtractor):
|
|||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '2015-0703-001',
|
'id': '2015-0703-001',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Daily Show',
|
'title': 'Daily Show for July 03, 2015',
|
||||||
|
'description': 'md5:80eb927244d6749900de6072c7cc2c86',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.democracynow.org/2015/7/3/this_flag_comes_down_today_bree',
|
'url': 'http://www.democracynow.org/2015/7/3/this_flag_comes_down_today_bree',
|
||||||
|
@@ -15,7 +15,7 @@ from ..utils import (
|
|||||||
|
|
||||||
class DisneyIE(InfoExtractor):
|
class DisneyIE(InfoExtractor):
|
||||||
_VALID_URL = r'''(?x)
|
_VALID_URL = r'''(?x)
|
||||||
https?://(?P<domain>(?:[^/]+\.)?(?:disney\.[a-z]{2,3}(?:\.[a-z]{2})?|disney(?:(?:me|latino)\.com|turkiye\.com\.tr)|(?:starwars|marvelkids)\.com))/(?:(?:embed/|(?:[^/]+/)+[\w-]+-)(?P<id>[a-z0-9]{24})|(?:[^/]+/)?(?P<display_id>[^/?#]+))'''
|
https?://(?P<domain>(?:[^/]+\.)?(?:disney\.[a-z]{2,3}(?:\.[a-z]{2})?|disney(?:(?:me|latino)\.com|turkiye\.com\.tr|channel\.de)|(?:starwars|marvelkids)\.com))/(?:(?:embed/|(?:[^/]+/)+[\w-]+-)(?P<id>[a-z0-9]{24})|(?:[^/]+/)?(?P<display_id>[^/?#]+))'''
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
# Disney.EmbedVideo
|
# Disney.EmbedVideo
|
||||||
'url': 'http://video.disney.com/watch/moana-trailer-545ed1857afee5a0ec239977',
|
'url': 'http://video.disney.com/watch/moana-trailer-545ed1857afee5a0ec239977',
|
||||||
@@ -68,6 +68,9 @@ class DisneyIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://disneyjunior.en.disneyme.com/dj/watch-my-friends-tigger-and-pooh-promo',
|
'url': 'http://disneyjunior.en.disneyme.com/dj/watch-my-friends-tigger-and-pooh-promo',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://disneychannel.de/sehen/soy-luna-folge-118-5518518987ba27f3cc729268',
|
||||||
|
'only_matching': True,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://disneyjunior.disney.com/galactech-the-galactech-grab-galactech-an-admiral-rescue',
|
'url': 'http://disneyjunior.disney.com/galactech-the-galactech-grab-galactech-an-admiral-rescue',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
@@ -35,7 +35,7 @@ class DotsubIE(InfoExtractor):
|
|||||||
'thumbnail': 're:^https?://dotsub.com/media/747bcf58-bd59-45b7-8c8c-ac312d084ee6/p',
|
'thumbnail': 're:^https?://dotsub.com/media/747bcf58-bd59-45b7-8c8c-ac312d084ee6/p',
|
||||||
'duration': 290,
|
'duration': 290,
|
||||||
'timestamp': 1476767794.2809999,
|
'timestamp': 1476767794.2809999,
|
||||||
'upload_date': '20160525',
|
'upload_date': '20161018',
|
||||||
'uploader': 'parthivi001',
|
'uploader': 'parthivi001',
|
||||||
'uploader_id': 'user52596202',
|
'uploader_id': 'user52596202',
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
|
@@ -3,11 +3,14 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
|
unified_strdate,
|
||||||
|
urljoin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -20,7 +23,7 @@ class DouyuTVIE(InfoExtractor):
|
|||||||
'id': '17732',
|
'id': '17732',
|
||||||
'display_id': 'iseven',
|
'display_id': 'iseven',
|
||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
'title': 're:^清晨醒脑!T-ARA根本停不下来! [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
|
'title': 're:^清晨醒脑!根本停不下来! [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
|
||||||
'description': r're:.*m7show@163\.com.*',
|
'description': r're:.*m7show@163\.com.*',
|
||||||
'thumbnail': r're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'uploader': '7师傅',
|
'uploader': '7师傅',
|
||||||
@@ -51,7 +54,7 @@ class DouyuTVIE(InfoExtractor):
|
|||||||
'id': '17732',
|
'id': '17732',
|
||||||
'display_id': '17732',
|
'display_id': '17732',
|
||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
'title': 're:^清晨醒脑!T-ARA根本停不下来! [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
|
'title': 're:^清晨醒脑!根本停不下来! [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
|
||||||
'description': r're:.*m7show@163\.com.*',
|
'description': r're:.*m7show@163\.com.*',
|
||||||
'thumbnail': r're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'uploader': '7师傅',
|
'uploader': '7师傅',
|
||||||
@@ -117,3 +120,82 @@ class DouyuTVIE(InfoExtractor):
|
|||||||
'uploader': uploader,
|
'uploader': uploader,
|
||||||
'is_live': True,
|
'is_live': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DouyuShowIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://v(?:mobile)?\.douyu\.com/show/(?P<id>[0-9a-zA-Z]+)'
|
||||||
|
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://v.douyu.com/show/rjNBdvnVXNzvE2yw',
|
||||||
|
'md5': '0c2cfd068ee2afe657801269b2d86214',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'rjNBdvnVXNzvE2yw',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': '陈一发儿:砒霜 我有个室友系列!04-01 22点场',
|
||||||
|
'duration': 7150.08,
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
'uploader': '陈一发儿',
|
||||||
|
'uploader_id': 'XrZwYelr5wbK',
|
||||||
|
'uploader_url': 'https://v.douyu.com/author/XrZwYelr5wbK',
|
||||||
|
'upload_date': '20170402',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://vmobile.douyu.com/show/rjNBdvnVXNzvE2yw',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
url = url.replace('vmobile.', 'v.')
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
|
room_info = self._parse_json(self._search_regex(
|
||||||
|
r'var\s+\$ROOM\s*=\s*({.+});', webpage, 'room info'), video_id)
|
||||||
|
|
||||||
|
video_info = None
|
||||||
|
|
||||||
|
for trial in range(5):
|
||||||
|
# Sometimes Douyu rejects our request. Let's try it more times
|
||||||
|
try:
|
||||||
|
video_info = self._download_json(
|
||||||
|
'https://vmobile.douyu.com/video/getInfo', video_id,
|
||||||
|
query={'vid': video_id},
|
||||||
|
headers={
|
||||||
|
'Referer': url,
|
||||||
|
'x-requested-with': 'XMLHttpRequest',
|
||||||
|
})
|
||||||
|
break
|
||||||
|
except ExtractorError:
|
||||||
|
self._sleep(1, video_id)
|
||||||
|
|
||||||
|
if not video_info:
|
||||||
|
raise ExtractorError('Can\'t fetch video info')
|
||||||
|
|
||||||
|
formats = self._extract_m3u8_formats(
|
||||||
|
video_info['data']['video_url'], video_id,
|
||||||
|
entry_protocol='m3u8_native', ext='mp4')
|
||||||
|
|
||||||
|
upload_date = unified_strdate(self._html_search_regex(
|
||||||
|
r'<em>上传时间:</em><span>([^<]+)</span>', webpage,
|
||||||
|
'upload date', fatal=False))
|
||||||
|
|
||||||
|
uploader = uploader_id = uploader_url = None
|
||||||
|
mobj = re.search(
|
||||||
|
r'(?m)<a[^>]+href="/author/([0-9a-zA-Z]+)".+?<strong[^>]+title="([^"]+)"',
|
||||||
|
webpage)
|
||||||
|
if mobj:
|
||||||
|
uploader_id, uploader = mobj.groups()
|
||||||
|
uploader_url = urljoin(url, '/author/' + uploader_id)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': room_info['name'],
|
||||||
|
'formats': formats,
|
||||||
|
'duration': room_info.get('duration'),
|
||||||
|
'thumbnail': room_info.get('pic'),
|
||||||
|
'upload_date': upload_date,
|
||||||
|
'uploader': uploader,
|
||||||
|
'uploader_id': uploader_id,
|
||||||
|
'uploader_url': uploader_url,
|
||||||
|
}
|
||||||
|
@@ -1,135 +1,59 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import json
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
int_or_none,
|
js_to_json,
|
||||||
parse_iso8601,
|
parse_duration,
|
||||||
|
unescapeHTML,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DRBonanzaIE(InfoExtractor):
|
class DRBonanzaIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?dr\.dk/bonanza/(?:[^/]+/)+(?:[^/])+?(?:assetId=(?P<id>\d+))?(?:[#&]|$)'
|
_VALID_URL = r'https?://(?:www\.)?dr\.dk/bonanza/[^/]+/\d+/[^/]+/(?P<id>\d+)/(?P<display_id>[^/?#&]+)'
|
||||||
|
_TEST = {
|
||||||
_TESTS = [{
|
'url': 'http://www.dr.dk/bonanza/serie/154/matador/40312/matador---0824-komme-fremmede-',
|
||||||
'url': 'http://www.dr.dk/bonanza/serie/portraetter/Talkshowet.htm?assetId=65517',
|
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '65517',
|
'id': '40312',
|
||||||
|
'display_id': 'matador---0824-komme-fremmede-',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Talkshowet - Leonard Cohen',
|
'title': 'MATADOR - 08:24. "Komme fremmede".',
|
||||||
'description': 'md5:8f34194fb30cd8c8a30ad8b27b70c0ca',
|
'description': 'md5:77b4c1ac4d4c1b9d610ab4395212ff84',
|
||||||
'thumbnail': r're:^https?://.*\.(?:gif|jpg)$',
|
'thumbnail': r're:^https?://.*\.(?:gif|jpg)$',
|
||||||
'timestamp': 1295537932,
|
'duration': 4613,
|
||||||
'upload_date': '20110120',
|
|
||||||
'duration': 3664,
|
|
||||||
},
|
},
|
||||||
'params': {
|
}
|
||||||
'skip_download': True, # requires rtmp
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
'url': 'http://www.dr.dk/bonanza/radio/serie/sport/fodbold.htm?assetId=59410',
|
|
||||||
'md5': '6dfe039417e76795fb783c52da3de11d',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '59410',
|
|
||||||
'ext': 'mp3',
|
|
||||||
'title': 'EM fodbold 1992 Danmark - Tyskland finale Transmission',
|
|
||||||
'description': 'md5:501e5a195749480552e214fbbed16c4e',
|
|
||||||
'thumbnail': r're:^https?://.*\.(?:gif|jpg)$',
|
|
||||||
'timestamp': 1223274900,
|
|
||||||
'upload_date': '20081006',
|
|
||||||
'duration': 7369,
|
|
||||||
},
|
|
||||||
}]
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
url_id = self._match_id(url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
webpage = self._download_webpage(url, url_id)
|
video_id, display_id = mobj.group('id', 'display_id')
|
||||||
|
|
||||||
if url_id:
|
webpage = self._download_webpage(url, display_id)
|
||||||
info = json.loads(self._html_search_regex(r'({.*?%s.*})' % url_id, webpage, 'json'))
|
|
||||||
else:
|
|
||||||
# Just fetch the first video on that page
|
|
||||||
info = json.loads(self._html_search_regex(r'bonanzaFunctions.newPlaylist\(({.*})\)', webpage, 'json'))
|
|
||||||
|
|
||||||
asset_id = str(info['AssetId'])
|
info = self._parse_html5_media_entries(
|
||||||
title = info['Title'].rstrip(' \'\"-,.:;!?')
|
url, webpage, display_id, m3u8_id='hls',
|
||||||
duration = int_or_none(info.get('Duration'), scale=1000)
|
m3u8_entry_protocol='m3u8_native')[0]
|
||||||
# First published online. "FirstPublished" contains the date for original airing.
|
self._sort_formats(info['formats'])
|
||||||
timestamp = parse_iso8601(
|
|
||||||
re.sub(r'\.\d+$', '', info['Created']))
|
|
||||||
|
|
||||||
def parse_filename_info(url):
|
asset = self._parse_json(
|
||||||
match = re.search(r'/\d+_(?P<width>\d+)x(?P<height>\d+)x(?P<bitrate>\d+)K\.(?P<ext>\w+)$', url)
|
self._search_regex(
|
||||||
if match:
|
r'(?s)currentAsset\s*=\s*({.+?})\s*</script', webpage, 'asset'),
|
||||||
return {
|
display_id, transform_source=js_to_json)
|
||||||
'width': int(match.group('width')),
|
|
||||||
'height': int(match.group('height')),
|
|
||||||
'vbr': int(match.group('bitrate')),
|
|
||||||
'ext': match.group('ext')
|
|
||||||
}
|
|
||||||
match = re.search(r'/\d+_(?P<bitrate>\d+)K\.(?P<ext>\w+)$', url)
|
|
||||||
if match:
|
|
||||||
return {
|
|
||||||
'vbr': int(match.group('bitrate')),
|
|
||||||
'ext': match.group(2)
|
|
||||||
}
|
|
||||||
return {}
|
|
||||||
|
|
||||||
video_types = ['VideoHigh', 'VideoMid', 'VideoLow']
|
title = unescapeHTML(asset['AssetTitle']).strip()
|
||||||
preferencemap = {
|
|
||||||
'VideoHigh': -1,
|
|
||||||
'VideoMid': -2,
|
|
||||||
'VideoLow': -3,
|
|
||||||
'Audio': -4,
|
|
||||||
}
|
|
||||||
|
|
||||||
formats = []
|
def extract(field):
|
||||||
for file in info['Files']:
|
return self._search_regex(
|
||||||
if info['Type'] == 'Video':
|
r'<div[^>]+>\s*<p>%s:<p>\s*</div>\s*<div[^>]+>\s*<p>([^<]+)</p>' % field,
|
||||||
if file['Type'] in video_types:
|
webpage, field, default=None)
|
||||||
format = parse_filename_info(file['Location'])
|
|
||||||
format.update({
|
|
||||||
'url': file['Location'],
|
|
||||||
'format_id': file['Type'].replace('Video', ''),
|
|
||||||
'preference': preferencemap.get(file['Type'], -10),
|
|
||||||
})
|
|
||||||
if format['url'].startswith('rtmp'):
|
|
||||||
rtmp_url = format['url']
|
|
||||||
format['rtmp_live'] = True # --resume does not work
|
|
||||||
if '/bonanza/' in rtmp_url:
|
|
||||||
format['play_path'] = rtmp_url.split('/bonanza/')[1]
|
|
||||||
formats.append(format)
|
|
||||||
elif file['Type'] == 'Thumb':
|
|
||||||
thumbnail = file['Location']
|
|
||||||
elif info['Type'] == 'Audio':
|
|
||||||
if file['Type'] == 'Audio':
|
|
||||||
format = parse_filename_info(file['Location'])
|
|
||||||
format.update({
|
|
||||||
'url': file['Location'],
|
|
||||||
'format_id': file['Type'],
|
|
||||||
'vcodec': 'none',
|
|
||||||
})
|
|
||||||
formats.append(format)
|
|
||||||
elif file['Type'] == 'Thumb':
|
|
||||||
thumbnail = file['Location']
|
|
||||||
|
|
||||||
description = '%s\n%s\n%s\n' % (
|
info.update({
|
||||||
info['Description'], info['Actors'], info['Colophon'])
|
'id': asset.get('AssetId') or video_id,
|
||||||
|
|
||||||
self._sort_formats(formats)
|
|
||||||
|
|
||||||
display_id = re.sub(r'[^\w\d-]', '', re.sub(r' ', '-', title.lower())) + '-' + asset_id
|
|
||||||
display_id = re.sub(r'-+', '-', display_id)
|
|
||||||
|
|
||||||
return {
|
|
||||||
'id': asset_id,
|
|
||||||
'display_id': display_id,
|
'display_id': display_id,
|
||||||
'title': title,
|
'title': title,
|
||||||
'formats': formats,
|
'description': extract('Programinfo'),
|
||||||
'description': description,
|
'duration': parse_duration(extract('Tid')),
|
||||||
'thumbnail': thumbnail,
|
'thumbnail': asset.get('AssetImageUrl'),
|
||||||
'timestamp': timestamp,
|
})
|
||||||
'duration': duration,
|
return info
|
||||||
}
|
|
||||||
|
@@ -44,8 +44,23 @@ class DrTuberIE(InfoExtractor):
|
|||||||
webpage = self._download_webpage(
|
webpage = self._download_webpage(
|
||||||
'http://www.drtuber.com/video/%s' % video_id, display_id)
|
'http://www.drtuber.com/video/%s' % video_id, display_id)
|
||||||
|
|
||||||
video_url = self._html_search_regex(
|
video_data = self._download_json(
|
||||||
r'<source src="([^"]+)"', webpage, 'video URL')
|
'http://www.drtuber.com/player_config_json/', video_id, query={
|
||||||
|
'vid': video_id,
|
||||||
|
'embed': 0,
|
||||||
|
'aid': 0,
|
||||||
|
'domain_id': 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
formats = []
|
||||||
|
for format_id, video_url in video_data['files'].items():
|
||||||
|
if video_url:
|
||||||
|
formats.append({
|
||||||
|
'format_id': format_id,
|
||||||
|
'quality': 2 if format_id == 'hq' else 1,
|
||||||
|
'url': video_url
|
||||||
|
})
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
title = self._html_search_regex(
|
title = self._html_search_regex(
|
||||||
(r'class="title_watch"[^>]*><(?:p|h\d+)[^>]*>([^<]+)<',
|
(r'class="title_watch"[^>]*><(?:p|h\d+)[^>]*>([^<]+)<',
|
||||||
@@ -75,7 +90,7 @@ class DrTuberIE(InfoExtractor):
|
|||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'display_id': display_id,
|
'display_id': display_id,
|
||||||
'url': video_url,
|
'formats': formats,
|
||||||
'title': title,
|
'title': title,
|
||||||
'thumbnail': thumbnail,
|
'thumbnail': thumbnail,
|
||||||
'like_count': like_count,
|
'like_count': like_count,
|
||||||
|
@@ -20,7 +20,7 @@ class DRTVIE(InfoExtractor):
|
|||||||
IE_NAME = 'drtv'
|
IE_NAME = 'drtv'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'https://www.dr.dk/tv/se/boern/ultra/klassen-ultra/klassen-darlig-taber-10',
|
'url': 'https://www.dr.dk/tv/se/boern/ultra/klassen-ultra/klassen-darlig-taber-10',
|
||||||
'md5': '25e659cccc9a2ed956110a299fdf5983',
|
'md5': '7ae17b4e18eb5d29212f424a7511c184',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'klassen-darlig-taber-10',
|
'id': 'klassen-darlig-taber-10',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
@@ -30,21 +30,37 @@ class DRTVIE(InfoExtractor):
|
|||||||
'upload_date': '20160823',
|
'upload_date': '20160823',
|
||||||
'duration': 606.84,
|
'duration': 606.84,
|
||||||
},
|
},
|
||||||
'params': {
|
|
||||||
'skip_download': True,
|
|
||||||
},
|
|
||||||
}, {
|
}, {
|
||||||
|
# embed
|
||||||
'url': 'https://www.dr.dk/nyheder/indland/live-christianias-rydning-af-pusher-street-er-i-gang',
|
'url': 'https://www.dr.dk/nyheder/indland/live-christianias-rydning-af-pusher-street-er-i-gang',
|
||||||
'md5': '2c37175c718155930f939ef59952474a',
|
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'christiania-pusher-street-ryddes-drdkrjpo',
|
'id': 'christiania-pusher-street-ryddes-drdkrjpo',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'LIVE Christianias rydning af Pusher Street er i gang',
|
'title': 'LIVE Christianias rydning af Pusher Street er i gang',
|
||||||
'description': '- Det er det fedeste, der er sket i 20 år, fortæller christianit til DR Nyheder.',
|
'description': 'md5:2a71898b15057e9b97334f61d04e6eb5',
|
||||||
'timestamp': 1472800279,
|
'timestamp': 1472800279,
|
||||||
'upload_date': '20160902',
|
'upload_date': '20160902',
|
||||||
'duration': 131.4,
|
'duration': 131.4,
|
||||||
},
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
# with SignLanguage formats
|
||||||
|
'url': 'https://www.dr.dk/tv/se/historien-om-danmark/-/historien-om-danmark-stenalder',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'historien-om-danmark-stenalder',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Historien om Danmark: Stenalder (1)',
|
||||||
|
'description': 'md5:8c66dcbc1669bbc6f873879880f37f2a',
|
||||||
|
'timestamp': 1490401996,
|
||||||
|
'upload_date': '20170325',
|
||||||
|
'duration': 3502.04,
|
||||||
|
'formats': 'mincount:20',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
@@ -88,7 +104,7 @@ class DRTVIE(InfoExtractor):
|
|||||||
elif kind in ('VideoResource', 'AudioResource'):
|
elif kind in ('VideoResource', 'AudioResource'):
|
||||||
duration = float_or_none(asset.get('DurationInMilliseconds'), 1000)
|
duration = float_or_none(asset.get('DurationInMilliseconds'), 1000)
|
||||||
restricted_to_denmark = asset.get('RestrictedToDenmark')
|
restricted_to_denmark = asset.get('RestrictedToDenmark')
|
||||||
spoken_subtitles = asset.get('Target') == 'SpokenSubtitles'
|
asset_target = asset.get('Target')
|
||||||
for link in asset.get('Links', []):
|
for link in asset.get('Links', []):
|
||||||
uri = link.get('Uri')
|
uri = link.get('Uri')
|
||||||
if not uri:
|
if not uri:
|
||||||
@@ -96,9 +112,9 @@ class DRTVIE(InfoExtractor):
|
|||||||
target = link.get('Target')
|
target = link.get('Target')
|
||||||
format_id = target or ''
|
format_id = target or ''
|
||||||
preference = None
|
preference = None
|
||||||
if spoken_subtitles:
|
if asset_target in ('SpokenSubtitles', 'SignLanguage'):
|
||||||
preference = -1
|
preference = -1
|
||||||
format_id += '-spoken-subtitles'
|
format_id += '-%s' % asset_target
|
||||||
if target == 'HDS':
|
if target == 'HDS':
|
||||||
f4m_formats = self._extract_f4m_formats(
|
f4m_formats = self._extract_f4m_formats(
|
||||||
uri + '?hdcore=3.3.0&plugin=aasp-3.3.0.99.43',
|
uri + '?hdcore=3.3.0&plugin=aasp-3.3.0.99.43',
|
||||||
|
@@ -5,9 +5,12 @@ import re
|
|||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
js_to_json,
|
determine_ext,
|
||||||
unescapeHTML,
|
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
|
int_or_none,
|
||||||
|
js_to_json,
|
||||||
|
mimetype2ext,
|
||||||
|
unescapeHTML,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -24,14 +27,7 @@ class DVTVIE(InfoExtractor):
|
|||||||
'id': 'dc0768de855511e49e4b0025900fea04',
|
'id': 'dc0768de855511e49e4b0025900fea04',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Vondra o Českém století: Při pohledu na Havla mi bylo trapně',
|
'title': 'Vondra o Českém století: Při pohledu na Havla mi bylo trapně',
|
||||||
}
|
'duration': 1484,
|
||||||
}, {
|
|
||||||
'url': 'http://video.aktualne.cz/dvtv/stropnicky-policie-vrbetice-preventivne-nekontrolovala/r~82ed4322849211e4a10c0025900fea04/',
|
|
||||||
'md5': '6388f1941b48537dbd28791f712af8bf',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '72c02230849211e49f60002590604f2e',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Stropnický: Policie Vrbětice preventivně nekontrolovala',
|
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://video.aktualne.cz/dvtv/dvtv-16-12-2014-utok-talibanu-boj-o-kliniku-uprchlici/r~973eb3bc854e11e498be002590604f2e/',
|
'url': 'http://video.aktualne.cz/dvtv/dvtv-16-12-2014-utok-talibanu-boj-o-kliniku-uprchlici/r~973eb3bc854e11e498be002590604f2e/',
|
||||||
@@ -44,55 +40,100 @@ class DVTVIE(InfoExtractor):
|
|||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'b0b40906854d11e4bdad0025900fea04',
|
'id': 'b0b40906854d11e4bdad0025900fea04',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Drtinová Veselovský TV 16. 12. 2014: Témata dne'
|
'title': 'Drtinová Veselovský TV 16. 12. 2014: Témata dne',
|
||||||
|
'description': 'md5:0916925dea8e30fe84222582280b47a0',
|
||||||
|
'timestamp': 1418760010,
|
||||||
|
'upload_date': '20141216',
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'md5': '5f7652a08b05009c1292317b449ffea2',
|
'md5': '5f7652a08b05009c1292317b449ffea2',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '420ad9ec854a11e4bdad0025900fea04',
|
'id': '420ad9ec854a11e4bdad0025900fea04',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Školní masakr možná změní boj s Talibanem, říká novinářka'
|
'title': 'Školní masakr možná změní boj s Talibanem, říká novinářka',
|
||||||
|
'description': 'md5:ff2f9f6de73c73d7cef4f756c1c1af42',
|
||||||
|
'timestamp': 1418760010,
|
||||||
|
'upload_date': '20141216',
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'md5': '498eb9dfa97169f409126c617e2a3d64',
|
'md5': '498eb9dfa97169f409126c617e2a3d64',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '95d35580846a11e4b6d20025900fea04',
|
'id': '95d35580846a11e4b6d20025900fea04',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Boj o kliniku: Veřejný zájem, nebo právo na majetek?'
|
'title': 'Boj o kliniku: Veřejný zájem, nebo právo na majetek?',
|
||||||
|
'description': 'md5:889fe610a70fee5511dc3326a089188e',
|
||||||
|
'timestamp': 1418760010,
|
||||||
|
'upload_date': '20141216',
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'md5': 'b8dc6b744844032dab6ba3781a7274b9',
|
'md5': 'b8dc6b744844032dab6ba3781a7274b9',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '6fe14d66853511e4833a0025900fea04',
|
'id': '6fe14d66853511e4833a0025900fea04',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Pánek: Odmítání syrských uprchlíků je ostudou české vlády'
|
'title': 'Pánek: Odmítání syrských uprchlíků je ostudou české vlády',
|
||||||
|
'description': 'md5:544f86de6d20c4815bea11bf2ac3004f',
|
||||||
|
'timestamp': 1418760010,
|
||||||
|
'upload_date': '20141216',
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
}, {
|
||||||
|
'url': 'https://video.aktualne.cz/dvtv/zeman-si-jen-leci-mindraky-sobotku-nenavidi-a-babis-se-mu-te/r~960cdb3a365a11e7a83b0025900fea04/',
|
||||||
|
'md5': 'f8efe9656017da948369aa099788c8ea',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '3c496fec365911e7a6500025900fea04',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Zeman si jen léčí mindráky, Sobotku nenávidí a Babiš se mu teď hodí, tvrdí Kmenta',
|
||||||
|
'duration': 1103,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://video.aktualne.cz/v-cechach-poprve-zazni-zelenkova-zrestaurovana-mse/r~45b4b00483ec11e4883b002590604f2e/',
|
'url': 'http://video.aktualne.cz/v-cechach-poprve-zazni-zelenkova-zrestaurovana-mse/r~45b4b00483ec11e4883b002590604f2e/',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _parse_video_metadata(self, js, video_id):
|
def _parse_video_metadata(self, js, video_id):
|
||||||
metadata = self._parse_json(js, video_id, transform_source=js_to_json)
|
data = self._parse_json(js, video_id, transform_source=js_to_json)
|
||||||
|
|
||||||
|
title = unescapeHTML(data['title'])
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for video in metadata['sources']:
|
for video in data['sources']:
|
||||||
ext = video['type'][6:]
|
video_url = video.get('file')
|
||||||
formats.append({
|
if not video_url:
|
||||||
'url': video['file'],
|
continue
|
||||||
'ext': ext,
|
video_type = video.get('type')
|
||||||
'format_id': '%s-%s' % (ext, video['label']),
|
ext = determine_ext(video_url, mimetype2ext(video_type))
|
||||||
'height': int(video['label'].rstrip('p')),
|
if video_type == 'application/vnd.apple.mpegurl' or ext == 'm3u8':
|
||||||
'fps': 25,
|
formats.extend(self._extract_m3u8_formats(
|
||||||
})
|
video_url, video_id, 'mp4', entry_protocol='m3u8_native',
|
||||||
|
m3u8_id='hls', fatal=False))
|
||||||
|
elif video_type == 'application/dash+xml' or ext == 'mpd':
|
||||||
|
formats.extend(self._extract_mpd_formats(
|
||||||
|
video_url, video_id, mpd_id='dash', fatal=False))
|
||||||
|
else:
|
||||||
|
label = video.get('label')
|
||||||
|
height = self._search_regex(
|
||||||
|
r'^(\d+)[pP]', label or '', 'height', default=None)
|
||||||
|
format_id = ['http']
|
||||||
|
for f in (ext, label):
|
||||||
|
if f:
|
||||||
|
format_id.append(f)
|
||||||
|
formats.append({
|
||||||
|
'url': video_url,
|
||||||
|
'format_id': '-'.join(format_id),
|
||||||
|
'height': int_or_none(height),
|
||||||
|
})
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': metadata['mediaid'],
|
'id': data.get('mediaid') or video_id,
|
||||||
'title': unescapeHTML(metadata['title']),
|
'title': title,
|
||||||
'thumbnail': self._proto_relative_url(metadata['image'], 'http:'),
|
'description': data.get('description'),
|
||||||
|
'thumbnail': data.get('image'),
|
||||||
|
'duration': int_or_none(data.get('duration')),
|
||||||
|
'timestamp': int_or_none(data.get('pubtime')),
|
||||||
'formats': formats
|
'formats': formats
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +144,7 @@ class DVTVIE(InfoExtractor):
|
|||||||
|
|
||||||
# single video
|
# single video
|
||||||
item = self._search_regex(
|
item = self._search_regex(
|
||||||
r"(?s)embedData[0-9a-f]{32}\['asset'\]\s*=\s*(\{.+?\});",
|
r'(?s)embedData[0-9a-f]{32}\[["\']asset["\']\]\s*=\s*(\{.+?\});',
|
||||||
webpage, 'video', default=None, fatal=False)
|
webpage, 'video', default=None, fatal=False)
|
||||||
|
|
||||||
if item:
|
if item:
|
||||||
@@ -113,6 +154,8 @@ class DVTVIE(InfoExtractor):
|
|||||||
items = re.findall(
|
items = re.findall(
|
||||||
r"(?s)BBX\.context\.assets\['[0-9a-f]{32}'\]\.push\(({.+?})\);",
|
r"(?s)BBX\.context\.assets\['[0-9a-f]{32}'\]\.push\(({.+?})\);",
|
||||||
webpage)
|
webpage)
|
||||||
|
if not items:
|
||||||
|
items = re.findall(r'(?s)var\s+asset\s*=\s*({.+?});\n', webpage)
|
||||||
|
|
||||||
if items:
|
if items:
|
||||||
return {
|
return {
|
||||||
|
@@ -41,6 +41,7 @@ from .alphaporno import AlphaPornoIE
|
|||||||
from .amcnetworks import AMCNetworksIE
|
from .amcnetworks import AMCNetworksIE
|
||||||
from .animeondemand import AnimeOnDemandIE
|
from .animeondemand import AnimeOnDemandIE
|
||||||
from .anitube import AnitubeIE
|
from .anitube import AnitubeIE
|
||||||
|
from .anvato import AnvatoIE
|
||||||
from .anysex import AnySexIE
|
from .anysex import AnySexIE
|
||||||
from .aol import AolIE
|
from .aol import AolIE
|
||||||
from .allocine import AllocineIE
|
from .allocine import AllocineIE
|
||||||
@@ -70,6 +71,10 @@ from .arte import (
|
|||||||
TheOperaPlatformIE,
|
TheOperaPlatformIE,
|
||||||
ArteTVPlaylistIE,
|
ArteTVPlaylistIE,
|
||||||
)
|
)
|
||||||
|
from .asiancrush import (
|
||||||
|
AsianCrushIE,
|
||||||
|
AsianCrushPlaylistIE,
|
||||||
|
)
|
||||||
from .atresplayer import AtresPlayerIE
|
from .atresplayer import AtresPlayerIE
|
||||||
from .atttechchannel import ATTTechChannelIE
|
from .atttechchannel import ATTTechChannelIE
|
||||||
from .atvat import ATVAtIE
|
from .atvat import ATVAtIE
|
||||||
@@ -89,7 +94,7 @@ from .azmedien import (
|
|||||||
)
|
)
|
||||||
from .baidu import BaiduVideoIE
|
from .baidu import BaiduVideoIE
|
||||||
from .bambuser import BambuserIE, BambuserChannelIE
|
from .bambuser import BambuserIE, BambuserChannelIE
|
||||||
from .bandcamp import BandcampIE, BandcampAlbumIE
|
from .bandcamp import BandcampIE, BandcampAlbumIE, BandcampWeeklyIE
|
||||||
from .bbc import (
|
from .bbc import (
|
||||||
BBCCoUkIE,
|
BBCCoUkIE,
|
||||||
BBCCoUkArticleIE,
|
BBCCoUkArticleIE,
|
||||||
@@ -97,7 +102,10 @@ from .bbc import (
|
|||||||
BBCCoUkPlaylistIE,
|
BBCCoUkPlaylistIE,
|
||||||
BBCIE,
|
BBCIE,
|
||||||
)
|
)
|
||||||
from .beampro import BeamProLiveIE
|
from .beampro import (
|
||||||
|
BeamProLiveIE,
|
||||||
|
BeamProVodIE,
|
||||||
|
)
|
||||||
from .beeg import BeegIE
|
from .beeg import BeegIE
|
||||||
from .behindkink import BehindKinkIE
|
from .behindkink import BehindKinkIE
|
||||||
from .bellmedia import BellMediaIE
|
from .bellmedia import BellMediaIE
|
||||||
@@ -250,7 +258,10 @@ from .democracynow import DemocracynowIE
|
|||||||
from .dfb import DFBIE
|
from .dfb import DFBIE
|
||||||
from .dhm import DHMIE
|
from .dhm import DHMIE
|
||||||
from .dotsub import DotsubIE
|
from .dotsub import DotsubIE
|
||||||
from .douyutv import DouyuTVIE
|
from .douyutv import (
|
||||||
|
DouyuShowIE,
|
||||||
|
DouyuTVIE,
|
||||||
|
)
|
||||||
from .dplay import (
|
from .dplay import (
|
||||||
DPlayIE,
|
DPlayIE,
|
||||||
DPlayItIE,
|
DPlayItIE,
|
||||||
@@ -349,9 +360,9 @@ from .foxsports import FoxSportsIE
|
|||||||
from .franceculture import FranceCultureIE
|
from .franceculture import FranceCultureIE
|
||||||
from .franceinter import FranceInterIE
|
from .franceinter import FranceInterIE
|
||||||
from .francetv import (
|
from .francetv import (
|
||||||
PluzzIE,
|
|
||||||
FranceTvInfoIE,
|
|
||||||
FranceTVIE,
|
FranceTVIE,
|
||||||
|
FranceTVEmbedIE,
|
||||||
|
FranceTVInfoIE,
|
||||||
GenerationQuoiIE,
|
GenerationQuoiIE,
|
||||||
CultureboxIE,
|
CultureboxIE,
|
||||||
)
|
)
|
||||||
@@ -385,7 +396,6 @@ from .globo import (
|
|||||||
from .go import GoIE
|
from .go import GoIE
|
||||||
from .go90 import Go90IE
|
from .go90 import Go90IE
|
||||||
from .godtube import GodTubeIE
|
from .godtube import GodTubeIE
|
||||||
from .godtv import GodTVIE
|
|
||||||
from .golem import GolemIE
|
from .golem import GolemIE
|
||||||
from .googledrive import GoogleDriveIE
|
from .googledrive import GoogleDriveIE
|
||||||
from .googleplus import GooglePlusIE
|
from .googleplus import GooglePlusIE
|
||||||
@@ -541,6 +551,7 @@ from .mangomolo import (
|
|||||||
)
|
)
|
||||||
from .matchtv import MatchTVIE
|
from .matchtv import MatchTVIE
|
||||||
from .mdr import MDRIE
|
from .mdr import MDRIE
|
||||||
|
from .mediaset import MediasetIE
|
||||||
from .medici import MediciIE
|
from .medici import MediciIE
|
||||||
from .meipai import MeipaiIE
|
from .meipai import MeipaiIE
|
||||||
from .melonvod import MelonVODIE
|
from .melonvod import MelonVODIE
|
||||||
@@ -629,7 +640,10 @@ from .neteasemusic import (
|
|||||||
NetEaseMusicProgramIE,
|
NetEaseMusicProgramIE,
|
||||||
NetEaseMusicDjRadioIE,
|
NetEaseMusicDjRadioIE,
|
||||||
)
|
)
|
||||||
from .newgrounds import NewgroundsIE
|
from .newgrounds import (
|
||||||
|
NewgroundsIE,
|
||||||
|
NewgroundsPlaylistIE,
|
||||||
|
)
|
||||||
from .newstube import NewstubeIE
|
from .newstube import NewstubeIE
|
||||||
from .nextmedia import (
|
from .nextmedia import (
|
||||||
NextMediaIE,
|
NextMediaIE,
|
||||||
@@ -662,6 +676,8 @@ from .nintendo import NintendoIE
|
|||||||
from .njpwworld import NJPWWorldIE
|
from .njpwworld import NJPWWorldIE
|
||||||
from .nobelprize import NobelPrizeIE
|
from .nobelprize import NobelPrizeIE
|
||||||
from .noco import NocoIE
|
from .noco import NocoIE
|
||||||
|
from .nonktube import NonkTubeIE
|
||||||
|
from .noovo import NoovoIE
|
||||||
from .normalboots import NormalbootsIE
|
from .normalboots import NormalbootsIE
|
||||||
from .nosvideo import NosVideoIE
|
from .nosvideo import NosVideoIE
|
||||||
from .nova import NovaIE
|
from .nova import NovaIE
|
||||||
@@ -730,8 +746,8 @@ from .openload import OpenloadIE
|
|||||||
from .ora import OraTVIE
|
from .ora import OraTVIE
|
||||||
from .orf import (
|
from .orf import (
|
||||||
ORFTVthekIE,
|
ORFTVthekIE,
|
||||||
ORFOE1IE,
|
|
||||||
ORFFM4IE,
|
ORFFM4IE,
|
||||||
|
ORFOE1IE,
|
||||||
ORFIPTVIE,
|
ORFIPTVIE,
|
||||||
)
|
)
|
||||||
from .packtpub import (
|
from .packtpub import (
|
||||||
@@ -859,6 +875,7 @@ from .rutube import (
|
|||||||
)
|
)
|
||||||
from .rutv import RUTVIE
|
from .rutv import RUTVIE
|
||||||
from .ruutu import RuutuIE
|
from .ruutu import RuutuIE
|
||||||
|
from .ruv import RuvIE
|
||||||
from .sandia import SandiaIE
|
from .sandia import SandiaIE
|
||||||
from .safari import (
|
from .safari import (
|
||||||
SafariIE,
|
SafariIE,
|
||||||
@@ -1012,11 +1029,6 @@ from .trilulilu import TriluliluIE
|
|||||||
from .trutv import TruTVIE
|
from .trutv import TruTVIE
|
||||||
from .tube8 import Tube8IE
|
from .tube8 import Tube8IE
|
||||||
from .tubitv import TubiTvIE
|
from .tubitv import TubiTvIE
|
||||||
from .tudou import (
|
|
||||||
TudouIE,
|
|
||||||
TudouPlaylistIE,
|
|
||||||
TudouAlbumIE,
|
|
||||||
)
|
|
||||||
from .tumblr import TumblrIE
|
from .tumblr import TumblrIE
|
||||||
from .tunein import (
|
from .tunein import (
|
||||||
TuneInClipIE,
|
TuneInClipIE,
|
||||||
@@ -1096,6 +1108,10 @@ from .uplynk import (
|
|||||||
UplynkIE,
|
UplynkIE,
|
||||||
UplynkPreplayIE,
|
UplynkPreplayIE,
|
||||||
)
|
)
|
||||||
|
from .upskill import (
|
||||||
|
UpskillIE,
|
||||||
|
UpskillCourseIE,
|
||||||
|
)
|
||||||
from .urort import UrortIE
|
from .urort import UrortIE
|
||||||
from .urplay import URPlayIE
|
from .urplay import URPlayIE
|
||||||
from .usanetwork import USANetworkIE
|
from .usanetwork import USANetworkIE
|
||||||
@@ -1123,6 +1139,7 @@ from .vgtv import (
|
|||||||
from .vh1 import VH1IE
|
from .vh1 import VH1IE
|
||||||
from .vice import (
|
from .vice import (
|
||||||
ViceIE,
|
ViceIE,
|
||||||
|
ViceArticleIE,
|
||||||
ViceShowIE,
|
ViceShowIE,
|
||||||
)
|
)
|
||||||
from .viceland import VicelandIE
|
from .viceland import VicelandIE
|
||||||
@@ -1298,5 +1315,6 @@ from .youtube import (
|
|||||||
YoutubeWatchLaterIE,
|
YoutubeWatchLaterIE,
|
||||||
)
|
)
|
||||||
from .zapiks import ZapiksIE
|
from .zapiks import ZapiksIE
|
||||||
|
from .zaq1 import Zaq1IE
|
||||||
from .zdf import ZDFIE, ZDFChannelIE
|
from .zdf import ZDFIE, ZDFChannelIE
|
||||||
from .zingmp3 import ZingMp3IE
|
from .zingmp3 import ZingMp3IE
|
||||||
|
@@ -102,6 +102,8 @@ class FirstTVIE(InfoExtractor):
|
|||||||
'format_id': f.get('name'),
|
'format_id': f.get('name'),
|
||||||
'tbr': tbr,
|
'tbr': tbr,
|
||||||
'source_preference': quality(f.get('name')),
|
'source_preference': quality(f.get('name')),
|
||||||
|
# quality metadata of http formats may be incorrect
|
||||||
|
'preference': -1,
|
||||||
})
|
})
|
||||||
# m3u8 URL format is reverse engineered from [1] (search for
|
# m3u8 URL format is reverse engineered from [1] (search for
|
||||||
# master.m3u8). dashEdges (that is currently balancer-vod.1tv.ru)
|
# master.m3u8). dashEdges (that is currently balancer-vod.1tv.ru)
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_urllib_parse_urlencode
|
from ..compat import (
|
||||||
|
compat_str,
|
||||||
|
compat_urllib_parse_urlencode,
|
||||||
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
@@ -81,7 +84,7 @@ class FlickrIE(InfoExtractor):
|
|||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for stream in streams['stream']:
|
for stream in streams['stream']:
|
||||||
stream_type = str(stream.get('type'))
|
stream_type = compat_str(stream.get('type'))
|
||||||
formats.append({
|
formats.append({
|
||||||
'format_id': stream_type,
|
'format_id': stream_type,
|
||||||
'url': stream['_content'],
|
'url': stream['_content'],
|
||||||
|
@@ -85,11 +85,11 @@ class FourTubeIE(InfoExtractor):
|
|||||||
media_id = params[0]
|
media_id = params[0]
|
||||||
sources = ['%s' % p for p in params[2]]
|
sources = ['%s' % p for p in params[2]]
|
||||||
|
|
||||||
token_url = 'http://tkn.4tube.com/{0}/desktop/{1}'.format(
|
token_url = 'https://tkn.kodicdn.com/{0}/desktop/{1}'.format(
|
||||||
media_id, '+'.join(sources))
|
media_id, '+'.join(sources))
|
||||||
headers = {
|
headers = {
|
||||||
b'Content-Type': b'application/x-www-form-urlencoded',
|
b'Content-Type': b'application/x-www-form-urlencoded',
|
||||||
b'Origin': b'http://www.4tube.com',
|
b'Origin': b'https://www.4tube.com',
|
||||||
}
|
}
|
||||||
token_req = sanitized_Request(token_url, b'{}', headers)
|
token_req = sanitized_Request(token_url, b'{}', headers)
|
||||||
tokens = self._download_json(token_req, video_id)
|
tokens = self._download_json(token_req, video_id)
|
||||||
|
@@ -5,6 +5,7 @@ import itertools
|
|||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
get_element_by_id,
|
get_element_by_id,
|
||||||
|
int_or_none,
|
||||||
remove_end,
|
remove_end,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ class FoxgayIE(InfoExtractor):
|
|||||||
|
|
||||||
formats = [{
|
formats = [{
|
||||||
'url': source,
|
'url': source,
|
||||||
'height': resolution,
|
'height': int_or_none(resolution),
|
||||||
} for source, resolution in zip(
|
} for source, resolution in zip(
|
||||||
video_data['sources'], video_data.get('resolutions', itertools.repeat(None)))]
|
video_data['sources'], video_data.get('resolutions', itertools.repeat(None)))]
|
||||||
|
|
||||||
|
@@ -11,10 +11,10 @@ class FoxSportsIE(InfoExtractor):
|
|||||||
_VALID_URL = r'https?://(?:www\.)?foxsports\.com/(?:[^/]+/)*(?P<id>[^/]+)'
|
_VALID_URL = r'https?://(?:www\.)?foxsports\.com/(?:[^/]+/)*(?P<id>[^/]+)'
|
||||||
|
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://www.foxsports.com/video?vid=432609859715',
|
'url': 'http://www.foxsports.com/tennessee/video/432609859715',
|
||||||
'md5': 'b49050e955bebe32c301972e4012ac17',
|
'md5': 'b49050e955bebe32c301972e4012ac17',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'i0qKWsk3qJaM',
|
'id': 'bwduI3X_TgUB',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Courtney Lee on going up 2-0 in series vs. Blazers',
|
'title': 'Courtney Lee on going up 2-0 in series vs. Blazers',
|
||||||
'description': 'Courtney Lee talks about Memphis being focused.',
|
'description': 'Courtney Lee talks about Memphis being focused.',
|
||||||
@@ -31,8 +31,9 @@ class FoxSportsIE(InfoExtractor):
|
|||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
config = self._parse_json(
|
config = self._parse_json(
|
||||||
self._search_regex(
|
self._html_search_regex(
|
||||||
r"data-player-config='([^']+)'", webpage, 'data player config'),
|
r"""class="[^"]*(?:fs-player|platformPlayer-wrapper)[^"]*".+?data-player-config='([^']+)'""",
|
||||||
|
webpage, 'data player config'),
|
||||||
video_id)
|
video_id)
|
||||||
|
|
||||||
return self.url_result(smuggle_url(update_url_query(
|
return self.url_result(smuggle_url(update_url_query(
|
||||||
|
@@ -21,11 +21,13 @@ from .dailymotion import (
|
|||||||
|
|
||||||
|
|
||||||
class FranceTVBaseInfoExtractor(InfoExtractor):
|
class FranceTVBaseInfoExtractor(InfoExtractor):
|
||||||
def _extract_video(self, video_id, catalogue):
|
def _extract_video(self, video_id, catalogue=None):
|
||||||
info = self._download_json(
|
info = self._download_json(
|
||||||
'http://webservices.francetelevisions.fr/tools/getInfosOeuvre/v2/?idDiffusion=%s&catalogue=%s'
|
'https://sivideo.webservices.francetelevisions.fr/tools/getInfosOeuvre/v2/',
|
||||||
% (video_id, catalogue),
|
video_id, 'Downloading video JSON', query={
|
||||||
video_id, 'Downloading video JSON')
|
'idDiffusion': video_id,
|
||||||
|
'catalogue': catalogue or '',
|
||||||
|
})
|
||||||
|
|
||||||
if info.get('status') == 'NOK':
|
if info.get('status') == 'NOK':
|
||||||
raise ExtractorError(
|
raise ExtractorError(
|
||||||
@@ -109,27 +111,100 @@ class FranceTVBaseInfoExtractor(InfoExtractor):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PluzzIE(FranceTVBaseInfoExtractor):
|
class FranceTVIE(FranceTVBaseInfoExtractor):
|
||||||
IE_NAME = 'pluzz.francetv.fr'
|
_VALID_URL = r'https?://(?:(?:www\.)?france\.tv|mobile\.france\.tv)/(?:[^/]+/)*(?P<id>[^/]+)\.html'
|
||||||
_VALID_URL = r'https?://(?:m\.)?pluzz\.francetv\.fr/videos/(?P<id>.+?)\.html'
|
|
||||||
|
|
||||||
# Can't use tests, videos expire in 7 days
|
_TESTS = [{
|
||||||
|
'url': 'https://www.france.tv/france-2/13h15-le-dimanche/140921-les-mysteres-de-jesus.html',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '157550144',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': '13h15, le dimanche... - Les mystères de Jésus',
|
||||||
|
'description': 'md5:75efe8d4c0a8205e5904498ffe1e1a42',
|
||||||
|
'timestamp': 1494156300,
|
||||||
|
'upload_date': '20170507',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 downloads
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
# france3
|
||||||
|
'url': 'https://www.france.tv/france-3/des-chiffres-et-des-lettres/139063-emission-du-mardi-9-mai-2017.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# france4
|
||||||
|
'url': 'https://www.france.tv/france-4/hero-corp/saison-1/134151-apres-le-calme.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# france5
|
||||||
|
'url': 'https://www.france.tv/france-5/c-a-dire/saison-10/137013-c-a-dire.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# franceo
|
||||||
|
'url': 'https://www.france.tv/france-o/archipels/132249-mon-ancetre-l-esclave.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# france2 live
|
||||||
|
'url': 'https://www.france.tv/france-2/direct.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.france.tv/documentaires/histoire/136517-argentine-les-500-bebes-voles-de-la-dictature.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.france.tv/jeux-et-divertissements/divertissements/133965-le-web-contre-attaque.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://mobile.france.tv/france-5/c-dans-l-air/137347-emission-du-vendredi-12-mai-2017.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.france.tv/142749-rouge-sang.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
display_id = self._match_id(url)
|
display_id = self._match_id(url)
|
||||||
|
|
||||||
webpage = self._download_webpage(url, display_id)
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
|
||||||
video_id = self._html_search_meta(
|
catalogue = None
|
||||||
'id_video', webpage, 'video id', default=None)
|
video_id = self._search_regex(
|
||||||
|
r'data-main-video=(["\'])(?P<id>(?:(?!\1).)+)\1',
|
||||||
|
webpage, 'video id', default=None, group='id')
|
||||||
|
|
||||||
if not video_id:
|
if not video_id:
|
||||||
video_id = self._search_regex(
|
video_id, catalogue = self._html_search_regex(
|
||||||
r'data-diffusion=["\'](\d+)', webpage, 'video id')
|
r'(?:href=|player\.setVideo\(\s*)"http://videos?\.francetv\.fr/video/([^@]+@[^"]+)"',
|
||||||
|
webpage, 'video ID').split('@')
|
||||||
return self._extract_video(video_id, 'Pluzz')
|
return self._extract_video(video_id, catalogue)
|
||||||
|
|
||||||
|
|
||||||
class FranceTvInfoIE(FranceTVBaseInfoExtractor):
|
class FranceTVEmbedIE(FranceTVBaseInfoExtractor):
|
||||||
|
_VALID_URL = r'https?://embed\.francetv\.fr/*\?.*?\bue=(?P<id>[^&]+)'
|
||||||
|
|
||||||
|
_TEST = {
|
||||||
|
'url': 'http://embed.francetv.fr/?ue=7fd581a2ccf59d2fc5719c5c13cf6961',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'NI_983319',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Le Pen Reims',
|
||||||
|
'upload_date': '20170505',
|
||||||
|
'timestamp': 1493981780,
|
||||||
|
'duration': 16,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
|
video = self._download_json(
|
||||||
|
'http://api-embed.webservices.francetelevisions.fr/key/%s' % video_id,
|
||||||
|
video_id)
|
||||||
|
|
||||||
|
return self._extract_video(video['video_id'], video.get('catalog'))
|
||||||
|
|
||||||
|
|
||||||
|
class FranceTVInfoIE(FranceTVBaseInfoExtractor):
|
||||||
IE_NAME = 'francetvinfo.fr'
|
IE_NAME = 'francetvinfo.fr'
|
||||||
_VALID_URL = r'https?://(?:www|mobile|france3-regions)\.francetvinfo\.fr/(?:[^/]+/)*(?P<title>[^/?#&.]+)'
|
_VALID_URL = r'https?://(?:www|mobile|france3-regions)\.francetvinfo\.fr/(?:[^/]+/)*(?P<title>[^/?#&.]+)'
|
||||||
|
|
||||||
@@ -233,124 +308,6 @@ class FranceTvInfoIE(FranceTVBaseInfoExtractor):
|
|||||||
return self._extract_video(video_id, catalogue)
|
return self._extract_video(video_id, catalogue)
|
||||||
|
|
||||||
|
|
||||||
class FranceTVIE(FranceTVBaseInfoExtractor):
|
|
||||||
IE_NAME = 'francetv'
|
|
||||||
IE_DESC = 'France 2, 3, 4, 5 and Ô'
|
|
||||||
_VALID_URL = r'''(?x)
|
|
||||||
https?://
|
|
||||||
(?:
|
|
||||||
(?:www\.)?france[2345o]\.fr/
|
|
||||||
(?:
|
|
||||||
emissions/[^/]+/(?:videos|diffusions)|
|
|
||||||
emission/[^/]+|
|
|
||||||
videos|
|
|
||||||
jt
|
|
||||||
)
|
|
||||||
/|
|
|
||||||
embed\.francetv\.fr/\?ue=
|
|
||||||
)
|
|
||||||
(?P<id>[^/?]+)
|
|
||||||
'''
|
|
||||||
|
|
||||||
_TESTS = [
|
|
||||||
# france2
|
|
||||||
{
|
|
||||||
'url': 'http://www.france2.fr/emissions/13h15-le-samedi-le-dimanche/videos/75540104',
|
|
||||||
'md5': 'c03fc87cb85429ffd55df32b9fc05523',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '109169362',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': '13h15, le dimanche...',
|
|
||||||
'description': 'md5:9a0932bb465f22d377a449be9d1a0ff7',
|
|
||||||
'upload_date': '20140914',
|
|
||||||
'timestamp': 1410693600,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
# france3
|
|
||||||
{
|
|
||||||
'url': 'http://www.france3.fr/emissions/pieces-a-conviction/diffusions/13-11-2013_145575',
|
|
||||||
'md5': '679bb8f8921f8623bd658fa2f8364da0',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '000702326_CAPP_PicesconvictionExtrait313022013_120220131722_Au',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Le scandale du prix des médicaments',
|
|
||||||
'description': 'md5:1384089fbee2f04fc6c9de025ee2e9ce',
|
|
||||||
'upload_date': '20131113',
|
|
||||||
'timestamp': 1384380000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
# france4
|
|
||||||
{
|
|
||||||
'url': 'http://www.france4.fr/emissions/hero-corp/videos/rhozet_herocorp_bonus_1_20131106_1923_06112013172108_F4',
|
|
||||||
'md5': 'a182bf8d2c43d88d46ec48fbdd260c1c',
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'rhozet_herocorp_bonus_1_20131106_1923_06112013172108_F4',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Hero Corp Making of - Extrait 1',
|
|
||||||
'description': 'md5:c87d54871b1790679aec1197e73d650a',
|
|
||||||
'upload_date': '20131106',
|
|
||||||
'timestamp': 1383766500,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
# france5
|
|
||||||
{
|
|
||||||
'url': 'http://www.france5.fr/emissions/c-a-dire/videos/quels_sont_les_enjeux_de_cette_rentree_politique__31-08-2015_908948?onglet=tous&page=1',
|
|
||||||
'md5': 'f6c577df3806e26471b3d21631241fd0',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '123327454',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'C à dire ?! - Quels sont les enjeux de cette rentrée politique ?',
|
|
||||||
'description': 'md5:4a0d5cb5dce89d353522a84462bae5a4',
|
|
||||||
'upload_date': '20150831',
|
|
||||||
'timestamp': 1441035120,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
# franceo
|
|
||||||
{
|
|
||||||
'url': 'http://www.franceo.fr/jt/info-soir/18-07-2015',
|
|
||||||
'md5': '47d5816d3b24351cdce512ad7ab31da8',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '125377621',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'Infô soir',
|
|
||||||
'description': 'md5:01b8c6915a3d93d8bbbd692651714309',
|
|
||||||
'upload_date': '20150718',
|
|
||||||
'timestamp': 1437241200,
|
|
||||||
'duration': 414,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
# francetv embed
|
|
||||||
'url': 'http://embed.francetv.fr/?ue=8d7d3da1e3047c42ade5a5d7dfd3fc87',
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'EV_30231',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'Alcaline, le concert avec Calogero',
|
|
||||||
'description': 'md5:61f08036dcc8f47e9cfc33aed08ffaff',
|
|
||||||
'upload_date': '20150226',
|
|
||||||
'timestamp': 1424989860,
|
|
||||||
'duration': 5400,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'url': 'http://www.france4.fr/emission/highlander/diffusion-du-17-07-2015-04h05',
|
|
||||||
'only_matching': True,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'url': 'http://www.franceo.fr/videos/125377617',
|
|
||||||
'only_matching': True,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
|
||||||
video_id = self._match_id(url)
|
|
||||||
webpage = self._download_webpage(url, video_id)
|
|
||||||
video_id, catalogue = self._html_search_regex(
|
|
||||||
r'(?:href=|player\.setVideo\(\s*)"http://videos?\.francetv\.fr/video/([^@]+@[^"]+)"',
|
|
||||||
webpage, 'video ID').split('@')
|
|
||||||
return self._extract_video(video_id, catalogue)
|
|
||||||
|
|
||||||
|
|
||||||
class GenerationQuoiIE(InfoExtractor):
|
class GenerationQuoiIE(InfoExtractor):
|
||||||
IE_NAME = 'france2.fr:generation-quoi'
|
IE_NAME = 'france2.fr:generation-quoi'
|
||||||
_VALID_URL = r'https?://generation-quoi\.france2\.fr/portrait/(?P<id>[^/?#]+)'
|
_VALID_URL = r'https?://generation-quoi\.france2\.fr/portrait/(?P<id>[^/?#]+)'
|
||||||
|
@@ -2,15 +2,11 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_HTTPError
|
||||||
compat_HTTPError,
|
|
||||||
compat_urllib_parse_unquote_plus,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
determine_ext,
|
determine_ext,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
js_to_json,
|
js_to_json,
|
||||||
sanitized_Request,
|
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
urlencode_postdata
|
urlencode_postdata
|
||||||
)
|
)
|
||||||
@@ -20,6 +16,7 @@ class FunimationIE(InfoExtractor):
|
|||||||
_VALID_URL = r'https?://(?:www\.)?funimation(?:\.com|now\.uk)/shows/[^/]+/(?P<id>[^/?#&]+)'
|
_VALID_URL = r'https?://(?:www\.)?funimation(?:\.com|now\.uk)/shows/[^/]+/(?P<id>[^/?#&]+)'
|
||||||
|
|
||||||
_NETRC_MACHINE = 'funimation'
|
_NETRC_MACHINE = 'funimation'
|
||||||
|
_TOKEN = None
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'https://www.funimation.com/shows/hacksign/role-play/',
|
'url': 'https://www.funimation.com/shows/hacksign/role-play/',
|
||||||
@@ -38,56 +35,38 @@ class FunimationIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'https://www.funimation.com/shows/attack-on-titan-junior-high/broadcast-dub-preview/',
|
'url': 'https://www.funimation.com/shows/attack-on-titan-junior-high/broadcast-dub-preview/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '9635',
|
'id': '210051',
|
||||||
'display_id': 'broadcast-dub-preview',
|
'display_id': 'broadcast-dub-preview',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Attack on Titan: Junior High - Broadcast Dub Preview',
|
'title': 'Attack on Titan: Junior High - Broadcast Dub Preview',
|
||||||
'description': 'md5:f8ec49c0aff702a7832cd81b8a44f803',
|
|
||||||
'thumbnail': r're:https?://.*\.(?:jpg|png)',
|
'thumbnail': r're:https?://.*\.(?:jpg|png)',
|
||||||
},
|
},
|
||||||
'skip': 'Access without user interaction is forbidden by CloudFlare',
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://www.funimationnow.uk/shows/puzzle-dragons-x/drop-impact/simulcast/',
|
'url': 'https://www.funimationnow.uk/shows/puzzle-dragons-x/drop-impact/simulcast/',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
_LOGIN_URL = 'http://www.funimation.com/login'
|
|
||||||
|
|
||||||
def _extract_cloudflare_session_ua(self, url):
|
|
||||||
ci_session_cookie = self._get_cookies(url).get('ci_session')
|
|
||||||
if ci_session_cookie:
|
|
||||||
ci_session = compat_urllib_parse_unquote_plus(ci_session_cookie.value)
|
|
||||||
# ci_session is a string serialized by PHP function serialize()
|
|
||||||
# This case is simple enough to use regular expressions only
|
|
||||||
return self._search_regex(
|
|
||||||
r'"user_agent";s:\d+:"([^"]+)"', ci_session, 'user agent',
|
|
||||||
default=None)
|
|
||||||
|
|
||||||
def _login(self):
|
def _login(self):
|
||||||
(username, password) = self._get_login_info()
|
(username, password) = self._get_login_info()
|
||||||
if username is None:
|
if username is None:
|
||||||
return
|
return
|
||||||
data = urlencode_postdata({
|
try:
|
||||||
'email_field': username,
|
data = self._download_json(
|
||||||
'password_field': password,
|
'https://prod-api-funimationnow.dadcdigital.com/api/auth/login/',
|
||||||
})
|
None, 'Logging in as %s' % username, data=urlencode_postdata({
|
||||||
user_agent = self._extract_cloudflare_session_ua(self._LOGIN_URL)
|
'username': username,
|
||||||
if not user_agent:
|
'password': password,
|
||||||
user_agent = 'Mozilla/5.0 (Windows NT 5.2; WOW64; rv:42.0) Gecko/20100101 Firefox/42.0'
|
}))
|
||||||
login_request = sanitized_Request(self._LOGIN_URL, data, headers={
|
self._TOKEN = data['token']
|
||||||
'User-Agent': user_agent,
|
except ExtractorError as e:
|
||||||
'Content-Type': 'application/x-www-form-urlencoded'
|
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401:
|
||||||
})
|
error = self._parse_json(e.cause.read().decode(), None)['error']
|
||||||
login_page = self._download_webpage(
|
raise ExtractorError(error, expected=True)
|
||||||
login_request, None, 'Logging in as %s' % username)
|
raise
|
||||||
if any(p in login_page for p in ('funimation.com/logout', '>Log Out<')):
|
|
||||||
return
|
|
||||||
error = self._html_search_regex(
|
|
||||||
r'(?s)<div[^>]+id=["\']errorMessages["\'][^>]*>(.+?)</div>',
|
|
||||||
login_page, 'error messages', default=None)
|
|
||||||
if error:
|
|
||||||
raise ExtractorError('Unable to login: %s' % error, expected=True)
|
|
||||||
raise ExtractorError('Unable to log in')
|
|
||||||
|
|
||||||
def _real_initialize(self):
|
def _real_initialize(self):
|
||||||
self._login()
|
self._login()
|
||||||
@@ -125,9 +104,12 @@ class FunimationIE(InfoExtractor):
|
|||||||
description = self._html_search_meta(['description', 'og:description'], webpage, fatal=True)
|
description = self._html_search_meta(['description', 'og:description'], webpage, fatal=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
headers = {}
|
||||||
|
if self._TOKEN:
|
||||||
|
headers['Authorization'] = 'Token %s' % self._TOKEN
|
||||||
sources = self._download_json(
|
sources = self._download_json(
|
||||||
'https://prod-api-funimationnow.dadcdigital.com/api/source/catalog/video/%s/signed/' % video_id,
|
'https://prod-api-funimationnow.dadcdigital.com/api/source/catalog/video/%s/signed/' % video_id,
|
||||||
video_id)['items']
|
video_id, headers=headers)['items']
|
||||||
except ExtractorError as e:
|
except ExtractorError as e:
|
||||||
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
|
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
|
||||||
error = self._parse_json(e.cause.read(), video_id)['errors'][0]
|
error = self._parse_json(e.cause.read(), video_id)['errors'][0]
|
||||||
|
@@ -58,8 +58,7 @@ class FunnyOrDieIE(InfoExtractor):
|
|||||||
m3u8_url, video_id, 'mp4', 'm3u8_native',
|
m3u8_url, video_id, 'mp4', 'm3u8_native',
|
||||||
m3u8_id='hls', fatal=False)
|
m3u8_id='hls', fatal=False)
|
||||||
source_formats = list(filter(
|
source_formats = list(filter(
|
||||||
lambda f: f.get('vcodec') != 'none' and f.get('resolution') != 'multiple',
|
lambda f: f.get('vcodec') != 'none', m3u8_formats))
|
||||||
m3u8_formats))
|
|
||||||
|
|
||||||
bitrates = [int(bitrate) for bitrate in re.findall(r'[,/]v(\d+)(?=[,/])', m3u8_url)]
|
bitrates = [int(bitrate) for bitrate in re.findall(r'[,/]v(\d+)(?=[,/])', m3u8_url)]
|
||||||
bitrates.sort()
|
bitrates.sort()
|
||||||
|
@@ -78,8 +78,7 @@ class GameSpotIE(OnceIE):
|
|||||||
if m3u8_formats:
|
if m3u8_formats:
|
||||||
self._sort_formats(m3u8_formats)
|
self._sort_formats(m3u8_formats)
|
||||||
m3u8_formats = list(filter(
|
m3u8_formats = list(filter(
|
||||||
lambda f: f.get('vcodec') != 'none' and f.get('resolution') != 'multiple',
|
lambda f: f.get('vcodec') != 'none', m3u8_formats))
|
||||||
m3u8_formats))
|
|
||||||
if len(qualities) == len(m3u8_formats):
|
if len(qualities) == len(m3u8_formats):
|
||||||
for q, m3u8_format in zip(qualities, m3u8_formats):
|
for q, m3u8_format in zip(qualities, m3u8_formats):
|
||||||
f = m3u8_format.copy()
|
f = m3u8_format.copy()
|
||||||
|
@@ -6,62 +6,52 @@ from .common import InfoExtractor
|
|||||||
from ..utils import (
|
from ..utils import (
|
||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
js_to_json,
|
|
||||||
unified_strdate,
|
unified_strdate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class GaskrankIE(InfoExtractor):
|
class GaskrankIE(InfoExtractor):
|
||||||
"""InfoExtractor for gaskrank.tv"""
|
_VALID_URL = r'https?://(?:www\.)?gaskrank\.tv/tv/(?P<categories>[^/]+)/(?P<id>[^/]+)\.htm'
|
||||||
_VALID_URL = r'https?://(?:www\.)?gaskrank\.tv/tv/(?P<categories>[^/]+)/(?P<id>[^/]+)\.html?'
|
_TESTS = [{
|
||||||
_TESTS = [
|
'url': 'http://www.gaskrank.tv/tv/motorrad-fun/strike-einparken-durch-anfaenger-crash-mit-groesserem-flurschaden.htm',
|
||||||
{
|
'md5': '1ae88dbac97887d85ebd1157a95fc4f9',
|
||||||
'url': 'http://www.gaskrank.tv/tv/motorrad-fun/strike-einparken-durch-anfaenger-crash-mit-groesserem-flurschaden.htm',
|
'info_dict': {
|
||||||
'md5': '1ae88dbac97887d85ebd1157a95fc4f9',
|
'id': '201601/26955',
|
||||||
'info_dict': {
|
'ext': 'mp4',
|
||||||
'id': '201601/26955',
|
'title': 'Strike! Einparken können nur Männer - Flurschaden hält sich in Grenzen *lol*',
|
||||||
'ext': 'mp4',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'title': 'Strike! Einparken können nur Männer - Flurschaden hält sich in Grenzen *lol*',
|
'categories': ['motorrad-fun'],
|
||||||
'thumbnail': r're:^https?://.*\.jpg$',
|
'display_id': 'strike-einparken-durch-anfaenger-crash-mit-groesserem-flurschaden',
|
||||||
'categories': ['motorrad-fun'],
|
'uploader_id': 'Bikefun',
|
||||||
'display_id': 'strike-einparken-durch-anfaenger-crash-mit-groesserem-flurschaden',
|
'upload_date': '20170110',
|
||||||
'uploader_id': 'Bikefun',
|
'uploader_url': None,
|
||||||
'upload_date': '20170110',
|
|
||||||
'uploader_url': None,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'url': 'http://www.gaskrank.tv/tv/racing/isle-of-man-tt-2011-michael-du-15920.htm',
|
|
||||||
'md5': 'c33ee32c711bc6c8224bfcbe62b23095',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '201106/15920',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Isle of Man - Michael Dunlop vs Guy Martin - schwindelig kucken',
|
|
||||||
'thumbnail': r're:^https?://.*\.jpg$',
|
|
||||||
'categories': ['racing'],
|
|
||||||
'display_id': 'isle-of-man-tt-2011-michael-du-15920',
|
|
||||||
'uploader_id': 'IOM',
|
|
||||||
'upload_date': '20160506',
|
|
||||||
'uploader_url': 'www.iomtt.com',
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
}, {
|
||||||
|
'url': 'http://www.gaskrank.tv/tv/racing/isle-of-man-tt-2011-michael-du-15920.htm',
|
||||||
|
'md5': 'c33ee32c711bc6c8224bfcbe62b23095',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '201106/15920',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Isle of Man - Michael Dunlop vs Guy Martin - schwindelig kucken',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
'categories': ['racing'],
|
||||||
|
'display_id': 'isle-of-man-tt-2011-michael-du-15920',
|
||||||
|
'uploader_id': 'IOM',
|
||||||
|
'upload_date': '20170523',
|
||||||
|
'uploader_url': 'www.iomtt.com',
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
"""extract information from gaskrank.tv"""
|
|
||||||
def fix_json(code):
|
|
||||||
"""Removes trailing comma in json: {{},} --> {{}}"""
|
|
||||||
return re.sub(r',\s*}', r'}', js_to_json(code))
|
|
||||||
|
|
||||||
display_id = self._match_id(url)
|
display_id = self._match_id(url)
|
||||||
|
|
||||||
webpage = self._download_webpage(url, display_id)
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
|
||||||
|
title = self._og_search_title(
|
||||||
|
webpage, default=None) or self._html_search_meta(
|
||||||
|
'title', webpage, fatal=True)
|
||||||
|
|
||||||
categories = [re.match(self._VALID_URL, url).group('categories')]
|
categories = [re.match(self._VALID_URL, url).group('categories')]
|
||||||
title = self._search_regex(
|
|
||||||
r'movieName\s*:\s*\'([^\']*)\'',
|
|
||||||
webpage, 'title')
|
|
||||||
thumbnail = self._search_regex(
|
|
||||||
r'poster\s*:\s*\'([^\']*)\'',
|
|
||||||
webpage, 'thumbnail', default=None)
|
|
||||||
|
|
||||||
mobj = re.search(
|
mobj = re.search(
|
||||||
r'Video von:\s*(?P<uploader_id>[^|]*?)\s*\|\s*vom:\s*(?P<upload_date>[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9][0-9][0-9])',
|
r'Video von:\s*(?P<uploader_id>[^|]*?)\s*\|\s*vom:\s*(?P<upload_date>[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9][0-9][0-9])',
|
||||||
@@ -89,29 +79,14 @@ class GaskrankIE(InfoExtractor):
|
|||||||
if average_rating:
|
if average_rating:
|
||||||
average_rating = float_or_none(average_rating.replace(',', '.'))
|
average_rating = float_or_none(average_rating.replace(',', '.'))
|
||||||
|
|
||||||
playlist = self._parse_json(
|
|
||||||
self._search_regex(
|
|
||||||
r'playlist\s*:\s*\[([^\]]*)\]',
|
|
||||||
webpage, 'playlist', default='{}'),
|
|
||||||
display_id, transform_source=fix_json, fatal=False)
|
|
||||||
|
|
||||||
video_id = self._search_regex(
|
video_id = self._search_regex(
|
||||||
r'https?://movies\.gaskrank\.tv/([^-]*?)(-[^\.]*)?\.mp4',
|
r'https?://movies\.gaskrank\.tv/([^-]*?)(-[^\.]*)?\.mp4',
|
||||||
playlist.get('0').get('src'), 'video id')
|
webpage, 'video id', default=display_id)
|
||||||
|
|
||||||
formats = []
|
entry = self._parse_html5_media_entries(url, webpage, video_id)[0]
|
||||||
for key in playlist:
|
entry.update({
|
||||||
formats.append({
|
|
||||||
'url': playlist[key]['src'],
|
|
||||||
'format_id': key,
|
|
||||||
'quality': playlist[key].get('quality')})
|
|
||||||
self._sort_formats(formats, field_preference=['format_id'])
|
|
||||||
|
|
||||||
return {
|
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': title,
|
'title': title,
|
||||||
'formats': formats,
|
|
||||||
'thumbnail': thumbnail,
|
|
||||||
'categories': categories,
|
'categories': categories,
|
||||||
'display_id': display_id,
|
'display_id': display_id,
|
||||||
'uploader_id': uploader_id,
|
'uploader_id': uploader_id,
|
||||||
@@ -120,4 +95,7 @@ class GaskrankIE(InfoExtractor):
|
|||||||
'tags': tags,
|
'tags': tags,
|
||||||
'view_count': view_count,
|
'view_count': view_count,
|
||||||
'average_rating': average_rating,
|
'average_rating': average_rating,
|
||||||
}
|
})
|
||||||
|
self._sort_formats(entry['formats'])
|
||||||
|
|
||||||
|
return entry
|
||||||
|
@@ -75,6 +75,19 @@ class GDCVaultIE(InfoExtractor):
|
|||||||
'format': 'jp', # The japanese audio
|
'format': 'jp', # The japanese audio
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
# gdc-player.html
|
||||||
|
'url': 'http://www.gdcvault.com/play/1435/An-American-engine-in-Tokyo',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1435',
|
||||||
|
'display_id': 'An-American-engine-in-Tokyo',
|
||||||
|
'ext': 'flv',
|
||||||
|
'title': 'An American Engine in Tokyo:/nThe collaboration of Epic Games and Square Enix/nFor THE LAST REMINANT',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True, # Requires rtmpdump
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
def _login(self, webpage_url, display_id):
|
def _login(self, webpage_url, display_id):
|
||||||
@@ -128,7 +141,7 @@ class GDCVaultIE(InfoExtractor):
|
|||||||
'title': title,
|
'title': title,
|
||||||
}
|
}
|
||||||
|
|
||||||
PLAYER_REGEX = r'<iframe src="(?P<xml_root>.+?)/player.*?\.html.*?".*?</iframe>'
|
PLAYER_REGEX = r'<iframe src="(?P<xml_root>.+?)/(?:gdc-)?player.*?\.html.*?".*?</iframe>'
|
||||||
|
|
||||||
xml_root = self._html_search_regex(
|
xml_root = self._html_search_regex(
|
||||||
PLAYER_REGEX, start_page, 'xml root', default=None)
|
PLAYER_REGEX, start_page, 'xml root', default=None)
|
||||||
|
@@ -10,6 +10,7 @@ from .common import InfoExtractor
|
|||||||
from .youtube import YoutubeIE
|
from .youtube import YoutubeIE
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_etree_fromstring,
|
compat_etree_fromstring,
|
||||||
|
compat_str,
|
||||||
compat_urllib_parse_unquote,
|
compat_urllib_parse_unquote,
|
||||||
compat_urlparse,
|
compat_urlparse,
|
||||||
compat_xml_parse_error,
|
compat_xml_parse_error,
|
||||||
@@ -86,6 +87,10 @@ from .openload import OpenloadIE
|
|||||||
from .videopress import VideoPressIE
|
from .videopress import VideoPressIE
|
||||||
from .rutube import RutubeIE
|
from .rutube import RutubeIE
|
||||||
from .limelight import LimelightBaseIE
|
from .limelight import LimelightBaseIE
|
||||||
|
from .anvato import AnvatoIE
|
||||||
|
from .washingtonpost import WashingtonPostIE
|
||||||
|
from .wistia import WistiaIE
|
||||||
|
from .mediaset import MediasetIE
|
||||||
|
|
||||||
|
|
||||||
class GenericIE(InfoExtractor):
|
class GenericIE(InfoExtractor):
|
||||||
@@ -1427,6 +1432,22 @@ class GenericIE(InfoExtractor):
|
|||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
# Brightcove embed with whitespace around attribute names
|
||||||
|
'url': 'http://www.stack.com/video/3167554373001/learn-to-hit-open-three-pointers-with-damian-lillard-s-baseline-drift-drill',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '3167554373001',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': "Learn to Hit Open Three-Pointers With Damian Lillard's Baseline Drift Drill",
|
||||||
|
'description': 'md5:57bacb0e0f29349de4972bfda3191713',
|
||||||
|
'uploader_id': '1079349493',
|
||||||
|
'upload_date': '20140207',
|
||||||
|
'timestamp': 1391810548,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
},
|
||||||
# Another form of arte.tv embed
|
# Another form of arte.tv embed
|
||||||
{
|
{
|
||||||
'url': 'http://www.tv-replay.fr/redirection/09-04-16/arte-reportage-arte-11508975.html',
|
'url': 'http://www.tv-replay.fr/redirection/09-04-16/arte-reportage-arte-11508975.html',
|
||||||
@@ -1677,6 +1698,42 @@ class GenericIE(InfoExtractor):
|
|||||||
},
|
},
|
||||||
'playlist_mincount': 5,
|
'playlist_mincount': 5,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'url': 'http://kron4.com/2017/04/28/standoff-with-walnut-creek-murder-suspect-ends-with-arrest/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'standoff-with-walnut-creek-murder-suspect-ends-with-arrest',
|
||||||
|
'title': 'Standoff with Walnut Creek murder suspect ends',
|
||||||
|
'description': 'md5:3ccc48a60fc9441eeccfc9c469ebf788',
|
||||||
|
},
|
||||||
|
'playlist_mincount': 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# WashingtonPost embed
|
||||||
|
'url': 'http://www.vanityfair.com/hollywood/2017/04/donald-trump-tv-pitches',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '8caf6e88-d0ec-11e5-90d3-34c2c42653ac',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': "No one has seen the drama series based on Trump's life \u2014 until now",
|
||||||
|
'description': 'Donald Trump wanted a weekly TV drama based on his life. It never aired. But The Washington Post recently obtained a scene from the pilot script — and enlisted actors.',
|
||||||
|
'timestamp': 1455216756,
|
||||||
|
'uploader': 'The Washington Post',
|
||||||
|
'upload_date': '20160211',
|
||||||
|
},
|
||||||
|
'add_ie': [WashingtonPostIE.ie_key()],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# Mediaset embed
|
||||||
|
'url': 'http://www.tgcom24.mediaset.it/politica/serracchiani-voglio-vivere-in-una-societa-aperta-reazioni-sproporzionate-_3071354-201702a.shtml',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '720642',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Serracchiani: "Voglio vivere in una società aperta, con tutela del patto di fiducia"',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
'add_ie': [MediasetIE.ie_key()],
|
||||||
|
},
|
||||||
# {
|
# {
|
||||||
# # TODO: find another test
|
# # TODO: find another test
|
||||||
# # http://schema.org/VideoObject
|
# # http://schema.org/VideoObject
|
||||||
@@ -1851,14 +1908,14 @@ class GenericIE(InfoExtractor):
|
|||||||
content_type = head_response.headers.get('Content-Type', '').lower()
|
content_type = head_response.headers.get('Content-Type', '').lower()
|
||||||
m = re.match(r'^(?P<type>audio|video|application(?=/(?:ogg$|(?:vnd\.apple\.|x-)?mpegurl)))/(?P<format_id>[^;\s]+)', content_type)
|
m = re.match(r'^(?P<type>audio|video|application(?=/(?:ogg$|(?:vnd\.apple\.|x-)?mpegurl)))/(?P<format_id>[^;\s]+)', content_type)
|
||||||
if m:
|
if m:
|
||||||
format_id = m.group('format_id')
|
format_id = compat_str(m.group('format_id'))
|
||||||
if format_id.endswith('mpegurl'):
|
if format_id.endswith('mpegurl'):
|
||||||
formats = self._extract_m3u8_formats(url, video_id, 'mp4')
|
formats = self._extract_m3u8_formats(url, video_id, 'mp4')
|
||||||
elif format_id == 'f4m':
|
elif format_id == 'f4m':
|
||||||
formats = self._extract_f4m_formats(url, video_id)
|
formats = self._extract_f4m_formats(url, video_id)
|
||||||
else:
|
else:
|
||||||
formats = [{
|
formats = [{
|
||||||
'format_id': m.group('format_id'),
|
'format_id': format_id,
|
||||||
'url': url,
|
'url': url,
|
||||||
'vcodec': 'none' if m.group('type') == 'audio' else None
|
'vcodec': 'none' if m.group('type') == 'audio' else None
|
||||||
}]
|
}]
|
||||||
@@ -2070,57 +2127,20 @@ class GenericIE(InfoExtractor):
|
|||||||
playlists, video_id, video_title, lambda p: '//dailymotion.com/playlist/%s' % p)
|
playlists, video_id, video_title, lambda p: '//dailymotion.com/playlist/%s' % p)
|
||||||
|
|
||||||
# Look for embedded Wistia player
|
# Look for embedded Wistia player
|
||||||
match = re.search(
|
wistia_url = WistiaIE._extract_url(webpage)
|
||||||
r'<(?:meta[^>]+?content|iframe[^>]+?src)=(["\'])(?P<url>(?:https?:)?//(?:fast\.)?wistia\.net/embed/iframe/.+?)\1', webpage)
|
if wistia_url:
|
||||||
if match:
|
|
||||||
embed_url = self._proto_relative_url(
|
|
||||||
unescapeHTML(match.group('url')))
|
|
||||||
return {
|
return {
|
||||||
'_type': 'url_transparent',
|
'_type': 'url_transparent',
|
||||||
'url': embed_url,
|
'url': self._proto_relative_url(wistia_url),
|
||||||
'ie_key': 'Wistia',
|
'ie_key': WistiaIE.ie_key(),
|
||||||
'uploader': video_uploader,
|
'uploader': video_uploader,
|
||||||
}
|
}
|
||||||
|
|
||||||
match = re.search(r'(?:id=["\']wistia_|data-wistia-?id=["\']|Wistia\.embed\(["\'])(?P<id>[^"\']+)', webpage)
|
|
||||||
if match:
|
|
||||||
return {
|
|
||||||
'_type': 'url_transparent',
|
|
||||||
'url': 'wistia:%s' % match.group('id'),
|
|
||||||
'ie_key': 'Wistia',
|
|
||||||
'uploader': video_uploader,
|
|
||||||
}
|
|
||||||
|
|
||||||
match = re.search(
|
|
||||||
r'''(?sx)
|
|
||||||
<script[^>]+src=(["'])(?:https?:)?//fast\.wistia\.com/assets/external/E-v1\.js\1[^>]*>.*?
|
|
||||||
<div[^>]+class=(["']).*?\bwistia_async_(?P<id>[a-z0-9]+)\b.*?\2
|
|
||||||
''', webpage)
|
|
||||||
if match:
|
|
||||||
return self.url_result(self._proto_relative_url(
|
|
||||||
'wistia:%s' % match.group('id')), 'Wistia')
|
|
||||||
|
|
||||||
# Look for SVT player
|
# Look for SVT player
|
||||||
svt_url = SVTIE._extract_url(webpage)
|
svt_url = SVTIE._extract_url(webpage)
|
||||||
if svt_url:
|
if svt_url:
|
||||||
return self.url_result(svt_url, 'SVT')
|
return self.url_result(svt_url, 'SVT')
|
||||||
|
|
||||||
# Look for embedded condenast player
|
|
||||||
matches = re.findall(
|
|
||||||
r'<iframe\s+(?:[a-zA-Z-]+="[^"]+"\s+)*?src="(https?://player\.cnevids\.com/embed/[^"]+")',
|
|
||||||
webpage)
|
|
||||||
if matches:
|
|
||||||
return {
|
|
||||||
'_type': 'playlist',
|
|
||||||
'entries': [{
|
|
||||||
'_type': 'url',
|
|
||||||
'ie_key': 'CondeNast',
|
|
||||||
'url': ma,
|
|
||||||
} for ma in matches],
|
|
||||||
'title': video_title,
|
|
||||||
'id': video_id,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Look for Bandcamp pages with custom domain
|
# Look for Bandcamp pages with custom domain
|
||||||
mobj = re.search(r'<meta property="og:url"[^>]*?content="(.*?bandcamp\.com.*?)"', webpage)
|
mobj = re.search(r'<meta property="og:url"[^>]*?content="(.*?bandcamp\.com.*?)"', webpage)
|
||||||
if mobj is not None:
|
if mobj is not None:
|
||||||
@@ -2514,28 +2534,11 @@ class GenericIE(InfoExtractor):
|
|||||||
return self.playlist_result(
|
return self.playlist_result(
|
||||||
limelight_urls, video_id, video_title, video_description)
|
limelight_urls, video_id, video_title, video_description)
|
||||||
|
|
||||||
mobj = re.search(r'LimelightPlayer\.doLoad(Media|Channel|ChannelList)\(["\'](?P<id>[a-z0-9]{32})', webpage)
|
# Look for Anvato embeds
|
||||||
if mobj:
|
anvato_urls = AnvatoIE._extract_urls(self, webpage, video_id)
|
||||||
lm = {
|
if anvato_urls:
|
||||||
'Media': 'media',
|
return self.playlist_result(
|
||||||
'Channel': 'channel',
|
anvato_urls, video_id, video_title, video_description)
|
||||||
'ChannelList': 'channel_list',
|
|
||||||
}
|
|
||||||
return self.url_result(smuggle_url('limelight:%s:%s' % (
|
|
||||||
lm[mobj.group(1)], mobj.group(2)), {'source_url': url}),
|
|
||||||
'Limelight%s' % mobj.group(1), mobj.group(2))
|
|
||||||
|
|
||||||
mobj = re.search(
|
|
||||||
r'''(?sx)
|
|
||||||
<object[^>]+class=(["\'])LimelightEmbeddedPlayerFlash\1[^>]*>.*?
|
|
||||||
<param[^>]+
|
|
||||||
name=(["\'])flashVars\2[^>]+
|
|
||||||
value=(["\'])(?:(?!\3).)*mediaId=(?P<id>[a-z0-9]{32})
|
|
||||||
''', webpage)
|
|
||||||
if mobj:
|
|
||||||
return self.url_result(smuggle_url(
|
|
||||||
'limelight:media:%s' % mobj.group('id'),
|
|
||||||
{'source_url': url}), 'LimelightMedia', mobj.group('id'))
|
|
||||||
|
|
||||||
# Look for AdobeTVVideo embeds
|
# Look for AdobeTVVideo embeds
|
||||||
mobj = re.search(
|
mobj = re.search(
|
||||||
@@ -2654,6 +2657,18 @@ class GenericIE(InfoExtractor):
|
|||||||
return self.playlist_from_matches(
|
return self.playlist_from_matches(
|
||||||
rutube_urls, ie=RutubeIE.ie_key())
|
rutube_urls, ie=RutubeIE.ie_key())
|
||||||
|
|
||||||
|
# Look for WashingtonPost embeds
|
||||||
|
wapo_urls = WashingtonPostIE._extract_urls(webpage)
|
||||||
|
if wapo_urls:
|
||||||
|
return self.playlist_from_matches(
|
||||||
|
wapo_urls, video_id, video_title, ie=WashingtonPostIE.ie_key())
|
||||||
|
|
||||||
|
# Look for Mediaset embeds
|
||||||
|
mediaset_urls = MediasetIE._extract_urls(webpage)
|
||||||
|
if mediaset_urls:
|
||||||
|
return self.playlist_from_matches(
|
||||||
|
mediaset_urls, video_id, video_title, ie=MediasetIE.ie_key())
|
||||||
|
|
||||||
# Looking for http://schema.org/VideoObject
|
# Looking for http://schema.org/VideoObject
|
||||||
json_ld = self._search_json_ld(
|
json_ld = self._search_json_ld(
|
||||||
webpage, video_id, default={}, expected_type='VideoObject')
|
webpage, video_id, default={}, expected_type='VideoObject')
|
||||||
|
@@ -82,7 +82,7 @@ class GfycatIE(InfoExtractor):
|
|||||||
video_url = gfy.get('%sUrl' % format_id)
|
video_url = gfy.get('%sUrl' % format_id)
|
||||||
if not video_url:
|
if not video_url:
|
||||||
continue
|
continue
|
||||||
filesize = gfy.get('%sSize' % format_id)
|
filesize = int_or_none(gfy.get('%sSize' % format_id))
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': video_url,
|
'url': video_url,
|
||||||
'format_id': format_id,
|
'format_id': format_id,
|
||||||
|
@@ -36,22 +36,26 @@ class GoIE(AdobePassIE):
|
|||||||
'requestor_id': 'DisneyXD',
|
'requestor_id': 'DisneyXD',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_VALID_URL = r'https?://(?:(?P<sub_domain>%s)\.)?go\.com/(?:[^/]+/)*(?:vdka(?P<id>\w+)|(?:[^/]+/)*(?P<display_id>[^/?#]+))' % '|'.join(_SITE_INFO.keys())
|
_VALID_URL = r'https?://(?:(?P<sub_domain>%s)\.)?go\.com/(?:(?:[^/]+/)*(?P<id>vdka\w+)|(?:[^/]+/)*(?P<display_id>[^/?#]+))' % '|'.join(_SITE_INFO.keys())
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://abc.go.com/shows/castle/video/most-recent/vdka0_g86w5onx',
|
'url': 'http://abc.go.com/shows/designated-survivor/video/most-recent/VDKA3807643',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '0_g86w5onx',
|
'id': 'VDKA3807643',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Sneak Peek: Language Arts',
|
'title': 'The Traitor in the White House',
|
||||||
'description': 'md5:7dcdab3b2d17e5217c953256af964e9c',
|
'description': 'md5:05b009d2d145a1e85d25111bd37222e8',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
# m3u8 download
|
# m3u8 download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://abc.go.com/shows/after-paradise/video/most-recent/vdka3335601',
|
'url': 'http://watchdisneyxd.go.com/doraemon',
|
||||||
'only_matching': True,
|
'info_dict': {
|
||||||
|
'title': 'Doraemon',
|
||||||
|
'id': 'SH55574025',
|
||||||
|
},
|
||||||
|
'playlist_mincount': 51,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://abc.go.com/shows/the-catch/episode-guide/season-01/10-the-wedding',
|
'url': 'http://abc.go.com/shows/the-catch/episode-guide/season-01/10-the-wedding',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
@@ -60,19 +64,36 @@ class GoIE(AdobePassIE):
|
|||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
def _extract_videos(self, brand, video_id='-1', show_id='-1'):
|
||||||
|
display_id = video_id if video_id != '-1' else show_id
|
||||||
|
return self._download_json(
|
||||||
|
'http://api.contents.watchabc.go.com/vp2/ws/contents/3000/videos/%s/001/-1/%s/-1/%s/-1/-1.json' % (brand, show_id, video_id),
|
||||||
|
display_id)['video']
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
sub_domain, video_id, display_id = re.match(self._VALID_URL, url).groups()
|
sub_domain, video_id, display_id = re.match(self._VALID_URL, url).groups()
|
||||||
|
site_info = self._SITE_INFO[sub_domain]
|
||||||
|
brand = site_info['brand']
|
||||||
if not video_id:
|
if not video_id:
|
||||||
webpage = self._download_webpage(url, display_id)
|
webpage = self._download_webpage(url, display_id)
|
||||||
video_id = self._search_regex(
|
video_id = self._search_regex(
|
||||||
# There may be inner quotes, e.g. data-video-id="'VDKA3609139'"
|
# There may be inner quotes, e.g. data-video-id="'VDKA3609139'"
|
||||||
# from http://freeform.go.com/shows/shadowhunters/episodes/season-2/1-this-guilty-blood
|
# from http://freeform.go.com/shows/shadowhunters/episodes/season-2/1-this-guilty-blood
|
||||||
r'data-video-id=["\']*VDKA(\w+)', webpage, 'video id')
|
r'data-video-id=["\']*(VDKA\w+)', webpage, 'video id', default=None)
|
||||||
site_info = self._SITE_INFO[sub_domain]
|
if not video_id:
|
||||||
brand = site_info['brand']
|
# show extraction works for Disney, DisneyJunior and DisneyXD
|
||||||
video_data = self._download_json(
|
# ABC and Freeform has different layout
|
||||||
'http://api.contents.watchabc.go.com/vp2/ws/contents/3000/videos/%s/001/-1/-1/-1/%s/-1/-1.json' % (brand, video_id),
|
show_id = self._search_regex(r'data-show-id=["\']*(SH\d+)', webpage, 'show id')
|
||||||
video_id)['video'][0]
|
videos = self._extract_videos(brand, show_id=show_id)
|
||||||
|
show_title = self._search_regex(r'data-show-title="([^"]+)"', webpage, 'show title', fatal=False)
|
||||||
|
entries = []
|
||||||
|
for video in videos:
|
||||||
|
entries.append(self.url_result(
|
||||||
|
video['url'], 'Go', video.get('id'), video.get('title')))
|
||||||
|
entries.reverse()
|
||||||
|
return self.playlist_result(entries, show_id, show_title)
|
||||||
|
video_data = self._extract_videos(brand, video_id)[0]
|
||||||
|
video_id = video_data['id']
|
||||||
title = video_data['title']
|
title = video_data['title']
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
@@ -105,7 +126,7 @@ class GoIE(AdobePassIE):
|
|||||||
self._initialize_geo_bypass(['US'])
|
self._initialize_geo_bypass(['US'])
|
||||||
entitlement = self._download_json(
|
entitlement = self._download_json(
|
||||||
'https://api.entitlement.watchabc.go.com/vp2/ws-secure/entitlement/2020/authorize.json',
|
'https://api.entitlement.watchabc.go.com/vp2/ws-secure/entitlement/2020/authorize.json',
|
||||||
video_id, data=urlencode_postdata(data), headers=self.geo_verification_headers())
|
video_id, data=urlencode_postdata(data))
|
||||||
errors = entitlement.get('errors', {}).get('errors', [])
|
errors = entitlement.get('errors', {}).get('errors', [])
|
||||||
if errors:
|
if errors:
|
||||||
for error in errors:
|
for error in errors:
|
||||||
|
@@ -1,66 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from .common import InfoExtractor
|
|
||||||
from .ooyala import OoyalaIE
|
|
||||||
from ..utils import js_to_json
|
|
||||||
|
|
||||||
|
|
||||||
class GodTVIE(InfoExtractor):
|
|
||||||
_VALID_URL = r'https?://(?:www\.)?god\.tv(?:/[^/]+)*/(?P<id>[^/?#&]+)'
|
|
||||||
_TESTS = [{
|
|
||||||
'url': 'http://god.tv/jesus-image/video/jesus-conference-2016/randy-needham',
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'lpd3g2MzE6D1g8zFAKz8AGpxWcpu6o_3',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Randy Needham',
|
|
||||||
'duration': 3615.08,
|
|
||||||
},
|
|
||||||
'params': {
|
|
||||||
'skip_download': True,
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
'url': 'http://god.tv/playlist/bible-study',
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'bible-study',
|
|
||||||
},
|
|
||||||
'playlist_mincount': 37,
|
|
||||||
}, {
|
|
||||||
'url': 'http://god.tv/node/15097',
|
|
||||||
'only_matching': True,
|
|
||||||
}, {
|
|
||||||
'url': 'http://god.tv/live/africa',
|
|
||||||
'only_matching': True,
|
|
||||||
}, {
|
|
||||||
'url': 'http://god.tv/liveevents',
|
|
||||||
'only_matching': True,
|
|
||||||
}]
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
|
||||||
display_id = self._match_id(url)
|
|
||||||
|
|
||||||
webpage = self._download_webpage(url, display_id)
|
|
||||||
|
|
||||||
settings = self._parse_json(
|
|
||||||
self._search_regex(
|
|
||||||
r'jQuery\.extend\(Drupal\.settings\s*,\s*({.+?})\);',
|
|
||||||
webpage, 'settings', default='{}'),
|
|
||||||
display_id, transform_source=js_to_json, fatal=False)
|
|
||||||
|
|
||||||
ooyala_id = None
|
|
||||||
|
|
||||||
if settings:
|
|
||||||
playlist = settings.get('playlist')
|
|
||||||
if playlist and isinstance(playlist, list):
|
|
||||||
entries = [
|
|
||||||
OoyalaIE._build_url_result(video['content_id'])
|
|
||||||
for video in playlist if video.get('content_id')]
|
|
||||||
if entries:
|
|
||||||
return self.playlist_result(entries, display_id)
|
|
||||||
ooyala_id = settings.get('ooyala', {}).get('content_id')
|
|
||||||
|
|
||||||
if not ooyala_id:
|
|
||||||
ooyala_id = self._search_regex(
|
|
||||||
r'["\']content_id["\']\s*:\s*(["\'])(?P<id>[\w-]+)\1',
|
|
||||||
webpage, 'ooyala id', group='id')
|
|
||||||
|
|
||||||
return OoyalaIE._build_url_result(ooyala_id)
|
|
@@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
|
compat_str,
|
||||||
compat_urlparse,
|
compat_urlparse,
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
@@ -46,7 +47,7 @@ class GolemIE(InfoExtractor):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
formats.append({
|
formats.append({
|
||||||
'format_id': e.tag,
|
'format_id': compat_str(e.tag),
|
||||||
'url': compat_urlparse.urljoin(self._PREFIX, url),
|
'url': compat_urlparse.urljoin(self._PREFIX, url),
|
||||||
'height': self._int(e.get('height'), 'height'),
|
'height': self._int(e.get('height'), 'height'),
|
||||||
'width': self._int(e.get('width'), 'width'),
|
'width': self._int(e.get('width'), 'width'),
|
||||||
|
@@ -69,19 +69,32 @@ class GoogleDriveIE(InfoExtractor):
|
|||||||
r'"fmt_stream_map"\s*,\s*"([^"]+)', webpage, 'fmt stream map').split(',')
|
r'"fmt_stream_map"\s*,\s*"([^"]+)', webpage, 'fmt stream map').split(',')
|
||||||
fmt_list = self._search_regex(r'"fmt_list"\s*,\s*"([^"]+)', webpage, 'fmt_list').split(',')
|
fmt_list = self._search_regex(r'"fmt_list"\s*,\s*"([^"]+)', webpage, 'fmt_list').split(',')
|
||||||
|
|
||||||
|
resolutions = {}
|
||||||
|
for fmt in fmt_list:
|
||||||
|
mobj = re.search(
|
||||||
|
r'^(?P<format_id>\d+)/(?P<width>\d+)[xX](?P<height>\d+)', fmt)
|
||||||
|
if mobj:
|
||||||
|
resolutions[mobj.group('format_id')] = (
|
||||||
|
int(mobj.group('width')), int(mobj.group('height')))
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for fmt, fmt_stream in zip(fmt_list, fmt_stream_map):
|
for fmt_stream in fmt_stream_map:
|
||||||
fmt_id, fmt_url = fmt_stream.split('|')
|
fmt_stream_split = fmt_stream.split('|')
|
||||||
resolution = fmt.split('/')[1]
|
if len(fmt_stream_split) < 2:
|
||||||
width, height = resolution.split('x')
|
continue
|
||||||
formats.append({
|
format_id, format_url = fmt_stream_split[:2]
|
||||||
'url': lowercase_escape(fmt_url),
|
f = {
|
||||||
'format_id': fmt_id,
|
'url': lowercase_escape(format_url),
|
||||||
'resolution': resolution,
|
'format_id': format_id,
|
||||||
'width': int_or_none(width),
|
'ext': self._FORMATS_EXT[format_id],
|
||||||
'height': int_or_none(height),
|
}
|
||||||
'ext': self._FORMATS_EXT[fmt_id],
|
resolution = resolutions.get(format_id)
|
||||||
})
|
if resolution:
|
||||||
|
f.update({
|
||||||
|
'width': resolution[0],
|
||||||
|
'height': resolution[0],
|
||||||
|
})
|
||||||
|
formats.append(f)
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -7,14 +7,19 @@ from .common import InfoExtractor
|
|||||||
class HGTVComShowIE(InfoExtractor):
|
class HGTVComShowIE(InfoExtractor):
|
||||||
IE_NAME = 'hgtv.com:show'
|
IE_NAME = 'hgtv.com:show'
|
||||||
_VALID_URL = r'https?://(?:www\.)?hgtv\.com/shows/[^/]+/(?P<id>[^/?#&]+)'
|
_VALID_URL = r'https?://(?:www\.)?hgtv\.com/shows/[^/]+/(?P<id>[^/?#&]+)'
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
'url': 'http://www.hgtv.com/shows/flip-or-flop/flip-or-flop-full-episodes-videos',
|
# data-module="video"
|
||||||
|
'url': 'http://www.hgtv.com/shows/flip-or-flop/flip-or-flop-full-episodes-season-4-videos',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'flip-or-flop-full-episodes-videos',
|
'id': 'flip-or-flop-full-episodes-season-4-videos',
|
||||||
'title': 'Flip or Flop Full Episodes',
|
'title': 'Flip or Flop Full Episodes',
|
||||||
},
|
},
|
||||||
'playlist_mincount': 15,
|
'playlist_mincount': 15,
|
||||||
}
|
}, {
|
||||||
|
# data-deferred-module="video"
|
||||||
|
'url': 'http://www.hgtv.com/shows/good-bones/episodes/an-old-victorian-house-gets-a-new-facelift',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
display_id = self._match_id(url)
|
display_id = self._match_id(url)
|
||||||
@@ -23,7 +28,7 @@ class HGTVComShowIE(InfoExtractor):
|
|||||||
|
|
||||||
config = self._parse_json(
|
config = self._parse_json(
|
||||||
self._search_regex(
|
self._search_regex(
|
||||||
r'(?s)data-module=["\']video["\'][^>]*>.*?<script[^>]+type=["\']text/x-config["\'][^>]*>(.+?)</script',
|
r'(?s)data-(?:deferred)?-module=["\']video["\'][^>]*>.*?<script[^>]+type=["\']text/x-config["\'][^>]*>(.+?)</script',
|
||||||
webpage, 'video config'),
|
webpage, 'video config'),
|
||||||
display_id)['channels'][0]
|
display_id)['channels'][0]
|
||||||
|
|
||||||
|
@@ -16,8 +16,8 @@ from ..utils import (
|
|||||||
|
|
||||||
class HitboxIE(InfoExtractor):
|
class HitboxIE(InfoExtractor):
|
||||||
IE_NAME = 'hitbox'
|
IE_NAME = 'hitbox'
|
||||||
_VALID_URL = r'https?://(?:www\.)?hitbox\.tv/video/(?P<id>[0-9]+)'
|
_VALID_URL = r'https?://(?:www\.)?(?:hitbox|smashcast)\.tv/(?:[^/]+/)*videos?/(?P<id>[0-9]+)'
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
'url': 'http://www.hitbox.tv/video/203213',
|
'url': 'http://www.hitbox.tv/video/203213',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '203213',
|
'id': '203213',
|
||||||
@@ -38,13 +38,15 @@ class HitboxIE(InfoExtractor):
|
|||||||
# m3u8 download
|
# m3u8 download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
}
|
}, {
|
||||||
|
'url': 'https://www.smashcast.tv/hitboxlive/videos/203213',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
def _extract_metadata(self, url, video_id):
|
def _extract_metadata(self, url, video_id):
|
||||||
thumb_base = 'https://edge.sf.hitbox.tv'
|
thumb_base = 'https://edge.sf.hitbox.tv'
|
||||||
metadata = self._download_json(
|
metadata = self._download_json(
|
||||||
'%s/%s' % (url, video_id), video_id,
|
'%s/%s' % (url, video_id), video_id, 'Downloading metadata JSON')
|
||||||
'Downloading metadata JSON')
|
|
||||||
|
|
||||||
date = 'media_live_since'
|
date = 'media_live_since'
|
||||||
media_type = 'livestream'
|
media_type = 'livestream'
|
||||||
@@ -63,14 +65,15 @@ class HitboxIE(InfoExtractor):
|
|||||||
views = int_or_none(video_meta.get('media_views'))
|
views = int_or_none(video_meta.get('media_views'))
|
||||||
timestamp = parse_iso8601(video_meta.get(date), ' ')
|
timestamp = parse_iso8601(video_meta.get(date), ' ')
|
||||||
categories = [video_meta.get('category_name')]
|
categories = [video_meta.get('category_name')]
|
||||||
thumbs = [
|
thumbs = [{
|
||||||
{'url': thumb_base + video_meta.get('media_thumbnail'),
|
'url': thumb_base + video_meta.get('media_thumbnail'),
|
||||||
'width': 320,
|
'width': 320,
|
||||||
'height': 180},
|
'height': 180
|
||||||
{'url': thumb_base + video_meta.get('media_thumbnail_large'),
|
}, {
|
||||||
'width': 768,
|
'url': thumb_base + video_meta.get('media_thumbnail_large'),
|
||||||
'height': 432},
|
'width': 768,
|
||||||
]
|
'height': 432
|
||||||
|
}]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
@@ -90,7 +93,7 @@ class HitboxIE(InfoExtractor):
|
|||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
player_config = self._download_json(
|
player_config = self._download_json(
|
||||||
'https://www.hitbox.tv/api/player/config/video/%s' % video_id,
|
'https://www.smashcast.tv/api/player/config/video/%s' % video_id,
|
||||||
video_id, 'Downloading video JSON')
|
video_id, 'Downloading video JSON')
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
@@ -121,8 +124,7 @@ class HitboxIE(InfoExtractor):
|
|||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
metadata = self._extract_metadata(
|
metadata = self._extract_metadata(
|
||||||
'https://www.hitbox.tv/api/media/video',
|
'https://www.smashcast.tv/api/media/video', video_id)
|
||||||
video_id)
|
|
||||||
metadata['formats'] = formats
|
metadata['formats'] = formats
|
||||||
|
|
||||||
return metadata
|
return metadata
|
||||||
@@ -130,8 +132,8 @@ class HitboxIE(InfoExtractor):
|
|||||||
|
|
||||||
class HitboxLiveIE(HitboxIE):
|
class HitboxLiveIE(HitboxIE):
|
||||||
IE_NAME = 'hitbox:live'
|
IE_NAME = 'hitbox:live'
|
||||||
_VALID_URL = r'https?://(?:www\.)?hitbox\.tv/(?!video)(?P<id>.+)'
|
_VALID_URL = r'https?://(?:www\.)?(?:hitbox|smashcast)\.tv/(?P<id>[^/?#&]+)'
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
'url': 'http://www.hitbox.tv/dimak',
|
'url': 'http://www.hitbox.tv/dimak',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'dimak',
|
'id': 'dimak',
|
||||||
@@ -146,13 +148,20 @@ class HitboxLiveIE(HitboxIE):
|
|||||||
# live
|
# live
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
}
|
}, {
|
||||||
|
'url': 'https://www.smashcast.tv/dimak',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def suitable(cls, url):
|
||||||
|
return False if HitboxIE.suitable(url) else super(HitboxLiveIE, cls).suitable(url)
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
player_config = self._download_json(
|
player_config = self._download_json(
|
||||||
'https://www.hitbox.tv/api/player/config/live/%s' % video_id,
|
'https://www.smashcast.tv/api/player/config/live/%s' % video_id,
|
||||||
video_id)
|
video_id)
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
@@ -197,8 +206,7 @@ class HitboxLiveIE(HitboxIE):
|
|||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
metadata = self._extract_metadata(
|
metadata = self._extract_metadata(
|
||||||
'https://www.hitbox.tv/api/media/live',
|
'https://www.smashcast.tv/api/media/live', video_id)
|
||||||
video_id)
|
|
||||||
metadata['formats'] = formats
|
metadata['formats'] = formats
|
||||||
metadata['is_live'] = True
|
metadata['is_live'] = True
|
||||||
metadata['title'] = self._live_title(metadata.get('title'))
|
metadata['title'] = self._live_title(metadata.get('title'))
|
||||||
|
@@ -13,7 +13,7 @@ from ..utils import (
|
|||||||
class ImdbIE(InfoExtractor):
|
class ImdbIE(InfoExtractor):
|
||||||
IE_NAME = 'imdb'
|
IE_NAME = 'imdb'
|
||||||
IE_DESC = 'Internet Movie Database trailers'
|
IE_DESC = 'Internet Movie Database trailers'
|
||||||
_VALID_URL = r'https?://(?:www|m)\.imdb\.com/(?:video/[^/]+/|title/tt\d+.*?#lb-|videoplayer/)vi(?P<id>\d+)'
|
_VALID_URL = r'https?://(?:www|m)\.imdb\.com/(?:video|title).+?[/-]vi(?P<id>\d+)'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.imdb.com/video/imdb/vi2524815897',
|
'url': 'http://www.imdb.com/video/imdb/vi2524815897',
|
||||||
@@ -35,6 +35,9 @@ class ImdbIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://www.imdb.com/videoplayer/vi1562949145',
|
'url': 'http://www.imdb.com/videoplayer/vi1562949145',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.imdb.com/title/tt4218696/videoplayer/vi2608641561',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
@@ -87,8 +87,8 @@ class InfoQIE(BokeCCBaseIE):
|
|||||||
|
|
||||||
def _extract_http_audio(self, webpage, video_id):
|
def _extract_http_audio(self, webpage, video_id):
|
||||||
fields = self._hidden_inputs(webpage)
|
fields = self._hidden_inputs(webpage)
|
||||||
http_audio_url = fields['filename']
|
http_audio_url = fields.get('filename')
|
||||||
if http_audio_url is None:
|
if not http_audio_url:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
cookies_header = {'Cookie': self._extract_cookies(webpage)}
|
cookies_header = {'Cookie': self._extract_cookies(webpage)}
|
||||||
|
@@ -65,9 +65,9 @@ class JoveIE(InfoExtractor):
|
|||||||
webpage, 'description', fatal=False)
|
webpage, 'description', fatal=False)
|
||||||
publish_date = unified_strdate(self._html_search_meta(
|
publish_date = unified_strdate(self._html_search_meta(
|
||||||
'citation_publication_date', webpage, 'publish date', fatal=False))
|
'citation_publication_date', webpage, 'publish date', fatal=False))
|
||||||
comment_count = self._html_search_regex(
|
comment_count = int(self._html_search_regex(
|
||||||
r'<meta name="num_comments" content="(\d+) Comments?"',
|
r'<meta name="num_comments" content="(\d+) Comments?"',
|
||||||
webpage, 'comment count', fatal=False)
|
webpage, 'comment count', fatal=False))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
@@ -8,15 +10,15 @@ from ..utils import (
|
|||||||
urlencode_postdata,
|
urlencode_postdata,
|
||||||
xpath_element,
|
xpath_element,
|
||||||
xpath_text,
|
xpath_text,
|
||||||
urljoin,
|
|
||||||
update_url_query,
|
update_url_query,
|
||||||
|
js_to_json,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Laola1TvEmbedIE(InfoExtractor):
|
class Laola1TvEmbedIE(InfoExtractor):
|
||||||
IE_NAME = 'laola1tv:embed'
|
IE_NAME = 'laola1tv:embed'
|
||||||
_VALID_URL = r'https?://(?:www\.)?laola1\.tv/titanplayer\.php\?.*?\bvideoid=(?P<id>\d+)'
|
_VALID_URL = r'https?://(?:www\.)?laola1\.tv/titanplayer\.php\?.*?\bvideoid=(?P<id>\d+)'
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
# flashvars.premium = "false";
|
# flashvars.premium = "false";
|
||||||
'url': 'https://www.laola1.tv/titanplayer.php?videoid=708065&type=V&lang=en&portal=int&customer=1024',
|
'url': 'https://www.laola1.tv/titanplayer.php?videoid=708065&type=V&lang=en&portal=int&customer=1024',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@@ -26,7 +28,30 @@ class Laola1TvEmbedIE(InfoExtractor):
|
|||||||
'uploader': 'ITTF - International Table Tennis Federation',
|
'uploader': 'ITTF - International Table Tennis Federation',
|
||||||
'upload_date': '20161211',
|
'upload_date': '20161211',
|
||||||
},
|
},
|
||||||
}
|
}]
|
||||||
|
|
||||||
|
def _extract_token_url(self, stream_access_url, video_id, data):
|
||||||
|
return self._download_json(
|
||||||
|
stream_access_url, video_id, headers={
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}, data=json.dumps(data).encode())['data']['stream-access'][0]
|
||||||
|
|
||||||
|
def _extract_formats(self, token_url, video_id):
|
||||||
|
token_doc = self._download_xml(
|
||||||
|
token_url, video_id, 'Downloading token',
|
||||||
|
headers=self.geo_verification_headers())
|
||||||
|
|
||||||
|
token_attrib = xpath_element(token_doc, './/token').attrib
|
||||||
|
|
||||||
|
if token_attrib['status'] != '0':
|
||||||
|
raise ExtractorError(
|
||||||
|
'Token error: %s' % token_attrib['comment'], expected=True)
|
||||||
|
|
||||||
|
formats = self._extract_akamai_formats(
|
||||||
|
'%s?hdnea=%s' % (token_attrib['url'], token_attrib['auth']),
|
||||||
|
video_id)
|
||||||
|
self._sort_formats(formats)
|
||||||
|
return formats
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
@@ -68,29 +93,16 @@ class Laola1TvEmbedIE(InfoExtractor):
|
|||||||
else:
|
else:
|
||||||
data_abo = urlencode_postdata(
|
data_abo = urlencode_postdata(
|
||||||
dict((i, v) for i, v in enumerate(_v('req_liga_abos').split(','))))
|
dict((i, v) for i, v in enumerate(_v('req_liga_abos').split(','))))
|
||||||
token_url = self._download_json(
|
stream_access_url = update_url_query(
|
||||||
'https://club.laola1.tv/sp/laola1/api/v3/user/session/premium/player/stream-access',
|
'https://club.laola1.tv/sp/laola1/api/v3/user/session/premium/player/stream-access', {
|
||||||
video_id, query={
|
|
||||||
'videoId': _v('id'),
|
'videoId': _v('id'),
|
||||||
'target': self._search_regex(r'vs_target = (\d+);', webpage, 'vs target'),
|
'target': self._search_regex(r'vs_target = (\d+);', webpage, 'vs target'),
|
||||||
'label': _v('label'),
|
'label': _v('label'),
|
||||||
'area': _v('area'),
|
'area': _v('area'),
|
||||||
}, data=data_abo)['data']['stream-access'][0]
|
})
|
||||||
|
token_url = self._extract_token_url(stream_access_url, video_id, data_abo)
|
||||||
|
|
||||||
token_doc = self._download_xml(
|
formats = self._extract_formats(token_url, video_id)
|
||||||
token_url, video_id, 'Downloading token',
|
|
||||||
headers=self.geo_verification_headers())
|
|
||||||
|
|
||||||
token_attrib = xpath_element(token_doc, './/token').attrib
|
|
||||||
|
|
||||||
if token_attrib['status'] != '0':
|
|
||||||
raise ExtractorError(
|
|
||||||
'Token error: %s' % token_attrib['comment'], expected=True)
|
|
||||||
|
|
||||||
formats = self._extract_akamai_formats(
|
|
||||||
'%s?hdnea=%s' % (token_attrib['url'], token_attrib['auth']),
|
|
||||||
video_id)
|
|
||||||
self._sort_formats(formats)
|
|
||||||
|
|
||||||
categories_str = _v('meta_sports')
|
categories_str = _v('meta_sports')
|
||||||
categories = categories_str.split(',') if categories_str else []
|
categories = categories_str.split(',') if categories_str else []
|
||||||
@@ -107,7 +119,7 @@ class Laola1TvEmbedIE(InfoExtractor):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Laola1TvIE(InfoExtractor):
|
class Laola1TvIE(Laola1TvEmbedIE):
|
||||||
IE_NAME = 'laola1tv'
|
IE_NAME = 'laola1tv'
|
||||||
_VALID_URL = r'https?://(?:www\.)?laola1\.tv/[a-z]+-[a-z]+/[^/]+/(?P<id>[^/?#&]+)'
|
_VALID_URL = r'https?://(?:www\.)?laola1\.tv/[a-z]+-[a-z]+/[^/]+/(?P<id>[^/?#&]+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
@@ -164,13 +176,42 @@ class Laola1TvIE(InfoExtractor):
|
|||||||
if 'Dieser Livestream ist bereits beendet.' in webpage:
|
if 'Dieser Livestream ist bereits beendet.' in webpage:
|
||||||
raise ExtractorError('This live stream has already finished.', expected=True)
|
raise ExtractorError('This live stream has already finished.', expected=True)
|
||||||
|
|
||||||
iframe_url = urljoin(url, self._search_regex(
|
conf = self._parse_json(self._search_regex(
|
||||||
r'<iframe[^>]*?id="videoplayer"[^>]*?src="([^"]+)"',
|
r'(?s)conf\s*=\s*({.+?});', webpage, 'conf'),
|
||||||
webpage, 'iframe url'))
|
display_id, js_to_json)
|
||||||
|
|
||||||
|
video_id = conf['videoid']
|
||||||
|
|
||||||
|
config = self._download_json(conf['configUrl'], video_id, query={
|
||||||
|
'videoid': video_id,
|
||||||
|
'partnerid': conf['partnerid'],
|
||||||
|
'language': conf.get('language', ''),
|
||||||
|
'portal': conf.get('portalid', ''),
|
||||||
|
})
|
||||||
|
error = config.get('error')
|
||||||
|
if error:
|
||||||
|
raise ExtractorError('%s said: %s' % (self.IE_NAME, error), expected=True)
|
||||||
|
|
||||||
|
video_data = config['video']
|
||||||
|
title = video_data['title']
|
||||||
|
is_live = video_data.get('isLivestream') and video_data.get('isLive')
|
||||||
|
meta = video_data.get('metaInformation')
|
||||||
|
sports = meta.get('sports')
|
||||||
|
categories = sports.split(',') if sports else []
|
||||||
|
|
||||||
|
token_url = self._extract_token_url(
|
||||||
|
video_data['streamAccess'], video_id,
|
||||||
|
video_data['abo']['required'])
|
||||||
|
|
||||||
|
formats = self._extract_formats(token_url, video_id)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'_type': 'url',
|
'id': video_id,
|
||||||
'display_id': display_id,
|
'display_id': display_id,
|
||||||
'url': iframe_url,
|
'title': self._live_title(title) if is_live else title,
|
||||||
'ie_key': 'Laola1TvEmbed',
|
'description': video_data.get('description'),
|
||||||
|
'thumbnail': video_data.get('image'),
|
||||||
|
'categories': categories,
|
||||||
|
'formats': formats,
|
||||||
|
'is_live': is_live,
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,6 @@ from ..utils import (
|
|||||||
str_or_none,
|
str_or_none,
|
||||||
url_basename,
|
url_basename,
|
||||||
urshift,
|
urshift,
|
||||||
update_url_query,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -51,7 +50,7 @@ class LeIE(InfoExtractor):
|
|||||||
'id': '1415246',
|
'id': '1415246',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': '美人天下01',
|
'title': '美人天下01',
|
||||||
'description': 'md5:f88573d9d7225ada1359eaf0dbf8bcda',
|
'description': 'md5:28942e650e82ed4fcc8e4de919ee854d',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
'hls_prefer_native': True,
|
'hls_prefer_native': True,
|
||||||
@@ -69,7 +68,6 @@ class LeIE(InfoExtractor):
|
|||||||
'params': {
|
'params': {
|
||||||
'hls_prefer_native': True,
|
'hls_prefer_native': True,
|
||||||
},
|
},
|
||||||
'skip': 'Only available in China',
|
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://sports.le.com/video/25737697.html',
|
'url': 'http://sports.le.com/video/25737697.html',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
@@ -81,7 +79,7 @@ class LeIE(InfoExtractor):
|
|||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
# ror() and calc_time_key() are reversed from a embedded swf file in KLetvPlayer.swf
|
# ror() and calc_time_key() are reversed from a embedded swf file in LetvPlayer.swf
|
||||||
def ror(self, param1, param2):
|
def ror(self, param1, param2):
|
||||||
_loc3_ = 0
|
_loc3_ = 0
|
||||||
while _loc3_ < param2:
|
while _loc3_ < param2:
|
||||||
@@ -90,15 +88,8 @@ class LeIE(InfoExtractor):
|
|||||||
return param1
|
return param1
|
||||||
|
|
||||||
def calc_time_key(self, param1):
|
def calc_time_key(self, param1):
|
||||||
_loc2_ = 773625421
|
_loc2_ = 185025305
|
||||||
_loc3_ = self.ror(param1, _loc2_ % 13)
|
return self.ror(param1, _loc2_ % 17) ^ _loc2_
|
||||||
_loc3_ = _loc3_ ^ _loc2_
|
|
||||||
_loc3_ = self.ror(_loc3_, _loc2_ % 17)
|
|
||||||
return _loc3_
|
|
||||||
|
|
||||||
# reversed from http://jstatic.letvcdn.com/sdk/player.js
|
|
||||||
def get_mms_key(self, time):
|
|
||||||
return self.ror(time, 8) ^ 185025305
|
|
||||||
|
|
||||||
# see M3U8Encryption class in KLetvPlayer.swf
|
# see M3U8Encryption class in KLetvPlayer.swf
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -122,7 +113,7 @@ class LeIE(InfoExtractor):
|
|||||||
|
|
||||||
def _check_errors(self, play_json):
|
def _check_errors(self, play_json):
|
||||||
# Check for errors
|
# Check for errors
|
||||||
playstatus = play_json['playstatus']
|
playstatus = play_json['msgs']['playstatus']
|
||||||
if playstatus['status'] == 0:
|
if playstatus['status'] == 0:
|
||||||
flag = playstatus['flag']
|
flag = playstatus['flag']
|
||||||
if flag == 1:
|
if flag == 1:
|
||||||
@@ -134,58 +125,31 @@ class LeIE(InfoExtractor):
|
|||||||
media_id = self._match_id(url)
|
media_id = self._match_id(url)
|
||||||
page = self._download_webpage(url, media_id)
|
page = self._download_webpage(url, media_id)
|
||||||
|
|
||||||
play_json_h5 = self._download_json(
|
|
||||||
'http://api.le.com/mms/out/video/playJsonH5',
|
|
||||||
media_id, 'Downloading html5 playJson data', query={
|
|
||||||
'id': media_id,
|
|
||||||
'platid': 3,
|
|
||||||
'splatid': 304,
|
|
||||||
'format': 1,
|
|
||||||
'tkey': self.get_mms_key(int(time.time())),
|
|
||||||
'domain': 'www.le.com',
|
|
||||||
'tss': 'no',
|
|
||||||
},
|
|
||||||
headers=self.geo_verification_headers())
|
|
||||||
self._check_errors(play_json_h5)
|
|
||||||
|
|
||||||
play_json_flash = self._download_json(
|
play_json_flash = self._download_json(
|
||||||
'http://api.le.com/mms/out/video/playJson',
|
'http://player-pc.le.com/mms/out/video/playJson',
|
||||||
media_id, 'Downloading flash playJson data', query={
|
media_id, 'Downloading flash playJson data', query={
|
||||||
'id': media_id,
|
'id': media_id,
|
||||||
'platid': 1,
|
'platid': 1,
|
||||||
'splatid': 101,
|
'splatid': 101,
|
||||||
'format': 1,
|
'format': 1,
|
||||||
|
'source': 1000,
|
||||||
'tkey': self.calc_time_key(int(time.time())),
|
'tkey': self.calc_time_key(int(time.time())),
|
||||||
'domain': 'www.le.com',
|
'domain': 'www.le.com',
|
||||||
|
'region': 'cn',
|
||||||
},
|
},
|
||||||
headers=self.geo_verification_headers())
|
headers=self.geo_verification_headers())
|
||||||
self._check_errors(play_json_flash)
|
self._check_errors(play_json_flash)
|
||||||
|
|
||||||
def get_h5_urls(media_url, format_id):
|
|
||||||
location = self._download_json(
|
|
||||||
media_url, media_id,
|
|
||||||
'Download JSON metadata for format %s' % format_id, query={
|
|
||||||
'format': 1,
|
|
||||||
'expect': 3,
|
|
||||||
'tss': 'no',
|
|
||||||
})['location']
|
|
||||||
|
|
||||||
return {
|
|
||||||
'http': update_url_query(location, {'tss': 'no'}),
|
|
||||||
'hls': update_url_query(location, {'tss': 'ios'}),
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_flash_urls(media_url, format_id):
|
def get_flash_urls(media_url, format_id):
|
||||||
media_url += '&' + compat_urllib_parse_urlencode({
|
|
||||||
'm3v': 1,
|
|
||||||
'format': 1,
|
|
||||||
'expect': 3,
|
|
||||||
'rateid': format_id,
|
|
||||||
})
|
|
||||||
|
|
||||||
nodes_data = self._download_json(
|
nodes_data = self._download_json(
|
||||||
media_url, media_id,
|
media_url, media_id,
|
||||||
'Download JSON metadata for format %s' % format_id)
|
'Download JSON metadata for format %s' % format_id,
|
||||||
|
query={
|
||||||
|
'm3v': 1,
|
||||||
|
'format': 1,
|
||||||
|
'expect': 3,
|
||||||
|
'tss': 'ios',
|
||||||
|
})
|
||||||
|
|
||||||
req = self._request_webpage(
|
req = self._request_webpage(
|
||||||
nodes_data['nodelist'][0]['location'], media_id,
|
nodes_data['nodelist'][0]['location'], media_id,
|
||||||
@@ -199,29 +163,28 @@ class LeIE(InfoExtractor):
|
|||||||
|
|
||||||
extracted_formats = []
|
extracted_formats = []
|
||||||
formats = []
|
formats = []
|
||||||
for play_json, get_urls in ((play_json_h5, get_h5_urls), (play_json_flash, get_flash_urls)):
|
playurl = play_json_flash['msgs']['playurl']
|
||||||
playurl = play_json['playurl']
|
play_domain = playurl['domain'][0]
|
||||||
play_domain = playurl['domain'][0]
|
|
||||||
|
|
||||||
for format_id, format_data in playurl.get('dispatch', []).items():
|
for format_id, format_data in playurl.get('dispatch', []).items():
|
||||||
if format_id in extracted_formats:
|
if format_id in extracted_formats:
|
||||||
continue
|
continue
|
||||||
extracted_formats.append(format_id)
|
extracted_formats.append(format_id)
|
||||||
|
|
||||||
media_url = play_domain + format_data[0]
|
media_url = play_domain + format_data[0]
|
||||||
for protocol, format_url in get_urls(media_url, format_id).items():
|
for protocol, format_url in get_flash_urls(media_url, format_id).items():
|
||||||
f = {
|
f = {
|
||||||
'url': format_url,
|
'url': format_url,
|
||||||
'ext': determine_ext(format_data[1]),
|
'ext': determine_ext(format_data[1]),
|
||||||
'format_id': '%s-%s' % (protocol, format_id),
|
'format_id': '%s-%s' % (protocol, format_id),
|
||||||
'protocol': 'm3u8_native' if protocol == 'hls' else 'http',
|
'protocol': 'm3u8_native' if protocol == 'hls' else 'http',
|
||||||
'quality': int_or_none(format_id),
|
'quality': int_or_none(format_id),
|
||||||
}
|
}
|
||||||
|
|
||||||
if format_id[-1:] == 'p':
|
if format_id[-1:] == 'p':
|
||||||
f['height'] = int_or_none(format_id[:-1])
|
f['height'] = int_or_none(format_id[:-1])
|
||||||
|
|
||||||
formats.append(f)
|
formats.append(f)
|
||||||
self._sort_formats(formats, ('height', 'quality', 'format_id'))
|
self._sort_formats(formats, ('height', 'quality', 'format_id'))
|
||||||
|
|
||||||
publish_time = parse_iso8601(self._html_search_regex(
|
publish_time = parse_iso8601(self._html_search_regex(
|
||||||
|
@@ -86,7 +86,7 @@ class LEGOIE(InfoExtractor):
|
|||||||
formats = self._extract_akamai_formats(
|
formats = self._extract_akamai_formats(
|
||||||
'%si/s/public/%s_,%s,.mp4.csmil/master.m3u8' % (streaming_base, path, streaming_path), video_id)
|
'%si/s/public/%s_,%s,.mp4.csmil/master.m3u8' % (streaming_base, path, streaming_path), video_id)
|
||||||
m3u8_formats = list(filter(
|
m3u8_formats = list(filter(
|
||||||
lambda f: f.get('protocol') == 'm3u8_native' and f.get('vcodec') != 'none' and f.get('resolution') != 'multiple',
|
lambda f: f.get('protocol') == 'm3u8_native' and f.get('vcodec') != 'none',
|
||||||
formats))
|
formats))
|
||||||
if len(m3u8_formats) == len(self._BITRATES):
|
if len(m3u8_formats) == len(self._BITRATES):
|
||||||
self._sort_formats(m3u8_formats)
|
self._sort_formats(m3u8_formats)
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import json
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
@@ -11,10 +10,10 @@ class LiveLeakIE(InfoExtractor):
|
|||||||
_VALID_URL = r'https?://(?:\w+\.)?liveleak\.com/view\?(?:.*?)i=(?P<id>[\w_]+)(?:.*)'
|
_VALID_URL = r'https?://(?:\w+\.)?liveleak\.com/view\?(?:.*?)i=(?P<id>[\w_]+)(?:.*)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.liveleak.com/view?i=757_1364311680',
|
'url': 'http://www.liveleak.com/view?i=757_1364311680',
|
||||||
'md5': '50f79e05ba149149c1b4ea961223d5b3',
|
'md5': '0813c2430bea7a46bf13acf3406992f4',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '757_1364311680',
|
'id': '757_1364311680',
|
||||||
'ext': 'flv',
|
'ext': 'mp4',
|
||||||
'description': 'extremely bad day for this guy..!',
|
'description': 'extremely bad day for this guy..!',
|
||||||
'uploader': 'ljfriel2',
|
'uploader': 'ljfriel2',
|
||||||
'title': 'Most unlucky car accident',
|
'title': 'Most unlucky car accident',
|
||||||
@@ -22,7 +21,7 @@ class LiveLeakIE(InfoExtractor):
|
|||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.liveleak.com/view?i=f93_1390833151',
|
'url': 'http://www.liveleak.com/view?i=f93_1390833151',
|
||||||
'md5': 'b13a29626183c9d33944e6a04f41aafc',
|
'md5': 'd3f1367d14cc3c15bf24fbfbe04b9abf',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'f93_1390833151',
|
'id': 'f93_1390833151',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
@@ -32,6 +31,7 @@ class LiveLeakIE(InfoExtractor):
|
|||||||
'thumbnail': r're:^https?://.*\.jpg$'
|
'thumbnail': r're:^https?://.*\.jpg$'
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
# Prochan embed
|
||||||
'url': 'http://www.liveleak.com/view?i=4f7_1392687779',
|
'url': 'http://www.liveleak.com/view?i=4f7_1392687779',
|
||||||
'md5': '42c6d97d54f1db107958760788c5f48f',
|
'md5': '42c6d97d54f1db107958760788c5f48f',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@@ -41,11 +41,13 @@ class LiveLeakIE(InfoExtractor):
|
|||||||
'uploader': 'CapObveus',
|
'uploader': 'CapObveus',
|
||||||
'title': 'Man is Fatally Struck by Reckless Car While Packing up a Moving Truck',
|
'title': 'Man is Fatally Struck by Reckless Car While Packing up a Moving Truck',
|
||||||
'age_limit': 18,
|
'age_limit': 18,
|
||||||
}
|
},
|
||||||
|
'skip': 'Video is dead',
|
||||||
}, {
|
}, {
|
||||||
# Covers https://github.com/rg3/youtube-dl/pull/5983
|
# Covers https://github.com/rg3/youtube-dl/pull/5983
|
||||||
|
# Multiple resolutions
|
||||||
'url': 'http://www.liveleak.com/view?i=801_1409392012',
|
'url': 'http://www.liveleak.com/view?i=801_1409392012',
|
||||||
'md5': '0b3bec2d888c20728ca2ad3642f0ef15',
|
'md5': 'c3a449dbaca5c0d1825caecd52a57d7b',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '801_1409392012',
|
'id': '801_1409392012',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
@@ -93,57 +95,39 @@ class LiveLeakIE(InfoExtractor):
|
|||||||
webpage, 'age limit', default=None))
|
webpage, 'age limit', default=None))
|
||||||
video_thumbnail = self._og_search_thumbnail(webpage)
|
video_thumbnail = self._og_search_thumbnail(webpage)
|
||||||
|
|
||||||
sources_raw = self._search_regex(
|
entries = self._parse_html5_media_entries(url, webpage, video_id)
|
||||||
r'(?s)sources:\s*(\[.*?\]),', webpage, 'video URLs', default=None)
|
if not entries:
|
||||||
if sources_raw is None:
|
# Maybe an embed?
|
||||||
alt_source = self._search_regex(
|
embed_url = self._search_regex(
|
||||||
r'(file: ".*?"),', webpage, 'video URL', default=None)
|
r'<iframe[^>]+src="((?:https?:)?//(?:www\.)?(?:prochan|youtube)\.com/embed[^"]+)"',
|
||||||
if alt_source:
|
webpage, 'embed URL')
|
||||||
sources_raw = '[{ %s}]' % alt_source
|
return {
|
||||||
else:
|
'_type': 'url_transparent',
|
||||||
# Maybe an embed?
|
'url': embed_url,
|
||||||
embed_url = self._search_regex(
|
'id': video_id,
|
||||||
r'<iframe[^>]+src="(https?://(?:www\.)?(?:prochan|youtube)\.com/embed[^"]+)"',
|
'title': video_title,
|
||||||
webpage, 'embed URL')
|
'description': video_description,
|
||||||
return {
|
'uploader': video_uploader,
|
||||||
'_type': 'url_transparent',
|
'age_limit': age_limit,
|
||||||
'url': embed_url,
|
}
|
||||||
'id': video_id,
|
|
||||||
'title': video_title,
|
|
||||||
'description': video_description,
|
|
||||||
'uploader': video_uploader,
|
|
||||||
'age_limit': age_limit,
|
|
||||||
}
|
|
||||||
|
|
||||||
sources_json = re.sub(r'\s([a-z]+):\s', r'"\1": ', sources_raw)
|
info_dict = entries[0]
|
||||||
sources = json.loads(sources_json)
|
|
||||||
|
|
||||||
formats = [{
|
for a_format in info_dict['formats']:
|
||||||
'format_id': '%s' % i,
|
if not a_format.get('height'):
|
||||||
'format_note': s.get('label'),
|
a_format['height'] = int_or_none(self._search_regex(
|
||||||
'url': s['file'],
|
r'([0-9]+)p\.mp4', a_format['url'], 'height label',
|
||||||
} for i, s in enumerate(sources)]
|
default=None))
|
||||||
|
|
||||||
for i, s in enumerate(sources):
|
self._sort_formats(info_dict['formats'])
|
||||||
# Removing '.h264_*.mp4' gives the raw video, which is essentially
|
|
||||||
# the same video without the LiveLeak logo at the top (see
|
|
||||||
# https://github.com/rg3/youtube-dl/pull/4768)
|
|
||||||
orig_url = re.sub(r'\.h264_.+?\.mp4', '', s['file'])
|
|
||||||
if s['file'] != orig_url:
|
|
||||||
formats.append({
|
|
||||||
'format_id': 'original-%s' % i,
|
|
||||||
'format_note': s.get('label'),
|
|
||||||
'url': orig_url,
|
|
||||||
'preference': 1,
|
|
||||||
})
|
|
||||||
self._sort_formats(formats)
|
|
||||||
|
|
||||||
return {
|
info_dict.update({
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': video_title,
|
'title': video_title,
|
||||||
'description': video_description,
|
'description': video_description,
|
||||||
'uploader': video_uploader,
|
'uploader': video_uploader,
|
||||||
'formats': formats,
|
|
||||||
'age_limit': age_limit,
|
'age_limit': age_limit,
|
||||||
'thumbnail': video_thumbnail,
|
'thumbnail': video_thumbnail,
|
||||||
}
|
})
|
||||||
|
|
||||||
|
return info_dict
|
||||||
|
@@ -17,7 +17,7 @@ from ..utils import (
|
|||||||
class MedialaanIE(InfoExtractor):
|
class MedialaanIE(InfoExtractor):
|
||||||
_VALID_URL = r'''(?x)
|
_VALID_URL = r'''(?x)
|
||||||
https?://
|
https?://
|
||||||
(?:www\.)?
|
(?:www\.|nieuws\.)?
|
||||||
(?:
|
(?:
|
||||||
(?P<site_id>vtm|q2|vtmkzoom)\.be/
|
(?P<site_id>vtm|q2|vtmkzoom)\.be/
|
||||||
(?:
|
(?:
|
||||||
@@ -85,6 +85,22 @@ class MedialaanIE(InfoExtractor):
|
|||||||
# clip
|
# clip
|
||||||
'url': 'http://vtmkzoom.be/k3-dansstudio/een-nieuw-seizoen-van-k3-dansstudio',
|
'url': 'http://vtmkzoom.be/k3-dansstudio/een-nieuw-seizoen-van-k3-dansstudio',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# http/s redirect
|
||||||
|
'url': 'https://vtmkzoom.be/video?aid=45724',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '257136373657000',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'K3 Dansstudio Ushuaia afl.6',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
'skip': 'Requires account credentials',
|
||||||
|
}, {
|
||||||
|
# nieuws.vtm.be
|
||||||
|
'url': 'https://nieuws.vtm.be/stadion/stadion/genk-nog-moeilijk-programma',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_initialize(self):
|
def _real_initialize(self):
|
||||||
@@ -146,6 +162,8 @@ class MedialaanIE(InfoExtractor):
|
|||||||
video_id, transform_source=lambda s: '[%s]' % s, fatal=False)
|
video_id, transform_source=lambda s: '[%s]' % s, fatal=False)
|
||||||
if player:
|
if player:
|
||||||
video = player[-1]
|
video = player[-1]
|
||||||
|
if video['videoUrl'] in ('http', 'https'):
|
||||||
|
return self.url_result(video['url'], MedialaanIE.ie_key())
|
||||||
info = {
|
info = {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'url': video['videoUrl'],
|
'url': video['videoUrl'],
|
||||||
|
118
youtube_dl/extractor/mediaset.py
Normal file
118
youtube_dl/extractor/mediaset.py
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..compat import compat_str
|
||||||
|
from ..utils import (
|
||||||
|
determine_ext,
|
||||||
|
parse_duration,
|
||||||
|
try_get,
|
||||||
|
unified_strdate,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MediasetIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'''(?x)
|
||||||
|
(?:
|
||||||
|
mediaset:|
|
||||||
|
https?://
|
||||||
|
(?:www\.)?video\.mediaset\.it/
|
||||||
|
(?:
|
||||||
|
(?:video|on-demand)/(?:[^/]+/)+[^/]+_|
|
||||||
|
player/playerIFrame(?:Twitter)?\.shtml\?.*?\bid=
|
||||||
|
)
|
||||||
|
)(?P<id>[0-9]+)
|
||||||
|
'''
|
||||||
|
_TESTS = [{
|
||||||
|
# full episode
|
||||||
|
'url': 'http://www.video.mediaset.it/video/hello_goodbye/full/quarta-puntata_661824.html',
|
||||||
|
'md5': '9b75534d42c44ecef7bf1ffeacb7f85d',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '661824',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Quarta puntata',
|
||||||
|
'description': 'md5:7183696d6df570e3412a5ef74b27c5e2',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
'duration': 1414,
|
||||||
|
'creator': 'mediaset',
|
||||||
|
'upload_date': '20161107',
|
||||||
|
'series': 'Hello Goodbye',
|
||||||
|
'categories': ['reality'],
|
||||||
|
},
|
||||||
|
'expected_warnings': ['is not a supported codec'],
|
||||||
|
}, {
|
||||||
|
# clip
|
||||||
|
'url': 'http://www.video.mediaset.it/video/gogglebox/clip/un-grande-classico-della-commedia-sexy_661680.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# iframe simple
|
||||||
|
'url': 'http://www.video.mediaset.it/player/playerIFrame.shtml?id=665924&autoplay=true',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# iframe twitter (from http://www.wittytv.it/se-prima-mi-fidavo-zero/)
|
||||||
|
'url': 'https://www.video.mediaset.it/player/playerIFrameTwitter.shtml?id=665104&playrelated=false&autoplay=false&related=true&hidesocial=true',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'mediaset:661824',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_urls(webpage):
|
||||||
|
return [
|
||||||
|
mobj.group('url')
|
||||||
|
for mobj in re.finditer(
|
||||||
|
r'<iframe\b[^>]+\bsrc=(["\'])(?P<url>https?://(?:www\.)?video\.mediaset\.it/player/playerIFrame(?:Twitter)?\.shtml\?.*?\bid=\d+.*?)\1',
|
||||||
|
webpage)]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
|
video_list = self._download_json(
|
||||||
|
'http://cdnsel01.mediaset.net/GetCdn.aspx',
|
||||||
|
video_id, 'Downloading video CDN JSON', query={
|
||||||
|
'streamid': video_id,
|
||||||
|
'format': 'json',
|
||||||
|
})['videoList']
|
||||||
|
|
||||||
|
formats = []
|
||||||
|
for format_url in video_list:
|
||||||
|
if '.ism' in format_url:
|
||||||
|
formats.extend(self._extract_ism_formats(
|
||||||
|
format_url, video_id, ism_id='mss', fatal=False))
|
||||||
|
else:
|
||||||
|
formats.append({
|
||||||
|
'url': format_url,
|
||||||
|
'format_id': determine_ext(format_url),
|
||||||
|
})
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
mediainfo = self._download_json(
|
||||||
|
'http://plr.video.mediaset.it/html/metainfo.sjson',
|
||||||
|
video_id, 'Downloading video info JSON', query={
|
||||||
|
'id': video_id,
|
||||||
|
})['video']
|
||||||
|
|
||||||
|
title = mediainfo['title']
|
||||||
|
|
||||||
|
creator = try_get(
|
||||||
|
mediainfo, lambda x: x['brand-info']['publisher'], compat_str)
|
||||||
|
category = try_get(
|
||||||
|
mediainfo, lambda x: x['brand-info']['category'], compat_str)
|
||||||
|
categories = [category] if category else None
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': title,
|
||||||
|
'description': mediainfo.get('short-description'),
|
||||||
|
'thumbnail': mediainfo.get('thumbnail'),
|
||||||
|
'duration': parse_duration(mediainfo.get('duration')),
|
||||||
|
'creator': creator,
|
||||||
|
'upload_date': unified_strdate(mediainfo.get('production-date')),
|
||||||
|
'webpage_url': mediainfo.get('url'),
|
||||||
|
'series': mediainfo.get('brand-value'),
|
||||||
|
'categories': categories,
|
||||||
|
'formats': formats,
|
||||||
|
}
|
@@ -136,11 +136,9 @@ class MiTeleIE(InfoExtractor):
|
|||||||
video_id, 'Downloading gigya script')
|
video_id, 'Downloading gigya script')
|
||||||
|
|
||||||
# Get a appKey/uuid for getting the session key
|
# Get a appKey/uuid for getting the session key
|
||||||
appKey_var = self._search_regex(
|
|
||||||
r'value\s*\(\s*["\']appGridApplicationKey["\']\s*,\s*([0-9a-f]+)',
|
|
||||||
gigya_sc, 'appKey variable')
|
|
||||||
appKey = self._search_regex(
|
appKey = self._search_regex(
|
||||||
r'var\s+%s\s*=\s*["\']([0-9a-f]+)' % appKey_var, gigya_sc, 'appKey')
|
r'constant\s*\(\s*["\']_appGridApplicationKey["\']\s*,\s*["\']([0-9a-f]+)',
|
||||||
|
gigya_sc, 'appKey')
|
||||||
|
|
||||||
session_json = self._download_json(
|
session_json = self._download_json(
|
||||||
'https://appgrid-api.cloud.accedo.tv/session',
|
'https://appgrid-api.cloud.accedo.tv/session',
|
||||||
|
@@ -68,10 +68,6 @@ class MSNIE(InfoExtractor):
|
|||||||
format_url = file_.get('url')
|
format_url = file_.get('url')
|
||||||
if not format_url:
|
if not format_url:
|
||||||
continue
|
continue
|
||||||
ext = determine_ext(format_url)
|
|
||||||
if ext == 'ism':
|
|
||||||
formats.extend(self._extract_ism_formats(
|
|
||||||
format_url + '/Manifest', display_id, 'mss', fatal=False))
|
|
||||||
if 'm3u8' in format_url:
|
if 'm3u8' in format_url:
|
||||||
# m3u8_native should not be used here until
|
# m3u8_native should not be used here until
|
||||||
# https://github.com/rg3/youtube-dl/issues/9913 is fixed
|
# https://github.com/rg3/youtube-dl/issues/9913 is fixed
|
||||||
@@ -79,6 +75,9 @@ class MSNIE(InfoExtractor):
|
|||||||
format_url, display_id, 'mp4',
|
format_url, display_id, 'mp4',
|
||||||
m3u8_id='hls', fatal=False)
|
m3u8_id='hls', fatal=False)
|
||||||
formats.extend(m3u8_formats)
|
formats.extend(m3u8_formats)
|
||||||
|
elif determine_ext(format_url) == 'ism':
|
||||||
|
formats.extend(self._extract_ism_formats(
|
||||||
|
format_url + '/Manifest', display_id, 'mss', fatal=False))
|
||||||
else:
|
else:
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': format_url,
|
'url': format_url,
|
||||||
|
@@ -12,64 +12,62 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class MySpaceIE(InfoExtractor):
|
class MySpaceIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://myspace\.com/([^/]+)/(?P<mediatype>video/[^/]+/|music/song/.*?)(?P<id>\d+)'
|
_VALID_URL = r'''(?x)
|
||||||
|
https?://
|
||||||
|
myspace\.com/[^/]+/
|
||||||
|
(?P<mediatype>
|
||||||
|
video/[^/]+/(?P<video_id>\d+)|
|
||||||
|
music/song/[^/?#&]+-(?P<song_id>\d+)-\d+(?:[/?#&]|$)
|
||||||
|
)
|
||||||
|
'''
|
||||||
|
|
||||||
_TESTS = [
|
_TESTS = [{
|
||||||
{
|
'url': 'https://myspace.com/fiveminutestothestage/video/little-big-town/109594919',
|
||||||
'url': 'https://myspace.com/fiveminutestothestage/video/little-big-town/109594919',
|
'md5': '9c1483c106f4a695c47d2911feed50a7',
|
||||||
'md5': '9c1483c106f4a695c47d2911feed50a7',
|
'info_dict': {
|
||||||
'info_dict': {
|
'id': '109594919',
|
||||||
'id': '109594919',
|
'ext': 'mp4',
|
||||||
'ext': 'mp4',
|
'title': 'Little Big Town',
|
||||||
'title': 'Little Big Town',
|
'description': 'This country quartet was all smiles while playing a sold out show at the Pacific Amphitheatre in Orange County, California.',
|
||||||
'description': 'This country quartet was all smiles while playing a sold out show at the Pacific Amphitheatre in Orange County, California.',
|
'uploader': 'Five Minutes to the Stage',
|
||||||
'uploader': 'Five Minutes to the Stage',
|
'uploader_id': 'fiveminutestothestage',
|
||||||
'uploader_id': 'fiveminutestothestage',
|
'timestamp': 1414108751,
|
||||||
'timestamp': 1414108751,
|
'upload_date': '20141023',
|
||||||
'upload_date': '20141023',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
# songs
|
# songs
|
||||||
{
|
'url': 'https://myspace.com/killsorrow/music/song/of-weakened-soul...-93388656-103880681',
|
||||||
'url': 'https://myspace.com/killsorrow/music/song/of-weakened-soul...-93388656-103880681',
|
'md5': '1d7ee4604a3da226dd69a123f748b262',
|
||||||
'md5': '1d7ee4604a3da226dd69a123f748b262',
|
'info_dict': {
|
||||||
'info_dict': {
|
'id': '93388656',
|
||||||
'id': '93388656',
|
'ext': 'm4a',
|
||||||
'ext': 'm4a',
|
'title': 'Of weakened soul...',
|
||||||
'title': 'Of weakened soul...',
|
'uploader': 'Killsorrow',
|
||||||
'uploader': 'Killsorrow',
|
'uploader_id': 'killsorrow',
|
||||||
'uploader_id': 'killsorrow',
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
'add_ie': ['Youtube'],
|
|
||||||
'url': 'https://myspace.com/threedaysgrace/music/song/animal-i-have-become-28400208-28218041',
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'xqds0B_meys',
|
|
||||||
'ext': 'webm',
|
|
||||||
'title': 'Three Days Grace - Animal I Have Become',
|
|
||||||
'description': 'md5:8bd86b3693e72a077cf863a8530c54bb',
|
|
||||||
'uploader': 'ThreeDaysGraceVEVO',
|
|
||||||
'uploader_id': 'ThreeDaysGraceVEVO',
|
|
||||||
'upload_date': '20091002',
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
'add_ie': ['Youtube'],
|
|
||||||
'url': 'https://myspace.com/starset2/music/song/first-light-95799905-106964426',
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'ypWvQgnJrSU',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Starset - First Light',
|
|
||||||
'description': 'md5:2d5db6c9d11d527683bcda818d332414',
|
|
||||||
'uploader': 'Yumi K',
|
|
||||||
'uploader_id': 'SorenPromotions',
|
|
||||||
'upload_date': '20140725',
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
]
|
}, {
|
||||||
|
'add_ie': ['Youtube'],
|
||||||
|
'url': 'https://myspace.com/threedaysgrace/music/song/animal-i-have-become-28400208-28218041',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'xqds0B_meys',
|
||||||
|
'ext': 'webm',
|
||||||
|
'title': 'Three Days Grace - Animal I Have Become',
|
||||||
|
'description': 'md5:8bd86b3693e72a077cf863a8530c54bb',
|
||||||
|
'uploader': 'ThreeDaysGraceVEVO',
|
||||||
|
'uploader_id': 'ThreeDaysGraceVEVO',
|
||||||
|
'upload_date': '20091002',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://myspace.com/starset2/music/song/first-light-95799905-106964426',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://myspace.com/thelargemouthbassband/music/song/02-pure-eyes.mp3-94422330-105113388',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
video_id = mobj.group('id')
|
video_id = mobj.group('video_id') or mobj.group('song_id')
|
||||||
is_song = mobj.group('mediatype').startswith('music/song')
|
is_song = mobj.group('mediatype').startswith('music/song')
|
||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, video_id)
|
||||||
player_url = self._search_regex(
|
player_url = self._search_regex(
|
||||||
|
@@ -5,10 +5,8 @@ import re
|
|||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from .theplatform import ThePlatformIE
|
from .theplatform import ThePlatformIE
|
||||||
from .adobepass import AdobePassIE
|
from .adobepass import AdobePassIE
|
||||||
from ..compat import compat_urllib_parse_urlparse
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
find_xpath_attr,
|
find_xpath_attr,
|
||||||
lowercase_escape,
|
|
||||||
smuggle_url,
|
smuggle_url,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
update_url_query,
|
update_url_query,
|
||||||
@@ -17,7 +15,7 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class NBCIE(AdobePassIE):
|
class NBCIE(AdobePassIE):
|
||||||
_VALID_URL = r'https?://(?:www\.)?nbc\.com/(?:[^/]+/)+(?P<id>n?\d+)'
|
_VALID_URL = r'(?P<permalink>https?://(?:www\.)?nbc\.com/[^/]+/video/[^/]+/(?P<id>n?\d+))'
|
||||||
|
|
||||||
_TESTS = [
|
_TESTS = [
|
||||||
{
|
{
|
||||||
@@ -36,16 +34,6 @@ class NBCIE(AdobePassIE):
|
|||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
'url': 'http://www.nbc.com/the-tonight-show/episodes/176',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '176',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'Ricky Gervais, Steven Van Zandt, ILoveMakonnen',
|
|
||||||
'description': 'A brand new episode of The Tonight Show welcomes Ricky Gervais, Steven Van Zandt and ILoveMakonnen.',
|
|
||||||
},
|
|
||||||
'skip': '404 Not Found',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'url': 'http://www.nbc.com/saturday-night-live/video/star-wars-teaser/2832821',
|
'url': 'http://www.nbc.com/saturday-night-live/video/star-wars-teaser/2832821',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@@ -63,11 +51,6 @@ class NBCIE(AdobePassIE):
|
|||||||
},
|
},
|
||||||
'skip': 'Only works from US',
|
'skip': 'Only works from US',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
# This video has expired but with an escaped embedURL
|
|
||||||
'url': 'http://www.nbc.com/parenthood/episode-guide/season-5/just-like-at-home/515',
|
|
||||||
'only_matching': True,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
# HLS streams requires the 'hdnea3' cookie
|
# HLS streams requires the 'hdnea3' cookie
|
||||||
'url': 'http://www.nbc.com/Kings/video/goliath/n1806',
|
'url': 'http://www.nbc.com/Kings/video/goliath/n1806',
|
||||||
@@ -88,59 +71,38 @@ class NBCIE(AdobePassIE):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
permalink, video_id = re.match(self._VALID_URL, url).groups()
|
||||||
webpage = self._download_webpage(url, video_id)
|
video_data = self._download_json(
|
||||||
info = {
|
'https://api.nbc.com/v3/videos', video_id, query={
|
||||||
'_type': 'url_transparent',
|
'filter[permalink]': permalink,
|
||||||
'ie_key': 'ThePlatform',
|
})['data'][0]['attributes']
|
||||||
'id': video_id,
|
query = {
|
||||||
|
'mbr': 'true',
|
||||||
|
'manifest': 'm3u',
|
||||||
|
}
|
||||||
|
video_id = video_data['guid']
|
||||||
|
title = video_data['title']
|
||||||
|
if video_data.get('entitlement') == 'auth':
|
||||||
|
resource = self._get_mvpd_resource(
|
||||||
|
'nbcentertainment', title, video_id,
|
||||||
|
video_data.get('vChipRating'))
|
||||||
|
query['auth'] = self._extract_mvpd_auth(
|
||||||
|
url, video_id, 'nbcentertainment', resource)
|
||||||
|
theplatform_url = smuggle_url(update_url_query(
|
||||||
|
'http://link.theplatform.com/s/NnzsPC/media/guid/2410887629/' + video_id,
|
||||||
|
query), {'force_smil_url': True})
|
||||||
|
return {
|
||||||
|
'_type': 'url_transparent',
|
||||||
|
'id': video_id,
|
||||||
|
'title': title,
|
||||||
|
'url': theplatform_url,
|
||||||
|
'description': video_data.get('description'),
|
||||||
|
'keywords': video_data.get('keywords'),
|
||||||
|
'season_number': int_or_none(video_data.get('seasonNumber')),
|
||||||
|
'episode_number': int_or_none(video_data.get('episodeNumber')),
|
||||||
|
'series': video_data.get('showName'),
|
||||||
|
'ie_key': 'ThePlatform',
|
||||||
}
|
}
|
||||||
video_data = None
|
|
||||||
preload = self._search_regex(
|
|
||||||
r'PRELOAD\s*=\s*({.+})', webpage, 'preload data', default=None)
|
|
||||||
if preload:
|
|
||||||
preload_data = self._parse_json(preload, video_id)
|
|
||||||
path = compat_urllib_parse_urlparse(url).path.rstrip('/')
|
|
||||||
entity_id = preload_data.get('xref', {}).get(path)
|
|
||||||
video_data = preload_data.get('entities', {}).get(entity_id)
|
|
||||||
if video_data:
|
|
||||||
query = {
|
|
||||||
'mbr': 'true',
|
|
||||||
'manifest': 'm3u',
|
|
||||||
}
|
|
||||||
video_id = video_data['guid']
|
|
||||||
title = video_data['title']
|
|
||||||
if video_data.get('entitlement') == 'auth':
|
|
||||||
resource = self._get_mvpd_resource(
|
|
||||||
'nbcentertainment', title, video_id,
|
|
||||||
video_data.get('vChipRating'))
|
|
||||||
query['auth'] = self._extract_mvpd_auth(
|
|
||||||
url, video_id, 'nbcentertainment', resource)
|
|
||||||
theplatform_url = smuggle_url(update_url_query(
|
|
||||||
'http://link.theplatform.com/s/NnzsPC/media/guid/2410887629/' + video_id,
|
|
||||||
query), {'force_smil_url': True})
|
|
||||||
info.update({
|
|
||||||
'id': video_id,
|
|
||||||
'title': title,
|
|
||||||
'url': theplatform_url,
|
|
||||||
'description': video_data.get('description'),
|
|
||||||
'keywords': video_data.get('keywords'),
|
|
||||||
'season_number': int_or_none(video_data.get('seasonNumber')),
|
|
||||||
'episode_number': int_or_none(video_data.get('episodeNumber')),
|
|
||||||
'series': video_data.get('showName'),
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
theplatform_url = unescapeHTML(lowercase_escape(self._html_search_regex(
|
|
||||||
[
|
|
||||||
r'(?:class="video-player video-player-full" data-mpx-url|class="player" src)="(.*?)"',
|
|
||||||
r'<iframe[^>]+src="((?:https?:)?//player\.theplatform\.com/[^"]+)"',
|
|
||||||
r'"embedURL"\s*:\s*"([^"]+)"'
|
|
||||||
],
|
|
||||||
webpage, 'theplatform url').replace('_no_endcard', '').replace('\\/', '/')))
|
|
||||||
if theplatform_url.startswith('//'):
|
|
||||||
theplatform_url = 'http:' + theplatform_url
|
|
||||||
info['url'] = smuggle_url(theplatform_url, {'source_url': url})
|
|
||||||
return info
|
|
||||||
|
|
||||||
|
|
||||||
class NBCSportsVPlayerIE(InfoExtractor):
|
class NBCSportsVPlayerIE(InfoExtractor):
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user