Compare commits
261 Commits
2017.01.02
...
2017.02.10
Author | SHA1 | Date | |
---|---|---|---|
55d4de2283 | |||
61ee556aea | |||
ff24261ba0 | |||
fbc6dc525e | |||
9150d1eb69 | |||
b7f9843bec | |||
e64b0fca14 | |||
78ef214d2d | |||
be670b8e8f | |||
37084f6641 | |||
b04975733c | |||
c8b8fb0a99 | |||
8298018273 | |||
ae8d5a5c59 | |||
b9c9cb5f79 | |||
fdf9b959bc | |||
013877298d | |||
c87f95f991 | |||
f28aeff264 | |||
242a14a1f6 | |||
d5d904ff7d | |||
5620f840f6 | |||
b7a8c1bcfa | |||
7097bffba6 | |||
2aec7256ae | |||
815482d4eb | |||
9c14fe9681 | |||
e705755739 | |||
019f4c0371 | |||
2ab2c0d1f5 | |||
caf0f5f8b7 | |||
e4e50f60b1 | |||
6ef3e65a7b | |||
6fd138bed8 | |||
49bd8d5e2e | |||
3d2c2752c5 | |||
a713a86755 | |||
7bccd5fc8a | |||
3144eccf55 | |||
9db8f6c540 | |||
8e4041cf3f | |||
31487eb974 | |||
c2521c1ac6 | |||
643dc0fcfe | |||
36fce54816 | |||
2c15db829c | |||
f65dba7cdb | |||
605fd6392f | |||
f962790ee5 | |||
b7cc5f078e | |||
f7a10d8cd6 | |||
daac118bf4 | |||
8939f784d9 | |||
df0588a31f | |||
4ce3407d08 | |||
d7f9242e30 | |||
45024183ae | |||
33da98f493 | |||
4195096ea8 | |||
0bbcc8a10a | |||
b3ee552e4b | |||
a22b2fd19b | |||
c54c01f82d | |||
5a116e1302 | |||
a685751051 | |||
bd8f48c78b | |||
81aeafeb44 | |||
8bdc149441 | |||
020c5df52d | |||
da162c1135 | |||
5069594993 | |||
b996b88092 | |||
b83ef507b4 | |||
000f207944 | |||
fe5aa197b5 | |||
7882f1115e | |||
2b2d5d319b | |||
26c0f09935 | |||
c15cd29640 | |||
c38a67bcd5 | |||
363245ad94 | |||
7c5329e6f4 | |||
8fd65faece | |||
d7e215b42d | |||
3a528ffd89 | |||
3c90cc8b6f | |||
ae9a173b64 | |||
75822ca790 | |||
dadb836139 | |||
4719419951 | |||
c2d9c25f81 | |||
4d2fdb07c4 | |||
fe323a4800 | |||
f13da8af28 | |||
e228616c6e | |||
c58c2d63cb | |||
d04621daf4 | |||
76aaf1faae | |||
56fc078da8 | |||
0842b8241d | |||
59c307891a | |||
4d07b748c2 | |||
f5169501d2 | |||
186f4abe93 | |||
34cea6137e | |||
ffcfb7e3e0 | |||
c0af11abee | |||
1a241a2d02 | |||
acbb2374bc | |||
4edeac5bfa | |||
f592ff9868 | |||
24ee6b9721 | |||
a71b8d3b3b | |||
732fb3f8be | |||
008f247077 | |||
661cc229d2 | |||
b92d3c5343 | |||
ab6f6aee78 | |||
26e40542dd | |||
99a0baf370 | |||
d41ed6d243 | |||
815d2a36d8 | |||
e0b6e50ccd | |||
3a194cb4ec | |||
9b73471801 | |||
489ffc1182 | |||
0b23c222ba | |||
b51a4ebed4 | |||
9463637887 | |||
3cbecdd111 | |||
15846398ca | |||
c19ef77c31 | |||
b3277115a1 | |||
9bccdc7004 | |||
cf0cabbe50 | |||
556dbe7fe3 | |||
2417d41535 | |||
2c302cf66b | |||
c1fa3f4672 | |||
17f8deeb48 | |||
b8a03b6660 | |||
c60089c022 | |||
af59bddc4e | |||
23b35a634e | |||
74af9c700d | |||
d61aa5eb37 | |||
c3a65c3de0 | |||
ee4c091ce5 | |||
b494d6856c | |||
bc35ed3fb6 | |||
0c1c6f4b9f | |||
6d119c2a6b | |||
4201ba13e6 | |||
8bc0800d7c | |||
a089545e03 | |||
30dda24de3 | |||
9d5b29c881 | |||
6c031a35f3 | |||
271808b6b2 | |||
8d1fbe0cb2 | |||
a243abb80d | |||
42697bab3c | |||
94629e537f | |||
e84495cd8d | |||
7c20b7484c | |||
04a3d4d234 | |||
12afdc2ad6 | |||
f4ec8dce48 | |||
f3c21cb7a7 | |||
972efe60c3 | |||
4447fb2332 | |||
d77ac73790 | |||
1fe84be0f3 | |||
1076858f76 | |||
cccd70a275 | |||
eb3f008c9e | |||
f1e70fc2ff | |||
1560baacc6 | |||
460f61fac4 | |||
baa3e1845b | |||
aaf2b7c57a | |||
b687c85eab | |||
538b17a09c | |||
4e44598547 | |||
136078966b | |||
8a5f0a6357 | |||
c0bd51c090 | |||
c1c2fe2045 | |||
ddd53c392e | |||
79fc8496c6 | |||
0ce8c66fb0 | |||
906420cae3 | |||
16e2c8f771 | |||
dcae7b3fdc | |||
8e4988f1a2 | |||
a7acf868a5 | |||
6f0be93747 | |||
af62de104f | |||
cd55c6ccd7 | |||
621a2800ca | |||
b80e2ebc8d | |||
99d537a5e0 | |||
8854f3fe78 | |||
abe8cb763f | |||
5d4c7daa49 | |||
0b94510cd0 | |||
4f66c16f33 | |||
e54fc0524e | |||
adf063dad1 | |||
5e8eebb600 | |||
9837cb7507 | |||
fb6a59205e | |||
06e9363b7a | |||
1f393a3241 | |||
c4251b9aaa | |||
3a407e707a | |||
cb655f34fb | |||
ed06da4e7b | |||
365d136b7c | |||
1fd0fc42bd | |||
10cd2003b4 | |||
cdd11c0540 | |||
67fc365b86 | |||
20faad74b6 | |||
2032d935d1 | |||
31ea2ad89d | |||
2184d44361 | |||
d1aeacd9bf | |||
366b759a60 | |||
7f0bdc7a31 | |||
022a5d663b | |||
8409b3683c | |||
bfedb2cc5a | |||
8084951b7f | |||
e7ea724cb9 | |||
e60166020b | |||
364131584b | |||
553c68bbd9 | |||
827961b122 | |||
a5eefc492b | |||
a9cd1691b2 | |||
2365f94412 | |||
32b7c2a57e | |||
221ce32529 | |||
e5dfdc8164 | |||
a814da3f62 | |||
b2727d0bee | |||
dbaf601646 | |||
a9ee260217 | |||
1219201143 | |||
ec85ded83c | |||
24d8a75982 | |||
7232bb299b | |||
2b12e34076 | |||
fb47cb5b23 | |||
b6de53ea8a | |||
96d315c2be | |||
1911d77d28 | |||
027e231295 | |||
7a9e066972 | |||
a0758dfa1a |
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.01.02*. 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.02.10*. 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.01.02**
|
- [ ] I've **verified** and **I assure** that I'm running youtube-dl **2017.02.10**
|
||||||
|
|
||||||
### 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.01.02
|
[debug] youtube-dl version 2017.02.10
|
||||||
[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: {}
|
||||||
|
@ -6,6 +6,7 @@ python:
|
|||||||
- "3.3"
|
- "3.3"
|
||||||
- "3.4"
|
- "3.4"
|
||||||
- "3.5"
|
- "3.5"
|
||||||
|
- "3.6"
|
||||||
sudo: false
|
sudo: false
|
||||||
script: nosetests test --verbose
|
script: nosetests test --verbose
|
||||||
notifications:
|
notifications:
|
||||||
|
11
AUTHORS
11
AUTHORS
@ -191,3 +191,14 @@ Rich Leeper
|
|||||||
Zhong Jianxin
|
Zhong Jianxin
|
||||||
Thor77
|
Thor77
|
||||||
Mattias Wadman
|
Mattias Wadman
|
||||||
|
Arjan Verwer
|
||||||
|
Costy Petrisor
|
||||||
|
Logan B
|
||||||
|
Alex Seiler
|
||||||
|
Vijay Singh
|
||||||
|
Paul Hartmann
|
||||||
|
Stephen Chen
|
||||||
|
Fabian Stahl
|
||||||
|
Bagira
|
||||||
|
Odd Stråbø
|
||||||
|
Philip Herzog
|
||||||
|
@ -124,7 +124,7 @@ After you have ensured this site is distributing its content legally, you can fo
|
|||||||
'id': '42',
|
'id': '42',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Video title goes here',
|
'title': 'Video title goes here',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
# TODO more properties, either as:
|
# TODO more properties, either as:
|
||||||
# * A value
|
# * A value
|
||||||
# * MD5 checksum; start the string with md5:
|
# * MD5 checksum; start the string with md5:
|
||||||
|
264
ChangeLog
264
ChangeLog
@ -1,3 +1,267 @@
|
|||||||
|
version 2017.02.10
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [xtube] Fix extraction (#12023)
|
||||||
|
* [pornhub] Fix extraction (#12007, #12018)
|
||||||
|
* [facebook] Improve JS data regular expression (#12042)
|
||||||
|
* [kaltura] Improve embed partner id extraction (#12041)
|
||||||
|
+ [sprout] Add support for sproutonline.com
|
||||||
|
* [6play] Improve extraction
|
||||||
|
+ [scrippsnetworks:watch] Add support for Scripps Networks sites (#10765)
|
||||||
|
+ [go] Add support for Adobe Pass authentication (#11468, #10831)
|
||||||
|
* [6play] Fix extraction (#12011)
|
||||||
|
+ [nbc] Add support for Adobe Pass authentication (#12006)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.02.07
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [extractor/common] Fix audio only with audio group in m3u8 (#11995)
|
||||||
|
+ [downloader/fragment] Respect --no-part
|
||||||
|
* [extractor/common] Speed-up HTML5 media entries extraction (#11979)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [pornhub] Fix extraction (#11997)
|
||||||
|
+ [canalplus] Add support for cstar.fr (#11990)
|
||||||
|
+ [extractor/generic] Improve RTMP support (#11993)
|
||||||
|
+ [gaskrank] Add support for gaskrank.tv (#11685)
|
||||||
|
* [bandcamp] Fix extraction for incomplete albums (#11727)
|
||||||
|
* [iwara] Fix extraction (#11781)
|
||||||
|
* [googledrive] Fix extraction on Python 3.6
|
||||||
|
+ [videopress] Add support for videopress.com
|
||||||
|
+ [afreecatv] Extract RTMP formats
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.02.04.1
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [twitch:stream] Add support for player.twitch.tv (#11971)
|
||||||
|
* [radiocanada] Fix extraction for toutv rtmp formats
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.02.04
|
||||||
|
|
||||||
|
Core
|
||||||
|
+ Add --playlist-random to shuffle playlists (#11889, #11901)
|
||||||
|
* [utils] Improve comments processing in js_to_json (#11947)
|
||||||
|
* [utils] Handle single-line comments in js_to_json
|
||||||
|
* [downloader/external:ffmpeg] Minimize the use of aac_adtstoasc filter
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [piksel] Add another app token pattern (#11969)
|
||||||
|
+ [vk] Capture and output author blocked error message (#11965)
|
||||||
|
+ [turner] Fix secure HLS formats downloading with ffmpeg (#11358, #11373,
|
||||||
|
#11800)
|
||||||
|
+ [drtv] Add support for live and radio sections (#1827, #3427)
|
||||||
|
* [myspace] Fix extraction and extract HLS and HTTP formats
|
||||||
|
+ [youtube] Add format info for itag 325 and 328
|
||||||
|
* [vine] Fix extraction (#11955)
|
||||||
|
- [sportbox] Remove extractor (#11954)
|
||||||
|
+ [filmon] Add support for filmon.com (#11187)
|
||||||
|
+ [infoq] Add audio only formats (#11565)
|
||||||
|
* [douyutv] Improve room id regular expression (#11931)
|
||||||
|
* [iprima] Fix extraction (#11920, #11896)
|
||||||
|
* [youtube] Fix ytsearch when cookies are provided (#11924)
|
||||||
|
* [go] Relax video id regular expression (#11937)
|
||||||
|
* [facebook] Fix title extraction (#11941)
|
||||||
|
+ [youtube:playlist] Recognize TL playlists (#11945)
|
||||||
|
+ [bilibili] Support new Bangumi URLs (#11845)
|
||||||
|
+ [cbc:watch] Extract audio codec for audio only formats (#11893)
|
||||||
|
+ [elpais] Fix extraction for some URLs (#11765)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.02.01
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [facebook] Add another fallback extraction scenario (#11926)
|
||||||
|
* [prosiebensat1] Fix extraction of descriptions (#11810, #11929)
|
||||||
|
- [crunchyroll] Remove ScaledBorderAndShadow settings (#9028)
|
||||||
|
+ [vimeo] Extract upload timestamp
|
||||||
|
+ [vimeo] Extract license (#8726, #11880)
|
||||||
|
+ [nrk:series] Add support for series (#11571, #11711)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.01.31
|
||||||
|
|
||||||
|
Core
|
||||||
|
+ [compat] Add compat_etree_register_namespace
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [youtube] Fix extraction for domainless player URLs (#11890, #11891, #11892,
|
||||||
|
#11894, #11895, #11897, #11900, #11903, #11904, #11906, #11907, #11909,
|
||||||
|
#11913, #11914, #11915, #11916, #11917, #11918, #11919)
|
||||||
|
+ [vimeo] Extract both mixed and separated DASH formats
|
||||||
|
+ [ruutu] Extract DASH formats
|
||||||
|
* [itv] Fix extraction for python 2.6
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.01.29
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [extractor/common] Fix initialization template (#11605, #11825)
|
||||||
|
+ [extractor/common] Document fragment_base_url and fragment's path fields
|
||||||
|
* [extractor/common] Fix duration per DASH segment (#11868)
|
||||||
|
+ Introduce --autonumber-start option for initial value of %(autonumber)s
|
||||||
|
template (#727, #2702, #9362, #10457, #10529, #11862)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [azmedien:playlist] Add support for topic and themen playlists (#11817)
|
||||||
|
* [npo] Fix subtitles extraction
|
||||||
|
+ [itv] Extract subtitles
|
||||||
|
+ [itv] Add support for itv.com (#9240)
|
||||||
|
+ [mtv81] Add support for mtv81.com (#7619)
|
||||||
|
+ [vlive] Add support for channels (#11826)
|
||||||
|
+ [kaltura] Add fallback for fileExt
|
||||||
|
+ [kaltura] Improve uploader_id extraction
|
||||||
|
+ [konserthusetplay] Add support for rspoplay.se (#11828)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.01.28
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [utils] Improve parse_duration
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [crunchyroll] Improve series and season metadata extraction (#11832)
|
||||||
|
* [soundcloud] Improve formats extraction and extract audio bitrate
|
||||||
|
+ [soundcloud] Extract HLS formats
|
||||||
|
* [soundcloud] Fix track URL extraction (#11852)
|
||||||
|
+ [twitch:vod] Expand URL regular expressions (#11846)
|
||||||
|
* [aenetworks] Fix season episodes extraction (#11669)
|
||||||
|
+ [tva] Add support for videos.tva.ca (#11842)
|
||||||
|
* [jamendo] Improve and extract more metadata (#11836)
|
||||||
|
+ [disney] Add support for Disney sites (#7409, #11801, #4975, #11000)
|
||||||
|
* [vevo] Remove request to old API and catch API v2 errors
|
||||||
|
+ [cmt,mtv,southpark] Add support for episode URLs (#11837)
|
||||||
|
+ [youtube] Add fallback for duration extraction (#11841)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.01.25
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [openload] Fallback video extension to mp4
|
||||||
|
+ [extractor/generic] Add support for Openload embeds (#11536, #11812)
|
||||||
|
* [srgssr] Fix rts video extraction (#11831)
|
||||||
|
+ [afreecatv:global] Add support for afreeca.tv (#11807)
|
||||||
|
+ [crackle] Extract vtt subtitles
|
||||||
|
+ [crackle] Extract multiple resolutions for thumbnails
|
||||||
|
+ [crackle] Add support for mobile URLs
|
||||||
|
+ [konserthusetplay] Extract subtitles (#11823)
|
||||||
|
+ [konserthusetplay] Add support for HLS videos (#11823)
|
||||||
|
* [vimeo:review] Fix config URL extraction (#11821)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.01.24
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [pluralsight] Fix extraction (#11820)
|
||||||
|
+ [nextmedia] Add support for NextTV (壹電視)
|
||||||
|
* [24video] Fix extraction (#11811)
|
||||||
|
* [youtube:playlist] Fix nonexistent and private playlist detection (#11604)
|
||||||
|
+ [chirbit] Extract uploader (#11809)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.01.22
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [pornflip] Add support for pornflip.com (#11556, #11795)
|
||||||
|
* [chaturbate] Fix extraction (#11797, #11802)
|
||||||
|
+ [azmedien] Add support for AZ Medien sites (#11784, #11785)
|
||||||
|
+ [nextmedia] Support redirected URLs
|
||||||
|
+ [vimeo:channel] Extract videos' titles for playlist entries (#11796)
|
||||||
|
+ [youtube] Extract episode metadata (#9695, #11774)
|
||||||
|
+ [cspan] Support Ustream embedded videos (#11547)
|
||||||
|
+ [1tv] Add support for HLS videos (#11786)
|
||||||
|
* [uol] Fix extraction (#11770)
|
||||||
|
* [mtv] Relax triforce feed regular expression (#11766)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.01.18
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [bilibili] Fix extraction (#11077)
|
||||||
|
+ [canalplus] Add fallback for video id (#11764)
|
||||||
|
* [20min] Fix extraction (#11683, #11751)
|
||||||
|
* [imdb] Extend URL regular expression (#11744)
|
||||||
|
+ [naver] Add support for tv.naver.com links (#11743)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.01.16
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [options] Apply custom config to final composite configuration (#11741)
|
||||||
|
* [YoutubeDL] Improve protocol auto determining (#11720)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [xiami] Relax URL regular expressions
|
||||||
|
* [xiami] Improve track metadata extraction (#11699)
|
||||||
|
+ [limelight] Check hand-make direct HTTP links
|
||||||
|
+ [limelight] Add support for direct HTTP links at video.llnw.net (#11737)
|
||||||
|
+ [brightcove] Recognize another player ID pattern (#11688)
|
||||||
|
+ [niconico] Support login via cookies (#7968)
|
||||||
|
* [yourupload] Fix extraction (#11601)
|
||||||
|
+ [beam:live] Add support for beam.pro live streams (#10702, #11596)
|
||||||
|
* [vevo] Improve geo restriction detection
|
||||||
|
+ [dramafever] Add support for URLs with language code (#11714)
|
||||||
|
* [cbc] Improve playlist support (#11704)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.01.14
|
||||||
|
|
||||||
|
Core
|
||||||
|
+ [common] Add ability to customize akamai manifest host
|
||||||
|
+ [utils] Add more date formats
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
- [mtv] Eliminate _transform_rtmp_url
|
||||||
|
* [mtv] Generalize triforce mgid extraction
|
||||||
|
+ [cmt] Add support for full episodes and video clips (#11623)
|
||||||
|
+ [mitele] Extract DASH formats
|
||||||
|
+ [ooyala] Add support for videos with embedToken (#11684)
|
||||||
|
* [mixcloud] Fix extraction (#11674)
|
||||||
|
* [openload] Fix extraction (#10408)
|
||||||
|
* [tv4] Improve extraction (#11698)
|
||||||
|
* [freesound] Fix and improve extraction (#11602)
|
||||||
|
+ [nick] Add support for beta.nick.com (#11655)
|
||||||
|
* [mtv,cc] Use HLS by default with native HLS downloader (#11641)
|
||||||
|
* [mtv] Fix non-HLS extraction
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.01.10
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [youtube] Fix extraction (#11663, #11664)
|
||||||
|
+ [inc] Add support for inc.com (#11277, #11647)
|
||||||
|
+ [youtube] Add itag 212 (#11575)
|
||||||
|
+ [egghead:course] Add support for egghead.io courses
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.01.08
|
||||||
|
|
||||||
|
Core
|
||||||
|
* Fix "invalid escape sequence" errors under Python 3.6 (#11581)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [hitrecord] Add support for hitrecord.org (#10867, #11626)
|
||||||
|
- [videott] Remove extractor
|
||||||
|
* [swrmediathek] Improve extraction
|
||||||
|
- [sharesix] Remove extractor
|
||||||
|
- [aol:features] Remove extractor
|
||||||
|
* [sendtonews] Improve info extraction
|
||||||
|
* [3sat,phoenix] Fix extraction (#11619)
|
||||||
|
* [comedycentral/mtv] Add support for HLS videos (#11600)
|
||||||
|
* [discoverygo] Fix JSON data parsing (#11219, #11522)
|
||||||
|
|
||||||
|
|
||||||
|
version 2017.01.05
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [zdf] Fix extraction (#11055, #11063)
|
||||||
|
* [pornhub:playlist] Improve extraction (#11594)
|
||||||
|
+ [cctv] Add support for ncpa-classic.com (#11591)
|
||||||
|
+ [tunein] Add support for embeds (#11579)
|
||||||
|
|
||||||
|
|
||||||
version 2017.01.02
|
version 2017.01.02
|
||||||
|
|
||||||
Extractors
|
Extractors
|
||||||
|
56
README.md
56
README.md
@ -88,8 +88,6 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo
|
|||||||
--mark-watched Mark videos watched (YouTube only)
|
--mark-watched Mark videos watched (YouTube only)
|
||||||
--no-mark-watched Do not mark videos watched (YouTube only)
|
--no-mark-watched Do not mark videos watched (YouTube only)
|
||||||
--no-color Do not emit color codes in output
|
--no-color Do not emit color codes in output
|
||||||
--abort-on-unavailable-fragment Abort downloading when some fragment is not
|
|
||||||
available
|
|
||||||
|
|
||||||
## Network Options:
|
## Network Options:
|
||||||
--proxy URL Use the specified HTTP/HTTPS/SOCKS proxy.
|
--proxy URL Use the specified HTTP/HTTPS/SOCKS proxy.
|
||||||
@ -99,16 +97,13 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo
|
|||||||
string (--proxy "") for direct connection
|
string (--proxy "") for direct connection
|
||||||
--socket-timeout SECONDS Time to wait before giving up, in seconds
|
--socket-timeout SECONDS Time to wait before giving up, in seconds
|
||||||
--source-address IP Client-side IP address to bind to
|
--source-address IP Client-side IP address to bind to
|
||||||
(experimental)
|
|
||||||
-4, --force-ipv4 Make all connections via IPv4
|
-4, --force-ipv4 Make all connections via IPv4
|
||||||
(experimental)
|
|
||||||
-6, --force-ipv6 Make all connections via IPv6
|
-6, --force-ipv6 Make all connections via IPv6
|
||||||
(experimental)
|
|
||||||
--geo-verification-proxy URL Use this proxy to verify the IP address for
|
--geo-verification-proxy URL Use this proxy to verify the IP address for
|
||||||
some geo-restricted sites. The default
|
some geo-restricted sites. The default
|
||||||
proxy specified by --proxy (or none, if the
|
proxy specified by --proxy (or none, if the
|
||||||
options is not present) is used for the
|
options is not present) is used for the
|
||||||
actual downloading. (experimental)
|
actual downloading.
|
||||||
|
|
||||||
## Video Selection:
|
## Video Selection:
|
||||||
--playlist-start NUMBER Playlist video to start at (default is 1)
|
--playlist-start NUMBER Playlist video to start at (default is 1)
|
||||||
@ -139,23 +134,23 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo
|
|||||||
COUNT views
|
COUNT views
|
||||||
--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 (experimental).
|
--match-filter FILTER Generic video filter. Specify any key (see
|
||||||
Specify any key (see help for -o for a list
|
help for -o for a list of available keys)
|
||||||
of available keys) to match if the key is
|
to match if the key is present, !key to
|
||||||
present, !key to check if the key is not
|
check if the key is not present,key >
|
||||||
present,key > NUMBER (like "comment_count >
|
NUMBER (like "comment_count > 12", also
|
||||||
12", also works with >=, <, <=, !=, =) to
|
works with >=, <, <=, !=, =) to compare
|
||||||
compare against a number, and & to require
|
against a number, and & to require multiple
|
||||||
multiple matches. Values which are not
|
matches. Values which are not known are
|
||||||
known are excluded unless you put a
|
excluded unless you put a question mark (?)
|
||||||
question mark (?) after the operator.For
|
after the operator.For example, to only
|
||||||
example, to only match videos that have
|
match videos that have been liked more than
|
||||||
been liked more than 100 times and disliked
|
100 times and disliked less than 50 times
|
||||||
less than 50 times (or the dislike
|
(or the dislike functionality is not
|
||||||
functionality is not available at the given
|
available at the given service), but who
|
||||||
service), but who also have a description,
|
also have a description, use --match-filter
|
||||||
use --match-filter "like_count > 100 &
|
"like_count > 100 & dislike_count <? 50 &
|
||||||
dislike_count <? 50 & description" .
|
description" .
|
||||||
--no-playlist Download only the video, if the URL refers
|
--no-playlist Download only the video, if the URL refers
|
||||||
to a video and a playlist.
|
to a video and a playlist.
|
||||||
--yes-playlist Download the playlist, if the URL refers to
|
--yes-playlist Download the playlist, if the URL refers to
|
||||||
@ -178,6 +173,8 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo
|
|||||||
only)
|
only)
|
||||||
--skip-unavailable-fragments Skip unavailable fragments (DASH and
|
--skip-unavailable-fragments Skip unavailable fragments (DASH and
|
||||||
hlsnative only)
|
hlsnative only)
|
||||||
|
--abort-on-unavailable-fragment Abort downloading when some fragment is not
|
||||||
|
available
|
||||||
--buffer-size SIZE Size of download buffer (e.g. 1024 or 16K)
|
--buffer-size SIZE Size of download buffer (e.g. 1024 or 16K)
|
||||||
(default is 1024)
|
(default is 1024)
|
||||||
--no-resize-buffer Do not automatically adjust the buffer
|
--no-resize-buffer Do not automatically adjust the buffer
|
||||||
@ -185,6 +182,7 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo
|
|||||||
automatically resized from an initial value
|
automatically resized from an initial value
|
||||||
of SIZE.
|
of SIZE.
|
||||||
--playlist-reverse Download playlist videos in reverse order
|
--playlist-reverse Download playlist videos in reverse order
|
||||||
|
--playlist-random Download playlist videos in random order
|
||||||
--xattr-set-filesize Set file xattribute ytdl.filesize with
|
--xattr-set-filesize Set file xattribute ytdl.filesize with
|
||||||
expected file size (experimental)
|
expected file size (experimental)
|
||||||
--hls-prefer-native Use the native HLS downloader instead of
|
--hls-prefer-native Use the native HLS downloader instead of
|
||||||
@ -210,7 +208,9 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo
|
|||||||
--autonumber-size NUMBER Specify the number of digits in
|
--autonumber-size NUMBER Specify the number of digits in
|
||||||
%(autonumber)s when it is present in output
|
%(autonumber)s when it is present in output
|
||||||
filename template or --auto-number option
|
filename template or --auto-number option
|
||||||
is given
|
is given (default is 5)
|
||||||
|
--autonumber-start NUMBER Specify the start value for %(autonumber)s
|
||||||
|
(default is 1)
|
||||||
--restrict-filenames Restrict filenames to only ASCII
|
--restrict-filenames Restrict filenames to only ASCII
|
||||||
characters, and avoid "&" and spaces in
|
characters, and avoid "&" and spaces in
|
||||||
filenames
|
filenames
|
||||||
@ -374,7 +374,7 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo
|
|||||||
avprobe)
|
avprobe)
|
||||||
--audio-format FORMAT Specify audio format: "best", "aac",
|
--audio-format FORMAT Specify audio format: "best", "aac",
|
||||||
"vorbis", "mp3", "m4a", "opus", or "wav";
|
"vorbis", "mp3", "m4a", "opus", or "wav";
|
||||||
"best" by default
|
"best" by default; No effect without -x
|
||||||
--audio-quality QUALITY Specify ffmpeg/avconv audio quality, insert
|
--audio-quality QUALITY Specify ffmpeg/avconv audio quality, insert
|
||||||
a value between 0 (better) and 9 (worse)
|
a value between 0 (better) and 9 (worse)
|
||||||
for VBR or a specific bitrate like 128K
|
for VBR or a specific bitrate like 128K
|
||||||
@ -446,6 +446,8 @@ Note that options in configuration file are just the same options aka switches u
|
|||||||
|
|
||||||
You can use `--ignore-config` if you want to disable the configuration file for a particular youtube-dl run.
|
You can use `--ignore-config` if you want to disable the configuration file for a particular youtube-dl run.
|
||||||
|
|
||||||
|
You can also use `--config-location` if you want to use custom configuration file for a particular youtube-dl run.
|
||||||
|
|
||||||
### Authentication with `.netrc` file
|
### Authentication with `.netrc` file
|
||||||
|
|
||||||
You may also want to configure automatic credentials storage for extractors that support authentication (by providing login and password with `--username` and `--password`) in order not to pass credentials as command line arguments on every youtube-dl execution and prevent tracking plain text passwords in the shell command history. You can achieve this using a [`.netrc` file](http://stackoverflow.com/tags/.netrc/info) on a per extractor basis. For that you will need to create a `.netrc` file in your `$HOME` and restrict permissions to read/write by only you:
|
You may also want to configure automatic credentials storage for extractors that support authentication (by providing login and password with `--username` and `--password`) in order not to pass credentials as command line arguments on every youtube-dl execution and prevent tracking plain text passwords in the shell command history. You can achieve this using a [`.netrc` file](http://stackoverflow.com/tags/.netrc/info) on a per extractor basis. For that you will need to create a `.netrc` file in your `$HOME` and restrict permissions to read/write by only you:
|
||||||
@ -839,7 +841,7 @@ Use the `--cookies` option, for example `--cookies /path/to/cookies/file.txt`.
|
|||||||
|
|
||||||
In order to extract cookies from browser use any conforming browser extension for exporting cookies. For example, [cookies.txt](https://chrome.google.com/webstore/detail/cookiestxt/njabckikapfpffapmjgojcnbfjonfjfg) (for Chrome) or [Export Cookies](https://addons.mozilla.org/en-US/firefox/addon/export-cookies/) (for Firefox).
|
In order to extract cookies from browser use any conforming browser extension for exporting cookies. For example, [cookies.txt](https://chrome.google.com/webstore/detail/cookiestxt/njabckikapfpffapmjgojcnbfjonfjfg) (for Chrome) or [Export Cookies](https://addons.mozilla.org/en-US/firefox/addon/export-cookies/) (for Firefox).
|
||||||
|
|
||||||
Note that the cookies file must be in Mozilla/Netscape format and the first line of the cookies file must be either `# HTTP Cookie File` or `# Netscape HTTP Cookie File`. Make sure you have correct [newline format](https://en.wikipedia.org/wiki/Newline) in the cookies file and convert newlines if necessary to correspond with your OS, namely `CRLF` (`\r\n`) for Windows, `LF` (`\n`) for Linux and `CR` (`\r`) for Mac OS. `HTTP Error 400: Bad Request` when using `--cookies` is a good sign of invalid newline format.
|
Note that the cookies file must be in Mozilla/Netscape format and the first line of the cookies file must be either `# HTTP Cookie File` or `# Netscape HTTP Cookie File`. Make sure you have correct [newline format](https://en.wikipedia.org/wiki/Newline) in the cookies file and convert newlines if necessary to correspond with your OS, namely `CRLF` (`\r\n`) for Windows and `LF` (`\n`) for Unix and Unix-like systems (Linux, Mac OS, etc.). `HTTP Error 400: Bad Request` when using `--cookies` is a good sign of invalid newline format.
|
||||||
|
|
||||||
Passing cookies to youtube-dl is a good way to workaround login when a particular extractor does not implement it explicitly. Another use case is working around [CAPTCHA](https://en.wikipedia.org/wiki/CAPTCHA) some websites require you to solve in particular cases in order to get access (e.g. YouTube, CloudFlare).
|
Passing cookies to youtube-dl is a good way to workaround login when a particular extractor does not implement it explicitly. Another use case is working around [CAPTCHA](https://en.wikipedia.org/wiki/CAPTCHA) some websites require you to solve in particular cases in order to get access (e.g. YouTube, CloudFlare).
|
||||||
|
|
||||||
@ -961,7 +963,7 @@ After you have ensured this site is distributing its content legally, you can fo
|
|||||||
'id': '42',
|
'id': '42',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Video title goes here',
|
'title': 'Video title goes here',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
# TODO more properties, either as:
|
# TODO more properties, either as:
|
||||||
# * A value
|
# * A value
|
||||||
# * MD5 checksum; start the string with md5:
|
# * MD5 checksum; start the string with md5:
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
- **4tube**
|
- **4tube**
|
||||||
- **56.com**
|
- **56.com**
|
||||||
- **5min**
|
- **5min**
|
||||||
|
- **6play**
|
||||||
- **8tracks**
|
- **8tracks**
|
||||||
- **91porn**
|
- **91porn**
|
||||||
- **9c9media**
|
- **9c9media**
|
||||||
@ -33,7 +34,8 @@
|
|||||||
- **AdobeTVVideo**
|
- **AdobeTVVideo**
|
||||||
- **AdultSwim**
|
- **AdultSwim**
|
||||||
- **aenetworks**: A+E Networks: A&E, Lifetime, History.com, FYI Network
|
- **aenetworks**: A+E Networks: A&E, Lifetime, History.com, FYI Network
|
||||||
- **AfreecaTV**: afreecatv.com
|
- **afreecatv**: afreecatv.com
|
||||||
|
- **afreecatv:global**: afreecatv.com
|
||||||
- **AirMozilla**
|
- **AirMozilla**
|
||||||
- **AlJazeera**
|
- **AlJazeera**
|
||||||
- **Allocine**
|
- **Allocine**
|
||||||
@ -74,6 +76,8 @@
|
|||||||
- **awaan:live**
|
- **awaan:live**
|
||||||
- **awaan:season**
|
- **awaan:season**
|
||||||
- **awaan:video**
|
- **awaan:video**
|
||||||
|
- **AZMedien**: AZ Medien videos
|
||||||
|
- **AZMedienPlaylist**: AZ Medien playlists
|
||||||
- **Azubu**
|
- **Azubu**
|
||||||
- **AzubuLive**
|
- **AzubuLive**
|
||||||
- **BaiduVideo**: 百度视频
|
- **BaiduVideo**: 百度视频
|
||||||
@ -81,11 +85,13 @@
|
|||||||
- **bambuser:channel**
|
- **bambuser:channel**
|
||||||
- **Bandcamp**
|
- **Bandcamp**
|
||||||
- **Bandcamp:album**
|
- **Bandcamp:album**
|
||||||
|
- **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**
|
||||||
@ -198,6 +204,7 @@
|
|||||||
- **Digiteka**
|
- **Digiteka**
|
||||||
- **Discovery**
|
- **Discovery**
|
||||||
- **DiscoveryGo**
|
- **DiscoveryGo**
|
||||||
|
- **Disney**
|
||||||
- **Dotsub**
|
- **Dotsub**
|
||||||
- **DouyuTV**: 斗鱼
|
- **DouyuTV**: 斗鱼
|
||||||
- **DPlay**
|
- **DPlay**
|
||||||
@ -206,7 +213,8 @@
|
|||||||
- **DRBonanza**
|
- **DRBonanza**
|
||||||
- **Dropbox**
|
- **Dropbox**
|
||||||
- **DrTuber**
|
- **DrTuber**
|
||||||
- **DRTV**
|
- **drtv**
|
||||||
|
- **drtv:live**
|
||||||
- **Dumpert**
|
- **Dumpert**
|
||||||
- **dvtv**: http://video.aktualne.cz/
|
- **dvtv**: http://video.aktualne.cz/
|
||||||
- **dw**
|
- **dw**
|
||||||
@ -214,6 +222,7 @@
|
|||||||
- **EaglePlatform**
|
- **EaglePlatform**
|
||||||
- **EbaumsWorld**
|
- **EbaumsWorld**
|
||||||
- **EchoMsk**
|
- **EchoMsk**
|
||||||
|
- **egghead:course**: egghead.io course
|
||||||
- **eHow**
|
- **eHow**
|
||||||
- **Einthusan**
|
- **Einthusan**
|
||||||
- **eitb.tv**
|
- **eitb.tv**
|
||||||
@ -240,8 +249,9 @@
|
|||||||
- **fc2**
|
- **fc2**
|
||||||
- **fc2:embed**
|
- **fc2:embed**
|
||||||
- **Fczenit**
|
- **Fczenit**
|
||||||
- **features.aol.com**
|
|
||||||
- **fernsehkritik.tv**
|
- **fernsehkritik.tv**
|
||||||
|
- **filmon**
|
||||||
|
- **filmon:channel**
|
||||||
- **Firstpost**
|
- **Firstpost**
|
||||||
- **FiveTV**
|
- **FiveTV**
|
||||||
- **Flickr**
|
- **Flickr**
|
||||||
@ -273,6 +283,7 @@
|
|||||||
- **Gamersyde**
|
- **Gamersyde**
|
||||||
- **GameSpot**
|
- **GameSpot**
|
||||||
- **GameStar**
|
- **GameStar**
|
||||||
|
- **Gaskrank**
|
||||||
- **Gazeta**
|
- **Gazeta**
|
||||||
- **GDCVault**
|
- **GDCVault**
|
||||||
- **generic**: Generic downloader that works on some sites
|
- **generic**: Generic downloader that works on some sites
|
||||||
@ -304,6 +315,7 @@
|
|||||||
- **history:topic**: History.com Topic
|
- **history:topic**: History.com Topic
|
||||||
- **hitbox**
|
- **hitbox**
|
||||||
- **hitbox:live**
|
- **hitbox:live**
|
||||||
|
- **HitRecord**
|
||||||
- **HornBunny**
|
- **HornBunny**
|
||||||
- **HotNewHipHop**
|
- **HotNewHipHop**
|
||||||
- **HotStar**
|
- **HotStar**
|
||||||
@ -321,6 +333,7 @@
|
|||||||
- **Imgur**
|
- **Imgur**
|
||||||
- **ImgurAlbum**
|
- **ImgurAlbum**
|
||||||
- **Ina**
|
- **Ina**
|
||||||
|
- **Inc**
|
||||||
- **Indavideo**
|
- **Indavideo**
|
||||||
- **IndavideoEmbed**
|
- **IndavideoEmbed**
|
||||||
- **InfoQ**
|
- **InfoQ**
|
||||||
@ -330,6 +343,7 @@
|
|||||||
- **IPrima**
|
- **IPrima**
|
||||||
- **iqiyi**: 爱奇艺
|
- **iqiyi**: 爱奇艺
|
||||||
- **Ir90Tv**
|
- **Ir90Tv**
|
||||||
|
- **ITV**
|
||||||
- **ivi**: ivi.ru
|
- **ivi**: ivi.ru
|
||||||
- **ivi:compilation**: ivi.ru compilations
|
- **ivi:compilation**: ivi.ru compilations
|
||||||
- **ivideon**: Ivideon TV
|
- **ivideon**: Ivideon TV
|
||||||
@ -438,6 +452,7 @@
|
|||||||
- **mtg**: MTG services
|
- **mtg**: MTG services
|
||||||
- **mtv**
|
- **mtv**
|
||||||
- **mtv.de**
|
- **mtv.de**
|
||||||
|
- **mtv81**
|
||||||
- **mtv:video**
|
- **mtv:video**
|
||||||
- **mtvservices:embedded**
|
- **mtvservices:embedded**
|
||||||
- **MuenchenTV**: münchen.tv
|
- **MuenchenTV**: münchen.tv
|
||||||
@ -480,6 +495,7 @@
|
|||||||
- **Newstube**
|
- **Newstube**
|
||||||
- **NextMedia**: 蘋果日報
|
- **NextMedia**: 蘋果日報
|
||||||
- **NextMediaActionNews**: 蘋果日報 - 動新聞
|
- **NextMediaActionNews**: 蘋果日報 - 動新聞
|
||||||
|
- **NextTV**: 壹電視
|
||||||
- **nfb**: National Film Board of Canada
|
- **nfb**: National Film Board of Canada
|
||||||
- **nfl.com**
|
- **nfl.com**
|
||||||
- **NhkVod**
|
- **NhkVod**
|
||||||
@ -518,6 +534,7 @@
|
|||||||
- **NRKTV**: NRK TV and NRK Radio
|
- **NRKTV**: NRK TV and NRK Radio
|
||||||
- **NRKTVDirekte**: NRK TV Direkte and NRK Radio Direkte
|
- **NRKTVDirekte**: NRK TV Direkte and NRK Radio Direkte
|
||||||
- **NRKTVEpisodes**
|
- **NRKTVEpisodes**
|
||||||
|
- **NRKTVSeries**
|
||||||
- **ntv.ru**
|
- **ntv.ru**
|
||||||
- **Nuvid**
|
- **Nuvid**
|
||||||
- **NYTimes**
|
- **NYTimes**
|
||||||
@ -569,6 +586,7 @@
|
|||||||
- **PolskieRadio**
|
- **PolskieRadio**
|
||||||
- **PolskieRadioCategory**
|
- **PolskieRadioCategory**
|
||||||
- **PornCom**
|
- **PornCom**
|
||||||
|
- **PornFlip**
|
||||||
- **PornHd**
|
- **PornHd**
|
||||||
- **PornHub**: PornHub and Thumbzilla
|
- **PornHub**: PornHub and Thumbzilla
|
||||||
- **PornHubPlaylist**
|
- **PornHubPlaylist**
|
||||||
@ -650,7 +668,7 @@
|
|||||||
- **screen.yahoo:search**: Yahoo screen search
|
- **screen.yahoo:search**: Yahoo screen search
|
||||||
- **Screencast**
|
- **Screencast**
|
||||||
- **ScreencastOMatic**
|
- **ScreencastOMatic**
|
||||||
- **ScreenJunkies**
|
- **scrippsnetworks:watch**
|
||||||
- **Seeker**
|
- **Seeker**
|
||||||
- **SenateISVP**
|
- **SenateISVP**
|
||||||
- **SendtoNews**
|
- **SendtoNews**
|
||||||
@ -658,10 +676,8 @@
|
|||||||
- **Sexu**
|
- **Sexu**
|
||||||
- **Shahid**
|
- **Shahid**
|
||||||
- **Shared**: shared.sx
|
- **Shared**: shared.sx
|
||||||
- **ShareSix**
|
|
||||||
- **ShowRoomLive**
|
- **ShowRoomLive**
|
||||||
- **Sina**
|
- **Sina**
|
||||||
- **SixPlay**
|
|
||||||
- **skynewsarabia:article**
|
- **skynewsarabia:article**
|
||||||
- **skynewsarabia:video**
|
- **skynewsarabia:video**
|
||||||
- **SkySports**
|
- **SkySports**
|
||||||
@ -693,10 +709,10 @@
|
|||||||
- **Spiegeltv**
|
- **Spiegeltv**
|
||||||
- **Spike**
|
- **Spike**
|
||||||
- **Sport5**
|
- **Sport5**
|
||||||
- **SportBox**
|
|
||||||
- **SportBoxEmbed**
|
- **SportBoxEmbed**
|
||||||
- **SportDeutschland**
|
- **SportDeutschland**
|
||||||
- **Sportschau**
|
- **Sportschau**
|
||||||
|
- **Sprout**
|
||||||
- **sr:mediathek**: Saarländischer Rundfunk
|
- **sr:mediathek**: Saarländischer Rundfunk
|
||||||
- **SRGSSR**
|
- **SRGSSR**
|
||||||
- **SRGSSRPlay**: srf.ch, rts.ch, rsi.ch, rtr.ch and swissinfo.ch play sites
|
- **SRGSSRPlay**: srf.ch, rts.ch, rsi.ch, rtr.ch and swissinfo.ch play sites
|
||||||
@ -779,6 +795,7 @@
|
|||||||
- **TV2Article**
|
- **TV2Article**
|
||||||
- **TV3**
|
- **TV3**
|
||||||
- **TV4**: tv4.se and tv4play.se
|
- **TV4**: tv4.se and tv4play.se
|
||||||
|
- **TVA**
|
||||||
- **TVANouvelles**
|
- **TVANouvelles**
|
||||||
- **TVANouvellesArticle**
|
- **TVANouvellesArticle**
|
||||||
- **TVC**
|
- **TVC**
|
||||||
@ -845,7 +862,7 @@
|
|||||||
- **videomore:season**
|
- **videomore:season**
|
||||||
- **videomore:video**
|
- **videomore:video**
|
||||||
- **VideoPremium**
|
- **VideoPremium**
|
||||||
- **VideoTt**: video.tt - Your True Tube (Currently broken)
|
- **VideoPress**
|
||||||
- **videoweed**: VideoWeed
|
- **videoweed**: VideoWeed
|
||||||
- **Vidio**
|
- **Vidio**
|
||||||
- **vidme**
|
- **vidme**
|
||||||
@ -880,6 +897,7 @@
|
|||||||
- **vk:uservideos**: VK - User's Videos
|
- **vk:uservideos**: VK - User's Videos
|
||||||
- **vk:wallpost**
|
- **vk:wallpost**
|
||||||
- **vlive**
|
- **vlive**
|
||||||
|
- **vlive:channel**
|
||||||
- **Vodlocker**
|
- **Vodlocker**
|
||||||
- **VODPlatform**
|
- **VODPlatform**
|
||||||
- **VoiceRepublic**
|
- **VoiceRepublic**
|
||||||
|
@ -295,6 +295,9 @@ class TestUtil(unittest.TestCase):
|
|||||||
self.assertEqual(unified_strdate('27.02.2016 17:30'), '20160227')
|
self.assertEqual(unified_strdate('27.02.2016 17:30'), '20160227')
|
||||||
self.assertEqual(unified_strdate('UNKNOWN DATE FORMAT'), None)
|
self.assertEqual(unified_strdate('UNKNOWN DATE FORMAT'), None)
|
||||||
self.assertEqual(unified_strdate('Feb 7, 2016 at 6:35 pm'), '20160207')
|
self.assertEqual(unified_strdate('Feb 7, 2016 at 6:35 pm'), '20160207')
|
||||||
|
self.assertEqual(unified_strdate('July 15th, 2013'), '20130715')
|
||||||
|
self.assertEqual(unified_strdate('September 1st, 2013'), '20130901')
|
||||||
|
self.assertEqual(unified_strdate('Sep 2nd, 2013'), '20130902')
|
||||||
|
|
||||||
def test_unified_timestamps(self):
|
def test_unified_timestamps(self):
|
||||||
self.assertEqual(unified_timestamp('December 21, 2010'), 1292889600)
|
self.assertEqual(unified_timestamp('December 21, 2010'), 1292889600)
|
||||||
@ -507,6 +510,7 @@ class TestUtil(unittest.TestCase):
|
|||||||
self.assertEqual(parse_duration('1 hour 3 minutes'), 3780)
|
self.assertEqual(parse_duration('1 hour 3 minutes'), 3780)
|
||||||
self.assertEqual(parse_duration('87 Min.'), 5220)
|
self.assertEqual(parse_duration('87 Min.'), 5220)
|
||||||
self.assertEqual(parse_duration('PT1H0.040S'), 3600.04)
|
self.assertEqual(parse_duration('PT1H0.040S'), 3600.04)
|
||||||
|
self.assertEqual(parse_duration('PT00H03M30SZ'), 210)
|
||||||
|
|
||||||
def test_fix_xml_ampersands(self):
|
def test_fix_xml_ampersands(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -781,12 +785,27 @@ class TestUtil(unittest.TestCase):
|
|||||||
on = js_to_json('["abc", "def",]')
|
on = js_to_json('["abc", "def",]')
|
||||||
self.assertEqual(json.loads(on), ['abc', 'def'])
|
self.assertEqual(json.loads(on), ['abc', 'def'])
|
||||||
|
|
||||||
|
on = js_to_json('[/*comment\n*/"abc"/*comment\n*/,/*comment\n*/"def",/*comment\n*/]')
|
||||||
|
self.assertEqual(json.loads(on), ['abc', 'def'])
|
||||||
|
|
||||||
|
on = js_to_json('[//comment\n"abc" //comment\n,//comment\n"def",//comment\n]')
|
||||||
|
self.assertEqual(json.loads(on), ['abc', 'def'])
|
||||||
|
|
||||||
on = js_to_json('{"abc": "def",}')
|
on = js_to_json('{"abc": "def",}')
|
||||||
self.assertEqual(json.loads(on), {'abc': 'def'})
|
self.assertEqual(json.loads(on), {'abc': 'def'})
|
||||||
|
|
||||||
|
on = js_to_json('{/*comment\n*/"abc"/*comment\n*/:/*comment\n*/"def"/*comment\n*/,/*comment\n*/}')
|
||||||
|
self.assertEqual(json.loads(on), {'abc': 'def'})
|
||||||
|
|
||||||
on = js_to_json('{ 0: /* " \n */ ",]" , }')
|
on = js_to_json('{ 0: /* " \n */ ",]" , }')
|
||||||
self.assertEqual(json.loads(on), {'0': ',]'})
|
self.assertEqual(json.loads(on), {'0': ',]'})
|
||||||
|
|
||||||
|
on = js_to_json('{ /*comment\n*/0/*comment\n*/: /* " \n */ ",]" , }')
|
||||||
|
self.assertEqual(json.loads(on), {'0': ',]'})
|
||||||
|
|
||||||
|
on = js_to_json('{ 0: // comment\n1 }')
|
||||||
|
self.assertEqual(json.loads(on), {'0': 1})
|
||||||
|
|
||||||
on = js_to_json(r'["<p>x<\/p>"]')
|
on = js_to_json(r'["<p>x<\/p>"]')
|
||||||
self.assertEqual(json.loads(on), ['<p>x</p>'])
|
self.assertEqual(json.loads(on), ['<p>x</p>'])
|
||||||
|
|
||||||
@ -796,15 +815,27 @@ class TestUtil(unittest.TestCase):
|
|||||||
on = js_to_json("['a\\\nb']")
|
on = js_to_json("['a\\\nb']")
|
||||||
self.assertEqual(json.loads(on), ['ab'])
|
self.assertEqual(json.loads(on), ['ab'])
|
||||||
|
|
||||||
|
on = js_to_json("/*comment\n*/[/*comment\n*/'a\\\nb'/*comment\n*/]/*comment\n*/")
|
||||||
|
self.assertEqual(json.loads(on), ['ab'])
|
||||||
|
|
||||||
on = js_to_json('{0xff:0xff}')
|
on = js_to_json('{0xff:0xff}')
|
||||||
self.assertEqual(json.loads(on), {'255': 255})
|
self.assertEqual(json.loads(on), {'255': 255})
|
||||||
|
|
||||||
|
on = js_to_json('{/*comment\n*/0xff/*comment\n*/:/*comment\n*/0xff/*comment\n*/}')
|
||||||
|
self.assertEqual(json.loads(on), {'255': 255})
|
||||||
|
|
||||||
on = js_to_json('{077:077}')
|
on = js_to_json('{077:077}')
|
||||||
self.assertEqual(json.loads(on), {'63': 63})
|
self.assertEqual(json.loads(on), {'63': 63})
|
||||||
|
|
||||||
|
on = js_to_json('{/*comment\n*/077/*comment\n*/:/*comment\n*/077/*comment\n*/}')
|
||||||
|
self.assertEqual(json.loads(on), {'63': 63})
|
||||||
|
|
||||||
on = js_to_json('{42:42}')
|
on = js_to_json('{42:42}')
|
||||||
self.assertEqual(json.loads(on), {'42': 42})
|
self.assertEqual(json.loads(on), {'42': 42})
|
||||||
|
|
||||||
|
on = js_to_json('{/*comment\n*/42/*comment\n*/:/*comment\n*/42/*comment\n*/}')
|
||||||
|
self.assertEqual(json.loads(on), {'42': 42})
|
||||||
|
|
||||||
def test_extract_attributes(self):
|
def test_extract_attributes(self):
|
||||||
self.assertEqual(extract_attributes('<e x="y">'), {'x': 'y'})
|
self.assertEqual(extract_attributes('<e x="y">'), {'x': 'y'})
|
||||||
self.assertEqual(extract_attributes("<e x='y'>"), {'x': 'y'})
|
self.assertEqual(extract_attributes("<e x='y'>"), {'x': 'y'})
|
||||||
|
@ -24,6 +24,7 @@ import sys
|
|||||||
import time
|
import time
|
||||||
import tokenize
|
import tokenize
|
||||||
import traceback
|
import traceback
|
||||||
|
import random
|
||||||
|
|
||||||
from .compat import (
|
from .compat import (
|
||||||
compat_basestring,
|
compat_basestring,
|
||||||
@ -159,6 +160,7 @@ class YoutubeDL(object):
|
|||||||
playlistend: Playlist item to end at.
|
playlistend: Playlist item to end at.
|
||||||
playlist_items: Specific indices of playlist to download.
|
playlist_items: Specific indices of playlist to download.
|
||||||
playlistreverse: Download playlist items in reverse order.
|
playlistreverse: Download playlist items in reverse order.
|
||||||
|
playlistrandom: Download playlist items in random order.
|
||||||
matchtitle: Download only matching titles.
|
matchtitle: Download only matching titles.
|
||||||
rejecttitle: Reject downloads for matching titles.
|
rejecttitle: Reject downloads for matching titles.
|
||||||
logger: Log messages to a logging.Logger instance.
|
logger: Log messages to a logging.Logger instance.
|
||||||
@ -584,7 +586,7 @@ class YoutubeDL(object):
|
|||||||
if autonumber_size is None:
|
if autonumber_size is None:
|
||||||
autonumber_size = 5
|
autonumber_size = 5
|
||||||
autonumber_templ = '%0' + str(autonumber_size) + 'd'
|
autonumber_templ = '%0' + str(autonumber_size) + 'd'
|
||||||
template_dict['autonumber'] = autonumber_templ % self._num_downloads
|
template_dict['autonumber'] = autonumber_templ % (self.params.get('autonumber_start', 1) - 1 + self._num_downloads)
|
||||||
if template_dict.get('playlist_index') is not None:
|
if template_dict.get('playlist_index') is not None:
|
||||||
template_dict['playlist_index'] = '%0*d' % (len(str(template_dict['n_entries'])), template_dict['playlist_index'])
|
template_dict['playlist_index'] = '%0*d' % (len(str(template_dict['n_entries'])), template_dict['playlist_index'])
|
||||||
if template_dict.get('resolution') is None:
|
if template_dict.get('resolution') is None:
|
||||||
@ -842,6 +844,9 @@ class YoutubeDL(object):
|
|||||||
if self.params.get('playlistreverse', False):
|
if self.params.get('playlistreverse', False):
|
||||||
entries = entries[::-1]
|
entries = entries[::-1]
|
||||||
|
|
||||||
|
if self.params.get('playlistrandom', False):
|
||||||
|
random.shuffle(entries)
|
||||||
|
|
||||||
for i, entry in enumerate(entries, 1):
|
for i, entry in enumerate(entries, 1):
|
||||||
self.to_screen('[download] Downloading video %s of %s' % (i, n_entries))
|
self.to_screen('[download] Downloading video %s of %s' % (i, n_entries))
|
||||||
extra = {
|
extra = {
|
||||||
@ -1339,7 +1344,7 @@ class YoutubeDL(object):
|
|||||||
format['format_id'] = compat_str(i)
|
format['format_id'] = compat_str(i)
|
||||||
else:
|
else:
|
||||||
# Sanitize format_id from characters used in format selector expression
|
# Sanitize format_id from characters used in format selector expression
|
||||||
format['format_id'] = re.sub('[\s,/+\[\]()]', '_', format['format_id'])
|
format['format_id'] = re.sub(r'[\s,/+\[\]()]', '_', format['format_id'])
|
||||||
format_id = format['format_id']
|
format_id = format['format_id']
|
||||||
if format_id not in formats_dict:
|
if format_id not in formats_dict:
|
||||||
formats_dict[format_id] = []
|
formats_dict[format_id] = []
|
||||||
@ -1363,7 +1368,7 @@ class YoutubeDL(object):
|
|||||||
format['ext'] = determine_ext(format['url']).lower()
|
format['ext'] = determine_ext(format['url']).lower()
|
||||||
# Automatically determine protocol if missing (useful for format
|
# Automatically determine protocol if missing (useful for format
|
||||||
# selection purposes)
|
# selection purposes)
|
||||||
if 'protocol' not in format:
|
if format.get('protocol') is None:
|
||||||
format['protocol'] = determine_protocol(format)
|
format['protocol'] = determine_protocol(format)
|
||||||
# Add HTTP headers, so that external programs can use them from the
|
# Add HTTP headers, so that external programs can use them from the
|
||||||
# json output
|
# json output
|
||||||
|
@ -133,6 +133,12 @@ def _real_main(argv=None):
|
|||||||
parser.error('TV Provider account username missing\n')
|
parser.error('TV Provider account username missing\n')
|
||||||
if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):
|
if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):
|
||||||
parser.error('using output template conflicts with using title, video ID or auto number')
|
parser.error('using output template conflicts with using title, video ID or auto number')
|
||||||
|
if opts.autonumber_size is not None:
|
||||||
|
if opts.autonumber_size <= 0:
|
||||||
|
parser.error('auto number size must be positive')
|
||||||
|
if opts.autonumber_start is not None:
|
||||||
|
if opts.autonumber_start < 0:
|
||||||
|
parser.error('auto number start must be positive or 0')
|
||||||
if opts.usetitle and opts.useid:
|
if opts.usetitle and opts.useid:
|
||||||
parser.error('using title conflicts with using video ID')
|
parser.error('using title conflicts with using video ID')
|
||||||
if opts.username is not None and opts.password is None:
|
if opts.username is not None and opts.password is None:
|
||||||
@ -321,6 +327,7 @@ def _real_main(argv=None):
|
|||||||
'listformats': opts.listformats,
|
'listformats': opts.listformats,
|
||||||
'outtmpl': outtmpl,
|
'outtmpl': outtmpl,
|
||||||
'autonumber_size': opts.autonumber_size,
|
'autonumber_size': opts.autonumber_size,
|
||||||
|
'autonumber_start': opts.autonumber_start,
|
||||||
'restrictfilenames': opts.restrictfilenames,
|
'restrictfilenames': opts.restrictfilenames,
|
||||||
'ignoreerrors': opts.ignoreerrors,
|
'ignoreerrors': opts.ignoreerrors,
|
||||||
'force_generic_extractor': opts.force_generic_extractor,
|
'force_generic_extractor': opts.force_generic_extractor,
|
||||||
@ -337,6 +344,7 @@ def _real_main(argv=None):
|
|||||||
'playliststart': opts.playliststart,
|
'playliststart': opts.playliststart,
|
||||||
'playlistend': opts.playlistend,
|
'playlistend': opts.playlistend,
|
||||||
'playlistreverse': opts.playlist_reverse,
|
'playlistreverse': opts.playlist_reverse,
|
||||||
|
'playlistrandom': opts.playlist_random,
|
||||||
'noplaylist': opts.noplaylist,
|
'noplaylist': opts.noplaylist,
|
||||||
'logtostderr': opts.outtmpl == '-',
|
'logtostderr': opts.outtmpl == '-',
|
||||||
'consoletitle': opts.consoletitle,
|
'consoletitle': opts.consoletitle,
|
||||||
|
@ -2344,7 +2344,7 @@ try:
|
|||||||
from urllib.parse import unquote_plus as compat_urllib_parse_unquote_plus
|
from urllib.parse import unquote_plus as compat_urllib_parse_unquote_plus
|
||||||
except ImportError: # Python 2
|
except ImportError: # Python 2
|
||||||
_asciire = (compat_urllib_parse._asciire if hasattr(compat_urllib_parse, '_asciire')
|
_asciire = (compat_urllib_parse._asciire if hasattr(compat_urllib_parse, '_asciire')
|
||||||
else re.compile('([\x00-\x7f]+)'))
|
else re.compile(r'([\x00-\x7f]+)'))
|
||||||
|
|
||||||
# HACK: The following are the correct unquote_to_bytes, unquote and unquote_plus
|
# HACK: The following are the correct unquote_to_bytes, unquote and unquote_plus
|
||||||
# implementations from cpython 3.4.3's stdlib. Python 2's version
|
# implementations from cpython 3.4.3's stdlib. Python 2's version
|
||||||
@ -2529,6 +2529,24 @@ else:
|
|||||||
el.text = el.text.decode('utf-8')
|
el.text = el.text.decode('utf-8')
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
if hasattr(etree, 'register_namespace'):
|
||||||
|
compat_etree_register_namespace = etree.register_namespace
|
||||||
|
else:
|
||||||
|
def compat_etree_register_namespace(prefix, uri):
|
||||||
|
"""Register a namespace prefix.
|
||||||
|
The registry is global, and any existing mapping for either the
|
||||||
|
given prefix or the namespace URI will be removed.
|
||||||
|
*prefix* is the namespace prefix, *uri* is a namespace uri. Tags and
|
||||||
|
attributes in this namespace will be serialized with prefix if possible.
|
||||||
|
ValueError is raised if prefix is reserved or is invalid.
|
||||||
|
"""
|
||||||
|
if re.match(r"ns\d+$", prefix):
|
||||||
|
raise ValueError("Prefix format reserved for internal use")
|
||||||
|
for k, v in list(etree._namespace_map.items()):
|
||||||
|
if k == uri or v == prefix:
|
||||||
|
del etree._namespace_map[k]
|
||||||
|
etree._namespace_map[uri] = prefix
|
||||||
|
|
||||||
if sys.version_info < (2, 7):
|
if sys.version_info < (2, 7):
|
||||||
# Here comes the crazy part: In 2.6, if the xpath is a unicode,
|
# Here comes the crazy part: In 2.6, if the xpath is a unicode,
|
||||||
# .//node does not match if a node is a direct child of . !
|
# .//node does not match if a node is a direct child of . !
|
||||||
@ -2865,6 +2883,7 @@ __all__ = [
|
|||||||
'compat_cookiejar',
|
'compat_cookiejar',
|
||||||
'compat_cookies',
|
'compat_cookies',
|
||||||
'compat_etree_fromstring',
|
'compat_etree_fromstring',
|
||||||
|
'compat_etree_register_namespace',
|
||||||
'compat_expanduser',
|
'compat_expanduser',
|
||||||
'compat_get_terminal_size',
|
'compat_get_terminal_size',
|
||||||
'compat_getenv',
|
'compat_getenv',
|
||||||
|
@ -17,6 +17,7 @@ from ..utils import (
|
|||||||
encodeArgument,
|
encodeArgument,
|
||||||
handle_youtubedl_headers,
|
handle_youtubedl_headers,
|
||||||
check_executable,
|
check_executable,
|
||||||
|
is_outdated_version,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -198,6 +199,15 @@ class FFmpegFD(ExternalFD):
|
|||||||
|
|
||||||
args = [ffpp.executable, '-y']
|
args = [ffpp.executable, '-y']
|
||||||
|
|
||||||
|
seekable = info_dict.get('_seekable')
|
||||||
|
if seekable is not None:
|
||||||
|
# setting -seekable prevents ffmpeg from guessing if the server
|
||||||
|
# supports seeking(by adding the header `Range: bytes=0-`), which
|
||||||
|
# can cause problems in some cases
|
||||||
|
# https://github.com/rg3/youtube-dl/issues/11800#issuecomment-275037127
|
||||||
|
# http://trac.ffmpeg.org/ticket/6125#comment:10
|
||||||
|
args += ['-seekable', '1' if seekable else '0']
|
||||||
|
|
||||||
args += self._configuration_args()
|
args += self._configuration_args()
|
||||||
|
|
||||||
# start_time = info_dict.get('start_time') or 0
|
# start_time = info_dict.get('start_time') or 0
|
||||||
@ -264,7 +274,9 @@ class FFmpegFD(ExternalFD):
|
|||||||
if self.params.get('hls_use_mpegts', False) or tmpfilename == '-':
|
if self.params.get('hls_use_mpegts', False) or tmpfilename == '-':
|
||||||
args += ['-f', 'mpegts']
|
args += ['-f', 'mpegts']
|
||||||
else:
|
else:
|
||||||
args += ['-f', 'mp4', '-bsf:a', 'aac_adtstoasc']
|
args += ['-f', 'mp4']
|
||||||
|
if (ffpp.basename == 'ffmpeg' and is_outdated_version(ffpp._versions['ffmpeg'], '3.2', False)) and (not info_dict.get('acodec') or info_dict['acodec'].split('.')[0] in ('aac', 'mp4a')):
|
||||||
|
args += ['-bsf:a', 'aac_adtstoasc']
|
||||||
elif protocol == 'rtmp':
|
elif protocol == 'rtmp':
|
||||||
args += ['-f', 'flv']
|
args += ['-f', 'flv']
|
||||||
else:
|
else:
|
||||||
|
@ -61,6 +61,7 @@ class FragmentFD(FileDownloader):
|
|||||||
'noprogress': True,
|
'noprogress': True,
|
||||||
'ratelimit': self.params.get('ratelimit'),
|
'ratelimit': self.params.get('ratelimit'),
|
||||||
'retries': self.params.get('retries', 0),
|
'retries': self.params.get('retries', 0),
|
||||||
|
'nopart': self.params.get('nopart', False),
|
||||||
'test': self.params.get('test', False),
|
'test': self.params.get('test', False),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -23,7 +23,7 @@ class AbcNewsVideoIE(AMPIE):
|
|||||||
'title': '\'This Week\' Exclusive: Iran\'s Foreign Minister Zarif',
|
'title': '\'This Week\' Exclusive: Iran\'s Foreign Minister Zarif',
|
||||||
'description': 'George Stephanopoulos goes one-on-one with Iranian Foreign Minister Dr. Javad Zarif.',
|
'description': 'George Stephanopoulos goes one-on-one with Iranian Foreign Minister Dr. Javad Zarif.',
|
||||||
'duration': 180,
|
'duration': 180,
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
# m3u8 download
|
# m3u8 download
|
||||||
@ -59,7 +59,7 @@ class AbcNewsIE(InfoExtractor):
|
|||||||
'display_id': 'dramatic-video-rare-death-job-america',
|
'display_id': 'dramatic-video-rare-death-job-america',
|
||||||
'title': 'Occupational Hazards',
|
'title': 'Occupational Hazards',
|
||||||
'description': 'Nightline investigates the dangers that lurk at various jobs.',
|
'description': 'Nightline investigates the dangers that lurk at various jobs.',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'upload_date': '20100428',
|
'upload_date': '20100428',
|
||||||
'timestamp': 1272412800,
|
'timestamp': 1272412800,
|
||||||
},
|
},
|
||||||
|
@ -23,7 +23,7 @@ class ABCOTVSIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'East Bay museum celebrates vintage synthesizers',
|
'title': 'East Bay museum celebrates vintage synthesizers',
|
||||||
'description': 'md5:a4f10fb2f2a02565c1749d4adbab4b10',
|
'description': 'md5:a4f10fb2f2a02565c1749d4adbab4b10',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'timestamp': 1421123075,
|
'timestamp': 1421123075,
|
||||||
'upload_date': '20150113',
|
'upload_date': '20150113',
|
||||||
'uploader': 'Jonathan Bloom',
|
'uploader': 'Jonathan Bloom',
|
||||||
|
@ -30,7 +30,7 @@ class AdobeTVIE(AdobeTVBaseIE):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Quick Tip - How to Draw a Circle Around an Object in Photoshop',
|
'title': 'Quick Tip - How to Draw a Circle Around an Object in Photoshop',
|
||||||
'description': 'md5:99ec318dc909d7ba2a1f2b038f7d2311',
|
'description': 'md5:99ec318dc909d7ba2a1f2b038f7d2311',
|
||||||
'thumbnail': 're:https?://.*\.jpg$',
|
'thumbnail': r're:https?://.*\.jpg$',
|
||||||
'upload_date': '20110914',
|
'upload_date': '20110914',
|
||||||
'duration': 60,
|
'duration': 60,
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
|
@ -87,7 +87,7 @@ class AENetworksIE(AENetworksBaseIE):
|
|||||||
self._html_search_meta('aetn:SeriesTitle', webpage))
|
self._html_search_meta('aetn:SeriesTitle', webpage))
|
||||||
elif url_parts_len == 2:
|
elif url_parts_len == 2:
|
||||||
entries = []
|
entries = []
|
||||||
for episode_item in re.findall(r'(?s)<div[^>]+class="[^"]*episode-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)
|
||||||
episode_url = compat_urlparse.urljoin(
|
episode_url = compat_urlparse.urljoin(
|
||||||
url, episode_attributes['data-canonical'])
|
url, episode_attributes['data-canonical'])
|
||||||
|
@ -18,6 +18,7 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class AfreecaTVIE(InfoExtractor):
|
class AfreecaTVIE(InfoExtractor):
|
||||||
|
IE_NAME = 'afreecatv'
|
||||||
IE_DESC = 'afreecatv.com'
|
IE_DESC = 'afreecatv.com'
|
||||||
_VALID_URL = r'''(?x)
|
_VALID_URL = r'''(?x)
|
||||||
https?://
|
https?://
|
||||||
@ -143,3 +144,107 @@ class AfreecaTVIE(InfoExtractor):
|
|||||||
expected=True)
|
expected=True)
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
class AfreecaTVGlobalIE(AfreecaTVIE):
|
||||||
|
IE_NAME = 'afreecatv:global'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?afreeca\.tv/(?P<channel_id>\d+)(?:/v/(?P<video_id>\d+))?'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'http://afreeca.tv/36853014/v/58301',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '58301',
|
||||||
|
'title': 'tryhard top100',
|
||||||
|
'uploader_id': '36853014',
|
||||||
|
'uploader': 'makgi Hearthstone Live!',
|
||||||
|
},
|
||||||
|
'playlist_count': 3,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
channel_id, video_id = re.match(self._VALID_URL, url).groups()
|
||||||
|
video_type = 'video' if video_id else 'live'
|
||||||
|
query = {
|
||||||
|
'pt': 'view',
|
||||||
|
'bid': channel_id,
|
||||||
|
}
|
||||||
|
if video_id:
|
||||||
|
query['vno'] = video_id
|
||||||
|
video_data = self._download_json(
|
||||||
|
'http://api.afreeca.tv/%s/view_%s.php' % (video_type, video_type),
|
||||||
|
video_id or channel_id, query=query)['channel']
|
||||||
|
|
||||||
|
if video_data.get('result') != 1:
|
||||||
|
raise ExtractorError('%s said: %s' % (self.IE_NAME, video_data['remsg']))
|
||||||
|
|
||||||
|
title = video_data['title']
|
||||||
|
|
||||||
|
info = {
|
||||||
|
'thumbnail': video_data.get('thumb'),
|
||||||
|
'view_count': int_or_none(video_data.get('vcnt')),
|
||||||
|
'age_limit': int_or_none(video_data.get('grade')),
|
||||||
|
'uploader_id': channel_id,
|
||||||
|
'uploader': video_data.get('cname'),
|
||||||
|
}
|
||||||
|
|
||||||
|
if video_id:
|
||||||
|
entries = []
|
||||||
|
for i, f in enumerate(video_data.get('flist', [])):
|
||||||
|
video_key = self.parse_video_key(f.get('key', ''))
|
||||||
|
f_url = f.get('file')
|
||||||
|
if not video_key or not f_url:
|
||||||
|
continue
|
||||||
|
entries.append({
|
||||||
|
'id': '%s_%s' % (video_id, video_key.get('part', i + 1)),
|
||||||
|
'title': title,
|
||||||
|
'upload_date': video_key.get('upload_date'),
|
||||||
|
'duration': int_or_none(f.get('length')),
|
||||||
|
'url': f_url,
|
||||||
|
'protocol': 'm3u8_native',
|
||||||
|
'ext': 'mp4',
|
||||||
|
})
|
||||||
|
|
||||||
|
info.update({
|
||||||
|
'id': video_id,
|
||||||
|
'title': title,
|
||||||
|
'duration': int_or_none(video_data.get('length')),
|
||||||
|
})
|
||||||
|
if len(entries) > 1:
|
||||||
|
info['_type'] = 'multi_video'
|
||||||
|
info['entries'] = entries
|
||||||
|
elif len(entries) == 1:
|
||||||
|
i = entries[0].copy()
|
||||||
|
i.update(info)
|
||||||
|
info = i
|
||||||
|
else:
|
||||||
|
formats = []
|
||||||
|
for s in video_data.get('strm', []):
|
||||||
|
s_url = s.get('purl')
|
||||||
|
if not s_url:
|
||||||
|
continue
|
||||||
|
stype = s.get('stype')
|
||||||
|
if stype == 'HLS':
|
||||||
|
formats.extend(self._extract_m3u8_formats(
|
||||||
|
s_url, channel_id, 'mp4', m3u8_id=stype, fatal=False))
|
||||||
|
elif stype == 'RTMP':
|
||||||
|
format_id = [stype]
|
||||||
|
label = s.get('label')
|
||||||
|
if label:
|
||||||
|
format_id.append(label)
|
||||||
|
formats.append({
|
||||||
|
'format_id': '-'.join(format_id),
|
||||||
|
'url': s_url,
|
||||||
|
'tbr': int_or_none(s.get('bps')),
|
||||||
|
'height': int_or_none(s.get('brt')),
|
||||||
|
'ext': 'flv',
|
||||||
|
'rtmp_live': True,
|
||||||
|
})
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
info.update({
|
||||||
|
'id': channel_id,
|
||||||
|
'title': self._live_title(title),
|
||||||
|
'is_live': True,
|
||||||
|
'formats': formats,
|
||||||
|
})
|
||||||
|
|
||||||
|
return info
|
||||||
|
@ -20,7 +20,7 @@ class AirMozillaIE(InfoExtractor):
|
|||||||
'id': '6x4q2w',
|
'id': '6x4q2w',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Privacy Lab - a meetup for privacy minded people in San Francisco',
|
'title': 'Privacy Lab - a meetup for privacy minded people in San Francisco',
|
||||||
'thumbnail': 're:https?://vid\.ly/(?P<id>[0-9a-z-]+)/poster',
|
'thumbnail': r're:https?://vid\.ly/(?P<id>[0-9a-z-]+)/poster',
|
||||||
'description': 'Brings together privacy professionals and others interested in privacy at for-profits, non-profits, and NGOs in an effort to contribute to the state of the ecosystem...',
|
'description': 'Brings together privacy professionals and others interested in privacy at for-profits, non-profits, and NGOs in an effort to contribute to the state of the ecosystem...',
|
||||||
'timestamp': 1422487800,
|
'timestamp': 1422487800,
|
||||||
'upload_date': '20150128',
|
'upload_date': '20150128',
|
||||||
|
@ -21,7 +21,7 @@ class AllocineIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Astérix - Le Domaine des Dieux Teaser VF',
|
'title': 'Astérix - Le Domaine des Dieux Teaser VF',
|
||||||
'description': 'md5:4a754271d9c6f16c72629a8a993ee884',
|
'description': 'md5:4a754271d9c6f16c72629a8a993ee884',
|
||||||
'thumbnail': 're:http://.*\.jpg',
|
'thumbnail': r're:http://.*\.jpg',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.allocine.fr/video/player_gen_cmedia=19540403&cfilm=222257.html',
|
'url': 'http://www.allocine.fr/video/player_gen_cmedia=19540403&cfilm=222257.html',
|
||||||
@ -32,7 +32,7 @@ class AllocineIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Planes 2 Bande-annonce VF',
|
'title': 'Planes 2 Bande-annonce VF',
|
||||||
'description': 'Regardez la bande annonce du film Planes 2 (Planes 2 Bande-annonce VF). Planes 2, un film de Roberts Gannaway',
|
'description': 'Regardez la bande annonce du film Planes 2 (Planes 2 Bande-annonce VF). Planes 2, un film de Roberts Gannaway',
|
||||||
'thumbnail': 're:http://.*\.jpg',
|
'thumbnail': r're:http://.*\.jpg',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.allocine.fr/video/player_gen_cmedia=19544709&cfilm=181290.html',
|
'url': 'http://www.allocine.fr/video/player_gen_cmedia=19544709&cfilm=181290.html',
|
||||||
@ -43,7 +43,7 @@ class AllocineIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Dragons 2 - Bande annonce finale VF',
|
'title': 'Dragons 2 - Bande annonce finale VF',
|
||||||
'description': 'md5:6cdd2d7c2687d4c6aafe80a35e17267a',
|
'description': 'md5:6cdd2d7c2687d4c6aafe80a35e17267a',
|
||||||
'thumbnail': 're:http://.*\.jpg',
|
'thumbnail': r're:http://.*\.jpg',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.allocine.fr/video/video-19550147/',
|
'url': 'http://www.allocine.fr/video/video-19550147/',
|
||||||
@ -53,7 +53,7 @@ class AllocineIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Faux Raccord N°123 - Les gaffes de Cliffhanger',
|
'title': 'Faux Raccord N°123 - Les gaffes de Cliffhanger',
|
||||||
'description': 'md5:bc734b83ffa2d8a12188d9eb48bb6354',
|
'description': 'md5:bc734b83ffa2d8a12188d9eb48bb6354',
|
||||||
'thumbnail': 're:http://.*\.jpg',
|
'thumbnail': r're:http://.*\.jpg',
|
||||||
},
|
},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ class AlphaPornoIE(InfoExtractor):
|
|||||||
'display_id': 'sensual-striptease-porn-with-samantha-alexandra',
|
'display_id': 'sensual-striptease-porn-with-samantha-alexandra',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Sensual striptease porn with Samantha Alexandra',
|
'title': 'Sensual striptease porn with Samantha Alexandra',
|
||||||
'thumbnail': 're:https?://.*\.jpg$',
|
'thumbnail': r're:https?://.*\.jpg$',
|
||||||
'timestamp': 1418694611,
|
'timestamp': 1418694611,
|
||||||
'upload_date': '20141216',
|
'upload_date': '20141216',
|
||||||
'duration': 387,
|
'duration': 387,
|
||||||
|
@ -12,7 +12,7 @@ from ..utils import (
|
|||||||
|
|
||||||
class AolIE(InfoExtractor):
|
class AolIE(InfoExtractor):
|
||||||
IE_NAME = 'on.aol.com'
|
IE_NAME = 'on.aol.com'
|
||||||
_VALID_URL = r'(?:aol-video:|https?://on\.aol\.com/(?:[^/]+/)*(?:[^/?#&]+-)?)(?P<id>[^/?#&]+)'
|
_VALID_URL = r'(?:aol-video:|https?://(?:(?:www|on)\.)?aol\.com/(?:[^/]+/)*(?:[^/?#&]+-)?)(?P<id>[^/?#&]+)'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
# video with 5min ID
|
# video with 5min ID
|
||||||
@ -33,7 +33,7 @@ class AolIE(InfoExtractor):
|
|||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
# video with vidible ID
|
# video with vidible ID
|
||||||
'url': 'http://on.aol.com/video/netflix-is-raising-rates-5707d6b8e4b090497b04f706?context=PC:homepage:PL1944:1460189336183',
|
'url': 'http://www.aol.com/video/view/netflix-is-raising-rates/5707d6b8e4b090497b04f706/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '5707d6b8e4b090497b04f706',
|
'id': '5707d6b8e4b090497b04f706',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
@ -108,30 +108,3 @@ class AolIE(InfoExtractor):
|
|||||||
'uploader': video_data.get('videoOwner'),
|
'uploader': video_data.get('videoOwner'),
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class AolFeaturesIE(InfoExtractor):
|
|
||||||
IE_NAME = 'features.aol.com'
|
|
||||||
_VALID_URL = r'https?://features\.aol\.com/video/(?P<id>[^/?#]+)'
|
|
||||||
|
|
||||||
_TESTS = [{
|
|
||||||
'url': 'http://features.aol.com/video/behind-secret-second-careers-late-night-talk-show-hosts',
|
|
||||||
'md5': '7db483bb0c09c85e241f84a34238cc75',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '519507715',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'What To Watch - February 17, 2016',
|
|
||||||
},
|
|
||||||
'add_ie': ['FiveMin'],
|
|
||||||
'params': {
|
|
||||||
# encrypted m3u8 download
|
|
||||||
'skip_download': True,
|
|
||||||
},
|
|
||||||
}]
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
|
||||||
display_id = self._match_id(url)
|
|
||||||
webpage = self._download_webpage(url, display_id)
|
|
||||||
return self.url_result(self._search_regex(
|
|
||||||
r'<script type="text/javascript" src="(https?://[^/]*?5min\.com/Scripts/PlayerSeed\.js[^"]+)"',
|
|
||||||
webpage, '5min embed url'), 'FiveMin')
|
|
||||||
|
@ -253,7 +253,7 @@ class ARDIE(InfoExtractor):
|
|||||||
'duration': 2600,
|
'duration': 2600,
|
||||||
'title': 'Die Story im Ersten: Mission unter falscher Flagge',
|
'title': 'Die Story im Ersten: Mission unter falscher Flagge',
|
||||||
'upload_date': '20140804',
|
'upload_date': '20140804',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
},
|
},
|
||||||
'skip': 'HTTP Error 404: Not Found',
|
'skip': 'HTTP Error 404: Not Found',
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ class AtresPlayerIE(InfoExtractor):
|
|||||||
'title': 'Especial Solidario de Nochebuena',
|
'title': 'Especial Solidario de Nochebuena',
|
||||||
'description': 'md5:e2d52ff12214fa937107d21064075bf1',
|
'description': 'md5:e2d52ff12214fa937107d21064075bf1',
|
||||||
'duration': 5527.6,
|
'duration': 5527.6,
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
},
|
},
|
||||||
'skip': 'This video is only available for registered users'
|
'skip': 'This video is only available for registered users'
|
||||||
},
|
},
|
||||||
@ -43,7 +43,7 @@ class AtresPlayerIE(InfoExtractor):
|
|||||||
'title': 'David Bustamante',
|
'title': 'David Bustamante',
|
||||||
'description': 'md5:f33f1c0a05be57f6708d4dd83a3b81c6',
|
'description': 'md5:f33f1c0a05be57f6708d4dd83a3b81c6',
|
||||||
'duration': 1439.0,
|
'duration': 1439.0,
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@ class ATTTechChannelIE(InfoExtractor):
|
|||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
'title': 'AT&T Archives : The UNIX System: Making Computers Easier to Use',
|
'title': 'AT&T Archives : The UNIX System: Making Computers Easier to Use',
|
||||||
'description': 'A 1982 film about UNIX is the foundation for software in use around Bell Labs and AT&T.',
|
'description': 'A 1982 film about UNIX is the foundation for software in use around Bell Labs and AT&T.',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'upload_date': '20140127',
|
'upload_date': '20140127',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
|
@ -17,7 +17,7 @@ class AudioBoomIE(InfoExtractor):
|
|||||||
'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': 'Steve Czaban',
|
||||||
'uploader_url': 're:https?://(?:www\.)?audioboom\.com/channel/steveczabanyahoosportsradio',
|
'uploader_url': r're:https?://(?:www\.)?audioboom\.com/channel/steveczabanyahoosportsradio',
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://audioboom.com/posts/4279833-3-09-2016-czaban-hour-3?t=0',
|
'url': 'https://audioboom.com/posts/4279833-3-09-2016-czaban-hour-3?t=0',
|
||||||
|
172
youtube_dl/extractor/azmedien.py
Normal file
172
youtube_dl/extractor/azmedien.py
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from .kaltura import KalturaIE
|
||||||
|
from ..utils import (
|
||||||
|
get_element_by_id,
|
||||||
|
strip_or_none,
|
||||||
|
urljoin,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AZMedienBaseIE(InfoExtractor):
|
||||||
|
def _kaltura_video(self, partner_id, entry_id):
|
||||||
|
return self.url_result(
|
||||||
|
'kaltura:%s:%s' % (partner_id, entry_id), ie=KalturaIE.ie_key(),
|
||||||
|
video_id=entry_id)
|
||||||
|
|
||||||
|
|
||||||
|
class AZMedienIE(AZMedienBaseIE):
|
||||||
|
IE_DESC = 'AZ Medien videos'
|
||||||
|
_VALID_URL = r'''(?x)
|
||||||
|
https?://
|
||||||
|
(?:www\.)?
|
||||||
|
(?:
|
||||||
|
telezueri\.ch|
|
||||||
|
telebaern\.tv|
|
||||||
|
telem1\.ch
|
||||||
|
)/
|
||||||
|
[0-9]+-show-[^/\#]+
|
||||||
|
(?:
|
||||||
|
/[0-9]+-episode-[^/\#]+
|
||||||
|
(?:
|
||||||
|
/[0-9]+-segment-(?:[^/\#]+\#)?|
|
||||||
|
\#
|
||||||
|
)|
|
||||||
|
\#
|
||||||
|
)
|
||||||
|
(?P<id>[^\#]+)
|
||||||
|
'''
|
||||||
|
|
||||||
|
_TESTS = [{
|
||||||
|
# URL with 'segment'
|
||||||
|
'url': 'http://www.telezueri.ch/62-show-zuerinews/13772-episode-sonntag-18-dezember-2016/32419-segment-massenabweisungen-beim-hiltl-club-wegen-pelzboom',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1_2444peh4',
|
||||||
|
'ext': 'mov',
|
||||||
|
'title': 'Massenabweisungen beim Hiltl Club wegen Pelzboom',
|
||||||
|
'description': 'md5:9ea9dd1b159ad65b36ddcf7f0d7c76a8',
|
||||||
|
'uploader_id': 'TeleZ?ri',
|
||||||
|
'upload_date': '20161218',
|
||||||
|
'timestamp': 1482084490,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
# URL with 'segment' and fragment:
|
||||||
|
'url': 'http://www.telebaern.tv/118-show-news/14240-episode-dienstag-17-januar-2017/33666-segment-achtung-gefahr#zu-wenig-pflegerinnen-und-pfleger',
|
||||||
|
'only_matching': True
|
||||||
|
}, {
|
||||||
|
# URL with 'episode' and fragment:
|
||||||
|
'url': 'http://www.telem1.ch/47-show-sonntalk/13986-episode-soldaten-fuer-grenzschutz-energiestrategie-obama-bilanz#soldaten-fuer-grenzschutz-energiestrategie-obama-bilanz',
|
||||||
|
'only_matching': True
|
||||||
|
}, {
|
||||||
|
# URL with 'show' and fragment:
|
||||||
|
'url': 'http://www.telezueri.ch/66-show-sonntalk#burka-plakate-trump-putin-china-besuch',
|
||||||
|
'only_matching': True
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
|
partner_id = self._search_regex(
|
||||||
|
r'<script[^>]+src=["\'](?:https?:)?//(?:[^/]+\.)?kaltura\.com(?:/[^/]+)*/(?:p|partner_id)/([0-9]+)',
|
||||||
|
webpage, 'kaltura partner id')
|
||||||
|
entry_id = self._html_search_regex(
|
||||||
|
r'<a[^>]+data-id=(["\'])(?P<id>(?:(?!\1).)+)\1[^>]+data-slug=["\']%s'
|
||||||
|
% re.escape(video_id), webpage, 'kaltura entry id', group='id')
|
||||||
|
|
||||||
|
return self._kaltura_video(partner_id, entry_id)
|
||||||
|
|
||||||
|
|
||||||
|
class AZMedienPlaylistIE(AZMedienBaseIE):
|
||||||
|
IE_DESC = 'AZ Medien playlists'
|
||||||
|
_VALID_URL = r'''(?x)
|
||||||
|
https?://
|
||||||
|
(?:www\.)?
|
||||||
|
(?:
|
||||||
|
telezueri\.ch|
|
||||||
|
telebaern\.tv|
|
||||||
|
telem1\.ch
|
||||||
|
)/
|
||||||
|
(?P<id>[0-9]+-
|
||||||
|
(?:
|
||||||
|
show|
|
||||||
|
topic|
|
||||||
|
themen
|
||||||
|
)-[^/\#]+
|
||||||
|
(?:
|
||||||
|
/[0-9]+-episode-[^/\#]+
|
||||||
|
)?
|
||||||
|
)$
|
||||||
|
'''
|
||||||
|
|
||||||
|
_TESTS = [{
|
||||||
|
# URL with 'episode'
|
||||||
|
'url': 'http://www.telebaern.tv/118-show-news/13735-episode-donnerstag-15-dezember-2016',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '118-show-news/13735-episode-donnerstag-15-dezember-2016',
|
||||||
|
'title': 'News - Donnerstag, 15. Dezember 2016',
|
||||||
|
},
|
||||||
|
'playlist_count': 9,
|
||||||
|
}, {
|
||||||
|
# URL with 'themen'
|
||||||
|
'url': 'http://www.telem1.ch/258-themen-tele-m1-classics',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '258-themen-tele-m1-classics',
|
||||||
|
'title': 'Tele M1 Classics',
|
||||||
|
},
|
||||||
|
'playlist_mincount': 15,
|
||||||
|
}, {
|
||||||
|
# URL with 'topic', contains nested playlists
|
||||||
|
'url': 'http://www.telezueri.ch/219-topic-aera-trump-hat-offiziell-begonnen',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# URL with 'show' only
|
||||||
|
'url': 'http://www.telezueri.ch/86-show-talktaeglich',
|
||||||
|
'only_matching': True
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
show_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, show_id)
|
||||||
|
|
||||||
|
entries = []
|
||||||
|
|
||||||
|
partner_id = self._search_regex(
|
||||||
|
r'src=["\'](?:https?:)?//(?:[^/]+\.)kaltura\.com/(?:[^/]+/)*(?:p|partner_id)/(\d+)',
|
||||||
|
webpage, 'kaltura partner id', default=None)
|
||||||
|
|
||||||
|
if partner_id:
|
||||||
|
entries = [
|
||||||
|
self._kaltura_video(partner_id, m.group('id'))
|
||||||
|
for m in re.finditer(
|
||||||
|
r'data-id=(["\'])(?P<id>(?:(?!\1).)+)\1', webpage)]
|
||||||
|
|
||||||
|
if not entries:
|
||||||
|
entries = [
|
||||||
|
self.url_result(m.group('url'), ie=AZMedienIE.ie_key())
|
||||||
|
for m in re.finditer(
|
||||||
|
r'<a[^>]+data-real=(["\'])(?P<url>http.+?)\1', webpage)]
|
||||||
|
|
||||||
|
if not entries:
|
||||||
|
entries = [
|
||||||
|
# May contain nested playlists (e.g. [1]) thus no explicit
|
||||||
|
# ie_key
|
||||||
|
# 1. http://www.telezueri.ch/219-topic-aera-trump-hat-offiziell-begonnen)
|
||||||
|
self.url_result(urljoin(url, m.group('url')))
|
||||||
|
for m in re.finditer(
|
||||||
|
r'<a[^>]+name=[^>]+href=(["\'])(?P<url>/.+?)\1', webpage)]
|
||||||
|
|
||||||
|
title = self._search_regex(
|
||||||
|
r'episodeShareTitle\s*=\s*(["\'])(?P<title>(?:(?!\1).)+)\1',
|
||||||
|
webpage, 'title',
|
||||||
|
default=strip_or_none(get_element_by_id(
|
||||||
|
'video-title', webpage)), group='title')
|
||||||
|
|
||||||
|
return self.playlist_result(entries, show_id, title)
|
@ -21,7 +21,7 @@ class AzubuIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': '2014 HOT6 CUP LAST BIG MATCH Ro8 Day 1',
|
'title': '2014 HOT6 CUP LAST BIG MATCH Ro8 Day 1',
|
||||||
'description': 'md5:d06bdea27b8cc4388a90ad35b5c66c01',
|
'description': 'md5:d06bdea27b8cc4388a90ad35b5c66c01',
|
||||||
'thumbnail': 're:^https?://.*\.jpe?g',
|
'thumbnail': r're:^https?://.*\.jpe?g',
|
||||||
'timestamp': 1417523507.334,
|
'timestamp': 1417523507.334,
|
||||||
'upload_date': '20141202',
|
'upload_date': '20141202',
|
||||||
'duration': 9988.7,
|
'duration': 9988.7,
|
||||||
@ -38,7 +38,7 @@ class AzubuIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Fnatic at Worlds 2014: Toyz - "I love Rekkles, he has amazing mechanics"',
|
'title': 'Fnatic at Worlds 2014: Toyz - "I love Rekkles, he has amazing mechanics"',
|
||||||
'description': 'md5:4a649737b5f6c8b5c5be543e88dc62af',
|
'description': 'md5:4a649737b5f6c8b5c5be543e88dc62af',
|
||||||
'thumbnail': 're:^https?://.*\.jpe?g',
|
'thumbnail': r're:^https?://.*\.jpe?g',
|
||||||
'timestamp': 1410530893.320,
|
'timestamp': 1410530893.320,
|
||||||
'upload_date': '20140912',
|
'upload_date': '20140912',
|
||||||
'duration': 172.385,
|
'duration': 172.385,
|
||||||
|
@ -209,6 +209,15 @@ class BandcampAlbumIE(InfoExtractor):
|
|||||||
'id': 'entropy-ep',
|
'id': 'entropy-ep',
|
||||||
},
|
},
|
||||||
'playlist_mincount': 3,
|
'playlist_mincount': 3,
|
||||||
|
}, {
|
||||||
|
# not all tracks have songs
|
||||||
|
'url': 'https://insulters.bandcamp.com/album/we-are-the-plague',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'we-are-the-plague',
|
||||||
|
'title': 'WE ARE THE PLAGUE',
|
||||||
|
'uploader_id': 'insulters',
|
||||||
|
},
|
||||||
|
'playlist_count': 2,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
@ -217,12 +226,16 @@ class BandcampAlbumIE(InfoExtractor):
|
|||||||
album_id = mobj.group('album_id')
|
album_id = mobj.group('album_id')
|
||||||
playlist_id = album_id or uploader_id
|
playlist_id = album_id or uploader_id
|
||||||
webpage = self._download_webpage(url, playlist_id)
|
webpage = self._download_webpage(url, playlist_id)
|
||||||
tracks_paths = re.findall(r'<a href="(.*?)" itemprop="url">', webpage)
|
track_elements = re.findall(
|
||||||
if not tracks_paths:
|
r'(?s)<div[^>]*>(.*?<a[^>]+href="([^"]+?)"[^>]+itemprop="url"[^>]*>.*?)</div>', webpage)
|
||||||
|
if not track_elements:
|
||||||
raise ExtractorError('The page doesn\'t contain any tracks')
|
raise ExtractorError('The page doesn\'t contain any tracks')
|
||||||
|
# Only tracks with duration info have songs
|
||||||
entries = [
|
entries = [
|
||||||
self.url_result(compat_urlparse.urljoin(url, t_path), ie=BandcampIE.ie_key())
|
self.url_result(compat_urlparse.urljoin(url, t_path), ie=BandcampIE.ie_key())
|
||||||
for t_path in tracks_paths]
|
for elem_content, t_path in track_elements
|
||||||
|
if self._html_search_meta('duration', elem_content, default=None)]
|
||||||
|
|
||||||
title = self._html_search_regex(
|
title = self._html_search_regex(
|
||||||
r'album_title\s*:\s*"((?:\\.|[^"\\])+?)"',
|
r'album_title\s*:\s*"((?:\\.|[^"\\])+?)"',
|
||||||
webpage, 'title', fatal=False)
|
webpage, 'title', fatal=False)
|
||||||
|
73
youtube_dl/extractor/beampro.py
Normal file
73
youtube_dl/extractor/beampro.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
ExtractorError,
|
||||||
|
clean_html,
|
||||||
|
compat_str,
|
||||||
|
int_or_none,
|
||||||
|
parse_iso8601,
|
||||||
|
try_get,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BeamProLiveIE(InfoExtractor):
|
||||||
|
IE_NAME = 'Beam:live'
|
||||||
|
_VALID_URL = r'https?://(?:\w+\.)?beam\.pro/(?P<id>[^/?#&]+)'
|
||||||
|
_RATINGS = {'family': 0, 'teen': 13, '18+': 18}
|
||||||
|
_TEST = {
|
||||||
|
'url': 'http://www.beam.pro/niterhayven',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '261562',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Introducing The Witcher 3 // The Grind Starts Now!',
|
||||||
|
'description': 'md5:0b161ac080f15fe05d18a07adb44a74d',
|
||||||
|
'thumbnail': r're:https://.*\.jpg$',
|
||||||
|
'timestamp': 1483477281,
|
||||||
|
'upload_date': '20170103',
|
||||||
|
'uploader': 'niterhayven',
|
||||||
|
'uploader_id': '373396',
|
||||||
|
'age_limit': 18,
|
||||||
|
'is_live': True,
|
||||||
|
'view_count': int,
|
||||||
|
},
|
||||||
|
'skip': 'niterhayven is offline',
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
channel_name = self._match_id(url)
|
||||||
|
|
||||||
|
chan = self._download_json(
|
||||||
|
'https://beam.pro/api/v1/channels/%s' % channel_name, channel_name)
|
||||||
|
|
||||||
|
if chan.get('online') is False:
|
||||||
|
raise ExtractorError(
|
||||||
|
'{0} is offline'.format(channel_name), expected=True)
|
||||||
|
|
||||||
|
channel_id = chan['id']
|
||||||
|
|
||||||
|
formats = self._extract_m3u8_formats(
|
||||||
|
'https://beam.pro/api/v1/channels/%s/manifest.m3u8' % channel_id,
|
||||||
|
channel_name, ext='mp4', m3u8_id='hls', fatal=False)
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
user_id = chan.get('userId') or try_get(chan, lambda x: x['user']['id'])
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': compat_str(chan.get('id') or channel_name),
|
||||||
|
'title': self._live_title(chan.get('name') or channel_name),
|
||||||
|
'description': clean_html(chan.get('description')),
|
||||||
|
'thumbnail': try_get(chan, lambda x: x['thumbnail']['url'], compat_str),
|
||||||
|
'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,
|
||||||
|
'view_count': int_or_none(chan.get('viewersTotal')),
|
||||||
|
'formats': formats,
|
||||||
|
}
|
@ -17,7 +17,7 @@ class BetIE(MTVServicesInfoExtractor):
|
|||||||
'description': 'President Obama urges persistence in confronting racism and bias.',
|
'description': 'President Obama urges persistence in confronting racism and bias.',
|
||||||
'duration': 1534,
|
'duration': 1534,
|
||||||
'upload_date': '20141208',
|
'upload_date': '20141208',
|
||||||
'thumbnail': 're:(?i)^https?://.*\.jpg$',
|
'thumbnail': r're:(?i)^https?://.*\.jpg$',
|
||||||
'subtitles': {
|
'subtitles': {
|
||||||
'en': 'mincount:2',
|
'en': 'mincount:2',
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ class BetIE(MTVServicesInfoExtractor):
|
|||||||
'description': 'A BET News special.',
|
'description': 'A BET News special.',
|
||||||
'duration': 1696,
|
'duration': 1696,
|
||||||
'upload_date': '20141125',
|
'upload_date': '20141125',
|
||||||
'thumbnail': 're:(?i)^https?://.*\.jpg$',
|
'thumbnail': r're:(?i)^https?://.*\.jpg$',
|
||||||
'subtitles': {
|
'subtitles': {
|
||||||
'en': 'mincount:2',
|
'en': 'mincount:2',
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ class BildIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Das können die neuen iPads',
|
'title': 'Das können die neuen iPads',
|
||||||
'description': 'md5:a4058c4fa2a804ab59c00d7244bbf62f',
|
'description': 'md5:a4058c4fa2a804ab59c00d7244bbf62f',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 196,
|
'duration': 196,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,19 +5,27 @@ import hashlib
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_parse_qs
|
from ..compat import (
|
||||||
|
compat_parse_qs,
|
||||||
|
compat_urlparse,
|
||||||
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
ExtractorError,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
|
parse_iso8601,
|
||||||
|
smuggle_url,
|
||||||
|
strip_jsonp,
|
||||||
unified_timestamp,
|
unified_timestamp,
|
||||||
|
unsmuggle_url,
|
||||||
urlencode_postdata,
|
urlencode_postdata,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BiliBiliIE(InfoExtractor):
|
class BiliBiliIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.|bangumi\.|)bilibili\.(?:tv|com)/(?:video/av|anime/v/)(?P<id>\d+)'
|
_VALID_URL = r'https?://(?:www\.|bangumi\.|)bilibili\.(?:tv|com)/(?:video/av|anime/(?P<anime_id>\d+)/play#)(?P<id>\d+)'
|
||||||
|
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
'url': 'http://www.bilibili.tv/video/av1074402/',
|
'url': 'http://www.bilibili.tv/video/av1074402/',
|
||||||
'md5': '9fa226fe2b8a9a4d5a69b4c6a183417e',
|
'md5': '9fa226fe2b8a9a4d5a69b4c6a183417e',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@ -28,29 +36,65 @@ class BiliBiliIE(InfoExtractor):
|
|||||||
'duration': 308.315,
|
'duration': 308.315,
|
||||||
'timestamp': 1398012660,
|
'timestamp': 1398012660,
|
||||||
'upload_date': '20140420',
|
'upload_date': '20140420',
|
||||||
'thumbnail': 're:^https?://.+\.jpg',
|
'thumbnail': r're:^https?://.+\.jpg',
|
||||||
'uploader': '菊子桑',
|
'uploader': '菊子桑',
|
||||||
'uploader_id': '156160',
|
'uploader_id': '156160',
|
||||||
},
|
},
|
||||||
}
|
}, {
|
||||||
|
# Tested in BiliBiliBangumiIE
|
||||||
|
'url': 'http://bangumi.bilibili.com/anime/1869/play#40062',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://bangumi.bilibili.com/anime/5802/play#100643',
|
||||||
|
'md5': '3f721ad1e75030cc06faf73587cfec57',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '100643',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'CHAOS;CHILD',
|
||||||
|
'description': '如果你是神明,并且能够让妄想成为现实。那你会进行怎么样的妄想?是淫靡的世界?独裁社会?毁灭性的制裁?还是……2015年,涩谷。从6年前发生的大灾害“涩谷地震”之后复兴了的这个街区里新设立的私立高中...',
|
||||||
|
},
|
||||||
|
'skip': 'Geo-restricted to China',
|
||||||
|
}]
|
||||||
|
|
||||||
_APP_KEY = '6f90a59ac58a4123'
|
_APP_KEY = '84956560bc028eb7'
|
||||||
_BILIBILI_KEY = '0bfd84cc3940035173f35e6777508326'
|
_BILIBILI_KEY = '94aba54af9065f71de72f5508f1cd42e'
|
||||||
|
|
||||||
|
def _report_error(self, result):
|
||||||
|
if 'message' in result:
|
||||||
|
raise ExtractorError('%s said: %s' % (self.IE_NAME, result['message']), expected=True)
|
||||||
|
elif 'code' in result:
|
||||||
|
raise ExtractorError('%s returns error %d' % (self.IE_NAME, result['code']), expected=True)
|
||||||
|
else:
|
||||||
|
raise ExtractorError('Can\'t extract Bangumi episode ID')
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
url, smuggled_data = unsmuggle_url(url, {})
|
||||||
|
|
||||||
|
mobj = re.match(self._VALID_URL, url)
|
||||||
|
video_id = mobj.group('id')
|
||||||
|
anime_id = mobj.group('anime_id')
|
||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
if 'anime/v' not in url:
|
if 'anime/' not in url:
|
||||||
cid = compat_parse_qs(self._search_regex(
|
cid = compat_parse_qs(self._search_regex(
|
||||||
[r'EmbedPlayer\([^)]+,\s*"([^"]+)"\)',
|
[r'EmbedPlayer\([^)]+,\s*"([^"]+)"\)',
|
||||||
r'<iframe[^>]+src="https://secure\.bilibili\.com/secure,([^"]+)"'],
|
r'<iframe[^>]+src="https://secure\.bilibili\.com/secure,([^"]+)"'],
|
||||||
webpage, 'player parameters'))['cid'][0]
|
webpage, 'player parameters'))['cid'][0]
|
||||||
else:
|
else:
|
||||||
|
if 'no_bangumi_tip' not in smuggled_data:
|
||||||
|
self.to_screen('Downloading episode %s. To download all videos in anime %s, re-run youtube-dl with %s' % (
|
||||||
|
video_id, anime_id, compat_urlparse.urljoin(url, '//bangumi.bilibili.com/anime/%s' % anime_id)))
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||||
|
}
|
||||||
|
headers.update(self.geo_verification_headers())
|
||||||
|
|
||||||
js = self._download_json(
|
js = self._download_json(
|
||||||
'http://bangumi.bilibili.com/web_api/get_source', video_id,
|
'http://bangumi.bilibili.com/web_api/get_source', video_id,
|
||||||
data=urlencode_postdata({'episode_id': video_id}),
|
data=urlencode_postdata({'episode_id': video_id}),
|
||||||
headers={'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'})
|
headers=headers)
|
||||||
|
if 'result' not in js:
|
||||||
|
self._report_error(js)
|
||||||
cid = js['result']['cid']
|
cid = js['result']['cid']
|
||||||
|
|
||||||
payload = 'appkey=%s&cid=%s&otype=json&quality=2&type=mp4' % (self._APP_KEY, cid)
|
payload = 'appkey=%s&cid=%s&otype=json&quality=2&type=mp4' % (self._APP_KEY, cid)
|
||||||
@ -58,7 +102,11 @@ class BiliBiliIE(InfoExtractor):
|
|||||||
|
|
||||||
video_info = self._download_json(
|
video_info = self._download_json(
|
||||||
'http://interface.bilibili.com/playurl?%s&sign=%s' % (payload, sign),
|
'http://interface.bilibili.com/playurl?%s&sign=%s' % (payload, sign),
|
||||||
video_id, note='Downloading video info page')
|
video_id, note='Downloading video info page',
|
||||||
|
headers=self.geo_verification_headers())
|
||||||
|
|
||||||
|
if 'durl' not in video_info:
|
||||||
|
self._report_error(video_info)
|
||||||
|
|
||||||
entries = []
|
entries = []
|
||||||
|
|
||||||
@ -85,7 +133,7 @@ class BiliBiliIE(InfoExtractor):
|
|||||||
title = self._html_search_regex('<h1[^>]+title="([^"]+)">', webpage, 'title')
|
title = self._html_search_regex('<h1[^>]+title="([^"]+)">', 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', fatal=False))
|
r'<time[^>]+datetime="([^"]+)"', webpage, 'upload time', default=None))
|
||||||
thumbnail = self._html_search_meta(['og:image', 'thumbnailUrl'], webpage)
|
thumbnail = self._html_search_meta(['og:image', 'thumbnailUrl'], webpage)
|
||||||
|
|
||||||
# TODO 'view_count' requires deobfuscating Javascript
|
# TODO 'view_count' requires deobfuscating Javascript
|
||||||
@ -99,7 +147,7 @@ class BiliBiliIE(InfoExtractor):
|
|||||||
}
|
}
|
||||||
|
|
||||||
uploader_mobj = re.search(
|
uploader_mobj = re.search(
|
||||||
r'<a[^>]+href="https?://space\.bilibili\.com/(?P<id>\d+)"[^>]+title="(?P<name>[^"]+)"',
|
r'<a[^>]+href="(?:https?:)?//space\.bilibili\.com/(?P<id>\d+)"[^>]+title="(?P<name>[^"]+)"',
|
||||||
webpage)
|
webpage)
|
||||||
if uploader_mobj:
|
if uploader_mobj:
|
||||||
info.update({
|
info.update({
|
||||||
@ -123,3 +171,70 @@ class BiliBiliIE(InfoExtractor):
|
|||||||
'description': description,
|
'description': description,
|
||||||
'entries': entries,
|
'entries': entries,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BiliBiliBangumiIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://bangumi\.bilibili\.com/anime/(?P<id>\d+)'
|
||||||
|
|
||||||
|
IE_NAME = 'bangumi.bilibili.com'
|
||||||
|
IE_DESC = 'BiliBili番剧'
|
||||||
|
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'http://bangumi.bilibili.com/anime/1869',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1869',
|
||||||
|
'title': '混沌武士',
|
||||||
|
'description': 'md5:6a9622b911565794c11f25f81d6a97d2',
|
||||||
|
},
|
||||||
|
'playlist_count': 26,
|
||||||
|
}, {
|
||||||
|
'url': 'http://bangumi.bilibili.com/anime/1869',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1869',
|
||||||
|
'title': '混沌武士',
|
||||||
|
'description': 'md5:6a9622b911565794c11f25f81d6a97d2',
|
||||||
|
},
|
||||||
|
'playlist': [{
|
||||||
|
'md5': '91da8621454dd58316851c27c68b0c13',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '40062',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': '混沌武士',
|
||||||
|
'description': '故事发生在日本的江户时代。风是一个小酒馆的打工女。一日,酒馆里来了一群恶霸,虽然他们的举动令风十分不满,但是毕竟风只是一届女流,无法对他们采取什么行动,只能在心里嘟哝。这时,酒家里又进来了个“不良份子...',
|
||||||
|
'timestamp': 1414538739,
|
||||||
|
'upload_date': '20141028',
|
||||||
|
'episode': '疾风怒涛 Tempestuous Temperaments',
|
||||||
|
'episode_number': 1,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
'params': {
|
||||||
|
'playlist_items': '1',
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def suitable(cls, url):
|
||||||
|
return False if BiliBiliIE.suitable(url) else super(BiliBiliBangumiIE, cls).suitable(url)
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
bangumi_id = self._match_id(url)
|
||||||
|
|
||||||
|
# Sometimes this API returns a JSONP response
|
||||||
|
season_info = self._download_json(
|
||||||
|
'http://bangumi.bilibili.com/jsonp/seasoninfo/%s.ver' % bangumi_id,
|
||||||
|
bangumi_id, transform_source=strip_jsonp)['result']
|
||||||
|
|
||||||
|
entries = [{
|
||||||
|
'_type': 'url_transparent',
|
||||||
|
'url': smuggle_url(episode['webplay_url'], {'no_bangumi_tip': 1}),
|
||||||
|
'ie_key': BiliBiliIE.ie_key(),
|
||||||
|
'timestamp': parse_iso8601(episode.get('update_time'), delimiter=' '),
|
||||||
|
'episode': episode.get('index_title'),
|
||||||
|
'episode_number': int_or_none(episode.get('index')),
|
||||||
|
} for episode in season_info['episodes']]
|
||||||
|
|
||||||
|
entries = sorted(entries, key=lambda entry: entry.get('episode_number'))
|
||||||
|
|
||||||
|
return self.playlist_result(
|
||||||
|
entries, bangumi_id,
|
||||||
|
season_info.get('bangumi_title'), season_info.get('evaluate'))
|
||||||
|
@ -19,7 +19,7 @@ class BioBioChileTVIE(InfoExtractor):
|
|||||||
'id': 'sobre-camaras-y-camarillas-parlamentarias',
|
'id': 'sobre-camaras-y-camarillas-parlamentarias',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Sobre Cámaras y camarillas parlamentarias',
|
'title': 'Sobre Cámaras y camarillas parlamentarias',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'uploader': 'Fernando Atria',
|
'uploader': 'Fernando Atria',
|
||||||
},
|
},
|
||||||
'skip': 'URL expired and redirected to http://www.biobiochile.cl/portada/bbtv/index.html',
|
'skip': 'URL expired and redirected to http://www.biobiochile.cl/portada/bbtv/index.html',
|
||||||
@ -31,7 +31,7 @@ class BioBioChileTVIE(InfoExtractor):
|
|||||||
'id': 'natalia-valdebenito-repasa-a-diputado-hasbun-paso-a-la-categoria-de-hablar-brutalidades',
|
'id': 'natalia-valdebenito-repasa-a-diputado-hasbun-paso-a-la-categoria-de-hablar-brutalidades',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Natalia Valdebenito repasa a diputado Hasbún: Pasó a la categoría de hablar brutalidades',
|
'title': 'Natalia Valdebenito repasa a diputado Hasbún: Pasó a la categoría de hablar brutalidades',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'uploader': 'Piangella Obrador',
|
'uploader': 'Piangella Obrador',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import json
|
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..compat import compat_str
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_age_limit,
|
parse_age_limit,
|
||||||
@ -11,7 +11,7 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class BreakIE(InfoExtractor):
|
class BreakIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?break\.com/video/(?:[^/]+/)*.+-(?P<id>\d+)'
|
_VALID_URL = r'https?://(?:www\.)?(?P<site>break|screenjunkies)\.com/video/(?P<display_id>[^/]+?)(?:-(?P<id>\d+))?(?:[/?#&]|$)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.break.com/video/when-girls-act-like-guys-2468056',
|
'url': 'http://www.break.com/video/when-girls-act-like-guys-2468056',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@ -20,45 +20,124 @@ class BreakIE(InfoExtractor):
|
|||||||
'title': 'When Girls Act Like D-Bags',
|
'title': 'When Girls Act Like D-Bags',
|
||||||
'age_limit': 13,
|
'age_limit': 13,
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.screenjunkies.com/video/best-quentin-tarantino-movie-2841915',
|
||||||
|
'md5': '5c2b686bec3d43de42bde9ec047536b0',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '2841915',
|
||||||
|
'display_id': 'best-quentin-tarantino-movie',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Best Quentin Tarantino Movie',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
|
'duration': 3671,
|
||||||
|
'age_limit': 13,
|
||||||
|
'tags': list,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.screenjunkies.com/video/honest-trailers-the-dark-knight',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '2348808',
|
||||||
|
'display_id': 'honest-trailers-the-dark-knight',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Honest Trailers - The Dark Knight',
|
||||||
|
'thumbnail': r're:^https?://.*\.(?:jpg|png)',
|
||||||
|
'age_limit': 10,
|
||||||
|
'tags': list,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
# requires subscription but worked around
|
||||||
|
'url': 'http://www.screenjunkies.com/video/knocking-dead-ep-1-the-show-so-far-3003285',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '3003285',
|
||||||
|
'display_id': 'knocking-dead-ep-1-the-show-so-far',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'State of The Dead Recap: Knocking Dead Pilot',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
|
'duration': 3307,
|
||||||
|
'age_limit': 13,
|
||||||
|
'tags': list,
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.break.com/video/ugc/baby-flex-2773063',
|
'url': 'http://www.break.com/video/ugc/baby-flex-2773063',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
_DEFAULT_BITRATES = (48, 150, 320, 496, 864, 2240, 3264)
|
||||||
video_id = self._match_id(url)
|
|
||||||
webpage = self._download_webpage(
|
|
||||||
'http://www.break.com/embed/%s' % video_id, video_id)
|
|
||||||
info = json.loads(self._search_regex(
|
|
||||||
r'var embedVars = ({.*})\s*?</script>',
|
|
||||||
webpage, 'info json', flags=re.DOTALL))
|
|
||||||
|
|
||||||
youtube_id = info.get('youtubeId')
|
def _real_extract(self, url):
|
||||||
|
site, display_id, video_id = re.match(self._VALID_URL, url).groups()
|
||||||
|
|
||||||
|
if not video_id:
|
||||||
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
video_id = self._search_regex(
|
||||||
|
(r'src=["\']/embed/(\d+)', r'data-video-content-id=["\'](\d+)'),
|
||||||
|
webpage, 'video id')
|
||||||
|
|
||||||
|
webpage = self._download_webpage(
|
||||||
|
'http://www.%s.com/embed/%s' % (site, video_id),
|
||||||
|
display_id, 'Downloading video embed page')
|
||||||
|
embed_vars = self._parse_json(
|
||||||
|
self._search_regex(
|
||||||
|
r'(?s)embedVars\s*=\s*({.+?})\s*</script>', webpage, 'embed vars'),
|
||||||
|
display_id)
|
||||||
|
|
||||||
|
youtube_id = embed_vars.get('youtubeId')
|
||||||
if youtube_id:
|
if youtube_id:
|
||||||
return self.url_result(youtube_id, 'Youtube')
|
return self.url_result(youtube_id, 'Youtube')
|
||||||
|
|
||||||
formats = [{
|
title = embed_vars['contentName']
|
||||||
'url': media['uri'] + '?' + info['AuthToken'],
|
|
||||||
'tbr': media['bitRate'],
|
|
||||||
'width': media['width'],
|
|
||||||
'height': media['height'],
|
|
||||||
} for media in info['media'] if media.get('mediaPurpose') == 'play']
|
|
||||||
|
|
||||||
if not formats:
|
formats = []
|
||||||
|
bitrates = []
|
||||||
|
for f in embed_vars.get('media', []):
|
||||||
|
if not f.get('uri') or f.get('mediaPurpose') != 'play':
|
||||||
|
continue
|
||||||
|
bitrate = int_or_none(f.get('bitRate'))
|
||||||
|
if bitrate:
|
||||||
|
bitrates.append(bitrate)
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': info['videoUri']
|
'url': f['uri'],
|
||||||
|
'format_id': 'http-%d' % bitrate if bitrate else 'http',
|
||||||
|
'width': int_or_none(f.get('width')),
|
||||||
|
'height': int_or_none(f.get('height')),
|
||||||
|
'tbr': bitrate,
|
||||||
|
'format': 'mp4',
|
||||||
})
|
})
|
||||||
|
|
||||||
self._sort_formats(formats)
|
if not bitrates:
|
||||||
|
# When subscriptionLevel > 0, i.e. plus subscription is required
|
||||||
|
# media list will be empty. However, hds and hls uris are still
|
||||||
|
# available. We can grab them assuming bitrates to be default.
|
||||||
|
bitrates = self._DEFAULT_BITRATES
|
||||||
|
|
||||||
duration = int_or_none(info.get('videoLengthInSeconds'))
|
auth_token = embed_vars.get('AuthToken')
|
||||||
age_limit = parse_age_limit(info.get('audienceRating'))
|
|
||||||
|
def construct_manifest_url(base_url, ext):
|
||||||
|
pieces = [base_url]
|
||||||
|
pieces.extend([compat_str(b) for b in bitrates])
|
||||||
|
pieces.append('_kbps.mp4.%s?%s' % (ext, auth_token))
|
||||||
|
return ','.join(pieces)
|
||||||
|
|
||||||
|
if bitrates and auth_token:
|
||||||
|
hds_url = embed_vars.get('hdsUri')
|
||||||
|
if hds_url:
|
||||||
|
formats.extend(self._extract_f4m_formats(
|
||||||
|
construct_manifest_url(hds_url, 'f4m'),
|
||||||
|
display_id, f4m_id='hds', fatal=False))
|
||||||
|
hls_url = embed_vars.get('hlsUri')
|
||||||
|
if hls_url:
|
||||||
|
formats.extend(self._extract_m3u8_formats(
|
||||||
|
construct_manifest_url(hls_url, 'm3u8'),
|
||||||
|
display_id, 'mp4', entry_protocol='m3u8_native', m3u8_id='hls', fatal=False))
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': info['contentName'],
|
'display_id': display_id,
|
||||||
'thumbnail': info['thumbUri'],
|
'title': title,
|
||||||
'duration': duration,
|
'thumbnail': embed_vars.get('thumbUri'),
|
||||||
'age_limit': age_limit,
|
'duration': int_or_none(embed_vars.get('videoLengthInSeconds')) or None,
|
||||||
|
'age_limit': parse_age_limit(embed_vars.get('audienceRating')),
|
||||||
|
'tags': embed_vars.get('tags', '').split(','),
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,7 @@ class BrightcoveLegacyIE(InfoExtractor):
|
|||||||
|
|
||||||
params = {}
|
params = {}
|
||||||
|
|
||||||
playerID = find_param('playerID')
|
playerID = find_param('playerID') or find_param('playerId')
|
||||||
if playerID is None:
|
if playerID is None:
|
||||||
raise ExtractorError('Cannot find player ID')
|
raise ExtractorError('Cannot find player ID')
|
||||||
params['playerID'] = playerID
|
params['playerID'] = playerID
|
||||||
@ -204,7 +204,7 @@ class BrightcoveLegacyIE(InfoExtractor):
|
|||||||
# // build Brightcove <object /> XML
|
# // build Brightcove <object /> XML
|
||||||
# }
|
# }
|
||||||
m = re.search(
|
m = re.search(
|
||||||
r'''(?x)customBC.\createVideo\(
|
r'''(?x)customBC\.createVideo\(
|
||||||
.*? # skipping width and height
|
.*? # skipping width and height
|
||||||
["\'](?P<playerID>\d+)["\']\s*,\s* # playerID
|
["\'](?P<playerID>\d+)["\']\s*,\s* # playerID
|
||||||
["\'](?P<playerKey>AQ[^"\']{48})[^"\']*["\']\s*,\s* # playerKey begins with AQ and is 50 characters
|
["\'](?P<playerKey>AQ[^"\']{48})[^"\']*["\']\s*,\s* # playerKey begins with AQ and is 50 characters
|
||||||
|
@ -16,7 +16,7 @@ class BYUtvIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Season 5 Episode 5',
|
'title': 'Season 5 Episode 5',
|
||||||
'description': 'md5:e07269172baff037f8e8bf9956bc9747',
|
'description': 'md5:e07269172baff037f8e8bf9956bc9747',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 1486.486,
|
'duration': 1486.486,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
|
@ -26,7 +26,7 @@ class CamdemyIE(InfoExtractor):
|
|||||||
'id': '5181',
|
'id': '5181',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Ch1-1 Introduction, Signals (02-23-2012)',
|
'title': 'Ch1-1 Introduction, Signals (02-23-2012)',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'creator': 'ss11spring',
|
'creator': 'ss11spring',
|
||||||
'duration': 1591,
|
'duration': 1591,
|
||||||
'upload_date': '20130114',
|
'upload_date': '20130114',
|
||||||
@ -41,7 +41,7 @@ class CamdemyIE(InfoExtractor):
|
|||||||
'id': '13885',
|
'id': '13885',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'EverCam + Camdemy QuickStart',
|
'title': 'EverCam + Camdemy QuickStart',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'description': 'md5:2a9f989c2b153a2342acee579c6e7db6',
|
'description': 'md5:2a9f989c2b153a2342acee579c6e7db6',
|
||||||
'creator': 'evercam',
|
'creator': 'evercam',
|
||||||
'duration': 318,
|
'duration': 318,
|
||||||
|
@ -27,6 +27,7 @@ class CanalplusIE(InfoExtractor):
|
|||||||
(?:www\.)?d8\.tv|
|
(?:www\.)?d8\.tv|
|
||||||
(?:www\.)?c8\.fr|
|
(?:www\.)?c8\.fr|
|
||||||
(?:www\.)?d17\.tv|
|
(?:www\.)?d17\.tv|
|
||||||
|
(?:(?:football|www)\.)?cstar\.fr|
|
||||||
(?:www\.)?itele\.fr
|
(?:www\.)?itele\.fr
|
||||||
)/(?:(?:[^/]+/)*(?P<display_id>[^/?#&]+))?(?:\?.*\bvid=(?P<vid>\d+))?|
|
)/(?:(?:[^/]+/)*(?P<display_id>[^/?#&]+))?(?:\?.*\bvid=(?P<vid>\d+))?|
|
||||||
player\.canalplus\.fr/#/(?P<id>\d+)
|
player\.canalplus\.fr/#/(?P<id>\d+)
|
||||||
@ -40,6 +41,7 @@ class CanalplusIE(InfoExtractor):
|
|||||||
'd8': 'd8',
|
'd8': 'd8',
|
||||||
'c8': 'd8',
|
'c8': 'd8',
|
||||||
'd17': 'd17',
|
'd17': 'd17',
|
||||||
|
'cstar': 'd17',
|
||||||
'itele': 'itele',
|
'itele': 'itele',
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +88,19 @@ class CanalplusIE(InfoExtractor):
|
|||||||
'description': 'Chaque matin du lundi au vendredi, Michaël Darmon reçoit un invité politique à 8h25.',
|
'description': 'Chaque matin du lundi au vendredi, Michaël Darmon reçoit un invité politique à 8h25.',
|
||||||
'upload_date': '20161014',
|
'upload_date': '20161014',
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'http://football.cstar.fr/cstar-minisite-foot/pid7566-feminines-videos.html?vid=1416769',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1416769',
|
||||||
|
'display_id': 'pid7566-feminines-videos',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'France - Albanie : les temps forts de la soirée - 20/09/2016',
|
||||||
|
'description': 'md5:c3f30f2aaac294c1c969b3294de6904e',
|
||||||
|
'upload_date': '20160921',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://m.canalplus.fr/?vid=1398231',
|
'url': 'http://m.canalplus.fr/?vid=1398231',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
@ -107,7 +122,7 @@ class CanalplusIE(InfoExtractor):
|
|||||||
[r'<canal:player[^>]+?videoId=(["\'])(?P<id>\d+)',
|
[r'<canal:player[^>]+?videoId=(["\'])(?P<id>\d+)',
|
||||||
r'id=["\']canal_video_player(?P<id>\d+)',
|
r'id=["\']canal_video_player(?P<id>\d+)',
|
||||||
r'data-video=["\'](?P<id>\d+)'],
|
r'data-video=["\'](?P<id>\d+)'],
|
||||||
webpage, 'video id', group='id')
|
webpage, 'video id', default=mobj.group('vid'), group='id')
|
||||||
|
|
||||||
info_url = self._VIDEO_INFO_TEMPLATE % (site_id, video_id)
|
info_url = self._VIDEO_INFO_TEMPLATE % (site_id, video_id)
|
||||||
video_data = self._download_json(info_url, video_id, 'Downloading video JSON')
|
video_data = self._download_json(info_url, video_id, 'Downloading video JSON')
|
||||||
|
@ -17,7 +17,7 @@ class CanvasIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'De afspraak veilt voor de Warmste Week',
|
'title': 'De afspraak veilt voor de Warmste Week',
|
||||||
'description': 'md5:24cb860c320dc2be7358e0e5aa317ba6',
|
'description': 'md5:24cb860c320dc2be7358e0e5aa317ba6',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 49.02,
|
'duration': 49.02,
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
@ -29,7 +29,7 @@ class CanvasIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Pieter 0167',
|
'title': 'Pieter 0167',
|
||||||
'description': 'md5:943cd30f48a5d29ba02c3a104dc4ec4e',
|
'description': 'md5:943cd30f48a5d29ba02c3a104dc4ec4e',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 2553.08,
|
'duration': 2553.08,
|
||||||
'subtitles': {
|
'subtitles': {
|
||||||
'nl': [{
|
'nl': [{
|
||||||
@ -48,7 +48,7 @@ class CanvasIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Herbekijk Sorry voor alles',
|
'title': 'Herbekijk Sorry voor alles',
|
||||||
'description': 'md5:8bb2805df8164e5eb95d6a7a29dc0dd3',
|
'description': 'md5:8bb2805df8164e5eb95d6a7a29dc0dd3',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 3788.06,
|
'duration': 3788.06,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
|
@ -21,7 +21,7 @@ class CarambaTVIE(InfoExtractor):
|
|||||||
'id': '191910501',
|
'id': '191910501',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': '[BadComedian] - Разборка в Маниле (Абсолютный обзор)',
|
'title': '[BadComedian] - Разборка в Маниле (Абсолютный обзор)',
|
||||||
'thumbnail': 're:^https?://.*\.jpg',
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
'duration': 2678.31,
|
'duration': 2678.31,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
@ -69,7 +69,7 @@ class CarambaTVPageIE(InfoExtractor):
|
|||||||
'id': '475222',
|
'id': '475222',
|
||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
'title': '[BadComedian] - Разборка в Маниле (Абсолютный обзор)',
|
'title': '[BadComedian] - Разборка в Маниле (Абсолютный обзор)',
|
||||||
'thumbnail': 're:^https?://.*\.jpg',
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
# duration reported by videomore is incorrect
|
# duration reported by videomore is incorrect
|
||||||
'duration': int,
|
'duration': int,
|
||||||
},
|
},
|
||||||
|
@ -90,19 +90,21 @@ class CBCIE(InfoExtractor):
|
|||||||
},
|
},
|
||||||
}],
|
}],
|
||||||
'skip': 'Geo-restricted to Canada',
|
'skip': 'Geo-restricted to Canada',
|
||||||
|
}, {
|
||||||
|
# multiple CBC.APP.Caffeine.initInstance(...)
|
||||||
|
'url': 'http://www.cbc.ca/news/canada/calgary/dog-indoor-exercise-winter-1.3928238',
|
||||||
|
'info_dict': {
|
||||||
|
'title': 'Keep Rover active during the deep freeze with doggie pushups and other fun indoor tasks',
|
||||||
|
'id': 'dog-indoor-exercise-winter-1.3928238',
|
||||||
|
},
|
||||||
|
'playlist_mincount': 6,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def suitable(cls, url):
|
def suitable(cls, url):
|
||||||
return False if CBCPlayerIE.suitable(url) else super(CBCIE, cls).suitable(url)
|
return False if CBCPlayerIE.suitable(url) else super(CBCIE, cls).suitable(url)
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _extract_player_init(self, player_init, display_id):
|
||||||
display_id = self._match_id(url)
|
|
||||||
webpage = self._download_webpage(url, display_id)
|
|
||||||
player_init = self._search_regex(
|
|
||||||
r'CBC\.APP\.Caffeine\.initInstance\(({.+?})\);', webpage, 'player init',
|
|
||||||
default=None)
|
|
||||||
if player_init:
|
|
||||||
player_info = self._parse_json(player_init, display_id, js_to_json)
|
player_info = self._parse_json(player_init, display_id, js_to_json)
|
||||||
media_id = player_info.get('mediaId')
|
media_id = player_info.get('mediaId')
|
||||||
if not media_id:
|
if not media_id:
|
||||||
@ -117,9 +119,20 @@ class CBCIE(InfoExtractor):
|
|||||||
'http://feed.theplatform.com/f/h9dtGB/punlNGjMlc1F?fields=id&byContent=byReleases%3DbyId%253D' + clip_id,
|
'http://feed.theplatform.com/f/h9dtGB/punlNGjMlc1F?fields=id&byContent=byReleases%3DbyId%253D' + clip_id,
|
||||||
clip_id)['entries'][0]['id'].split('/')[-1]
|
clip_id)['entries'][0]['id'].split('/')[-1]
|
||||||
return self.url_result('cbcplayer:%s' % media_id, 'CBCPlayer', media_id)
|
return self.url_result('cbcplayer:%s' % media_id, 'CBCPlayer', media_id)
|
||||||
else:
|
|
||||||
entries = [self.url_result('cbcplayer:%s' % media_id, 'CBCPlayer', media_id) for media_id in re.findall(r'<iframe[^>]+src="[^"]+?mediaId=(\d+)"', webpage)]
|
def _real_extract(self, url):
|
||||||
return self.playlist_result(entries)
|
display_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
entries = [
|
||||||
|
self._extract_player_init(player_init, display_id)
|
||||||
|
for player_init in re.findall(r'CBC\.APP\.Caffeine\.initInstance\(({.+?})\);', webpage)]
|
||||||
|
entries.extend([
|
||||||
|
self.url_result('cbcplayer:%s' % media_id, 'CBCPlayer', media_id)
|
||||||
|
for media_id in re.findall(r'<iframe[^>]+src="[^"]+?mediaId=(\d+)"', webpage)])
|
||||||
|
return self.playlist_result(
|
||||||
|
entries, display_id,
|
||||||
|
self._og_search_title(webpage, fatal=False),
|
||||||
|
self._og_search_description(webpage))
|
||||||
|
|
||||||
|
|
||||||
class CBCPlayerIE(InfoExtractor):
|
class CBCPlayerIE(InfoExtractor):
|
||||||
@ -283,6 +296,12 @@ class CBCWatchVideoIE(CBCWatchBaseIE):
|
|||||||
formats = self._extract_m3u8_formats(re.sub(r'/([^/]+)/[^/?]+\.m3u8', r'/\1/\1.m3u8', m3u8_url), video_id, 'mp4', fatal=False)
|
formats = self._extract_m3u8_formats(re.sub(r'/([^/]+)/[^/?]+\.m3u8', r'/\1/\1.m3u8', m3u8_url), video_id, 'mp4', fatal=False)
|
||||||
if len(formats) < 2:
|
if len(formats) < 2:
|
||||||
formats = self._extract_m3u8_formats(m3u8_url, video_id, 'mp4')
|
formats = self._extract_m3u8_formats(m3u8_url, video_id, 'mp4')
|
||||||
|
for f in formats:
|
||||||
|
format_id = f.get('format_id')
|
||||||
|
if format_id.startswith('AAC'):
|
||||||
|
f['acodec'] = 'aac'
|
||||||
|
elif format_id.startswith('AC3'):
|
||||||
|
f['acodec'] = 'ac-3'
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
info = {
|
info = {
|
||||||
|
@ -39,7 +39,7 @@ class CBSNewsIE(CBSIE):
|
|||||||
'upload_date': '20140404',
|
'upload_date': '20140404',
|
||||||
'timestamp': 1396650660,
|
'timestamp': 1396650660,
|
||||||
'uploader': 'CBSI-NEW',
|
'uploader': 'CBSI-NEW',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 205,
|
'duration': 205,
|
||||||
'subtitles': {
|
'subtitles': {
|
||||||
'en': [{
|
'en': [{
|
||||||
|
@ -19,7 +19,7 @@ class CCCIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Introduction to Processor Design',
|
'title': 'Introduction to Processor Design',
|
||||||
'description': 'md5:df55f6d073d4ceae55aae6f2fd98a0ac',
|
'description': 'md5:df55f6d073d4ceae55aae6f2fd98a0ac',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'upload_date': '20131228',
|
'upload_date': '20131228',
|
||||||
'timestamp': 1388188800,
|
'timestamp': 1388188800,
|
||||||
'duration': 3710,
|
'duration': 3710,
|
||||||
@ -32,7 +32,7 @@ class CCCIE(InfoExtractor):
|
|||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
display_id = self._match_id(url)
|
display_id = self._match_id(url)
|
||||||
webpage = self._download_webpage(url, display_id)
|
webpage = self._download_webpage(url, display_id)
|
||||||
event_id = self._search_regex("data-id='(\d+)'", webpage, 'event id')
|
event_id = self._search_regex(r"data-id='(\d+)'", webpage, 'event id')
|
||||||
event_data = self._download_json('https://media.ccc.de/public/events/%s' % event_id, event_id)
|
event_data = self._download_json('https://media.ccc.de/public/events/%s' % event_id, event_id)
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
|
@ -14,8 +14,9 @@ from ..utils import (
|
|||||||
|
|
||||||
class CCTVIE(InfoExtractor):
|
class CCTVIE(InfoExtractor):
|
||||||
IE_DESC = '央视网'
|
IE_DESC = '央视网'
|
||||||
_VALID_URL = r'https?://(?:[^/]+)\.(?:cntv|cctv)\.(?:com|cn)/(?:[^/]+/)*?(?P<id>[^/?#&]+?)(?:/index)?(?:\.s?html|[?#&]|$)'
|
_VALID_URL = r'https?://(?:(?:[^/]+)\.(?:cntv|cctv)\.(?:com|cn)|(?:www\.)?ncpa-classic\.com)/(?:[^/]+/)*?(?P<id>[^/?#&]+?)(?:/index)?(?:\.s?html|[?#&]|$)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
|
# fo.addVariable("videoCenterId","id")
|
||||||
'url': 'http://sports.cntv.cn/2016/02/12/ARTIaBRxv4rTT1yWf1frW2wi160212.shtml',
|
'url': 'http://sports.cntv.cn/2016/02/12/ARTIaBRxv4rTT1yWf1frW2wi160212.shtml',
|
||||||
'md5': 'd61ec00a493e09da810bf406a078f691',
|
'md5': 'd61ec00a493e09da810bf406a078f691',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@ -29,6 +30,7 @@ class CCTVIE(InfoExtractor):
|
|||||||
'upload_date': '20160212',
|
'upload_date': '20160212',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
|
# var guid = "id"
|
||||||
'url': 'http://tv.cctv.com/2016/02/05/VIDEUS7apq3lKrHG9Dncm03B160205.shtml',
|
'url': 'http://tv.cctv.com/2016/02/05/VIDEUS7apq3lKrHG9Dncm03B160205.shtml',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'efc5d49e5b3b4ab2b34f3a502b73d3ae',
|
'id': 'efc5d49e5b3b4ab2b34f3a502b73d3ae',
|
||||||
@ -44,6 +46,7 @@ class CCTVIE(InfoExtractor):
|
|||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
|
# changePlayer('id')
|
||||||
'url': 'http://english.cntv.cn/special/four_comprehensives/index.shtml',
|
'url': 'http://english.cntv.cn/special/four_comprehensives/index.shtml',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '4bb9bb4db7a6471ba85fdeda5af0381e',
|
'id': '4bb9bb4db7a6471ba85fdeda5af0381e',
|
||||||
@ -59,6 +62,7 @@ class CCTVIE(InfoExtractor):
|
|||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
|
# loadvideo('id')
|
||||||
'url': 'http://cctv.cntv.cn/lm/tvseries_russian/yilugesanghua/index.shtml',
|
'url': 'http://cctv.cntv.cn/lm/tvseries_russian/yilugesanghua/index.shtml',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'b15f009ff45c43968b9af583fc2e04b2',
|
'id': 'b15f009ff45c43968b9af583fc2e04b2',
|
||||||
@ -73,6 +77,37 @@ class CCTVIE(InfoExtractor):
|
|||||||
'params': {
|
'params': {
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
# var initMyAray = 'id'
|
||||||
|
'url': 'http://www.ncpa-classic.com/2013/05/22/VIDE1369219508996867.shtml',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'a194cfa7f18c426b823d876668325946',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': '小泽征尔音乐塾 音乐梦想无国界',
|
||||||
|
'duration': 2173,
|
||||||
|
'timestamp': 1369248264,
|
||||||
|
'upload_date': '20130522',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
# var ids = ["id"]
|
||||||
|
'url': 'http://www.ncpa-classic.com/clt/more/416/index.shtml',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'a8606119a4884588a79d81c02abecc16',
|
||||||
|
'ext': 'mp3',
|
||||||
|
'title': '来自维也纳的新年贺礼',
|
||||||
|
'description': 'md5:f13764ae8dd484e84dd4b39d5bcba2a7',
|
||||||
|
'duration': 1578,
|
||||||
|
'uploader': 'djy',
|
||||||
|
'timestamp': 1482942419,
|
||||||
|
'upload_date': '20161228',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
'expected_warnings': ['Failed to download m3u8 information'],
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://ent.cntv.cn/2016/01/18/ARTIjprSSJH8DryTVr5Bx8Wb160118.shtml',
|
'url': 'http://ent.cntv.cn/2016/01/18/ARTIjprSSJH8DryTVr5Bx8Wb160118.shtml',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
@ -87,7 +122,7 @@ class CCTVIE(InfoExtractor):
|
|||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://tv.cntv.cn/video/C39296/95cfac44cabd3ddc4a9438780a4e5c44',
|
'url': 'http://tv.cntv.cn/video/C39296/95cfac44cabd3ddc4a9438780a4e5c44',
|
||||||
'only_matching': True
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
@ -97,8 +132,10 @@ class CCTVIE(InfoExtractor):
|
|||||||
video_id = self._search_regex(
|
video_id = self._search_regex(
|
||||||
[r'var\s+guid\s*=\s*["\']([\da-fA-F]+)',
|
[r'var\s+guid\s*=\s*["\']([\da-fA-F]+)',
|
||||||
r'videoCenterId["\']\s*,\s*["\']([\da-fA-F]+)',
|
r'videoCenterId["\']\s*,\s*["\']([\da-fA-F]+)',
|
||||||
r'"changePlayer\s*\(\s*["\']([\da-fA-F]+)',
|
r'changePlayer\s*\(\s*["\']([\da-fA-F]+)',
|
||||||
r'"load[Vv]ideo\s*\(\s*["\']([\da-fA-F]+)'],
|
r'load[Vv]ideo\s*\(\s*["\']([\da-fA-F]+)',
|
||||||
|
r'var\s+initMyAray\s*=\s*["\']([\da-fA-F]+)',
|
||||||
|
r'var\s+ids\s*=\s*\[["\']([\da-fA-F]+)'],
|
||||||
webpage, 'video id')
|
webpage, 'video id')
|
||||||
|
|
||||||
data = self._download_json(
|
data = self._download_json(
|
||||||
@ -138,7 +175,8 @@ class CCTVIE(InfoExtractor):
|
|||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
uploader = data.get('editer_name')
|
uploader = data.get('editer_name')
|
||||||
description = self._html_search_meta('description', webpage)
|
description = self._html_search_meta(
|
||||||
|
'description', webpage, default=None)
|
||||||
timestamp = unified_timestamp(data.get('f_pgmtime'))
|
timestamp = unified_timestamp(data.get('f_pgmtime'))
|
||||||
duration = float_or_none(try_get(video, lambda x: x['totalLength']))
|
duration = float_or_none(try_get(video, lambda x: x['totalLength']))
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ class CDAIE(InfoExtractor):
|
|||||||
'height': 720,
|
'height': 720,
|
||||||
'title': 'Oto dlaczego przed zakrętem należy zwolnić.',
|
'title': 'Oto dlaczego przed zakrętem należy zwolnić.',
|
||||||
'description': 'md5:269ccd135d550da90d1662651fcb9772',
|
'description': 'md5:269ccd135d550da90d1662651fcb9772',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'average_rating': float,
|
'average_rating': float,
|
||||||
'duration': 39
|
'duration': 39
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ class CDAIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Lądowanie na lotnisku na Maderze',
|
'title': 'Lądowanie na lotnisku na Maderze',
|
||||||
'description': 'md5:60d76b71186dcce4e0ba6d4bbdb13e1a',
|
'description': 'md5:60d76b71186dcce4e0ba6d4bbdb13e1a',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'uploader': 'crash404',
|
'uploader': 'crash404',
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
'average_rating': float,
|
'average_rating': float,
|
||||||
|
@ -25,7 +25,7 @@ class CeskaTelevizeIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Hyde Park Civilizace',
|
'title': 'Hyde Park Civilizace',
|
||||||
'description': 'md5:fe93f6eda372d150759d11644ebbfb4a',
|
'description': 'md5:fe93f6eda372d150759d11644ebbfb4a',
|
||||||
'thumbnail': 're:^https?://.*\.jpg',
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
'duration': 3350,
|
'duration': 3350,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
@ -39,7 +39,7 @@ class CeskaTelevizeIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Hyde Park Civilizace: Bonus 01 - En',
|
'title': 'Hyde Park Civilizace: Bonus 01 - En',
|
||||||
'description': 'English Subtittles',
|
'description': 'English Subtittles',
|
||||||
'thumbnail': 're:^https?://.*\.jpg',
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
'duration': 81.3,
|
'duration': 81.3,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
@ -52,7 +52,7 @@ class CeskaTelevizeIE(InfoExtractor):
|
|||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 402,
|
'id': 402,
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 're:^ČT Sport \d{4}-\d{2}-\d{2} \d{2}:\d{2}$',
|
'title': r're:^ČT Sport \d{4}-\d{2}-\d{2} \d{2}:\d{2}$',
|
||||||
'is_live': True,
|
'is_live': True,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
@ -80,7 +80,7 @@ class CeskaTelevizeIE(InfoExtractor):
|
|||||||
'id': '61924494877068022',
|
'id': '61924494877068022',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Queer: Bogotart (Queer)',
|
'title': 'Queer: Bogotart (Queer)',
|
||||||
'thumbnail': 're:^https?://.*\.jpg',
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
'duration': 1558.3,
|
'duration': 1558.3,
|
||||||
},
|
},
|
||||||
}],
|
}],
|
||||||
|
@ -31,7 +31,7 @@ class Channel9IE(InfoExtractor):
|
|||||||
'title': 'Developer Kick-Off Session: Stuff We Love',
|
'title': 'Developer Kick-Off Session: Stuff We Love',
|
||||||
'description': 'md5:c08d72240b7c87fcecafe2692f80e35f',
|
'description': 'md5:c08d72240b7c87fcecafe2692f80e35f',
|
||||||
'duration': 4576,
|
'duration': 4576,
|
||||||
'thumbnail': 're:http://.*\.jpg',
|
'thumbnail': r're:http://.*\.jpg',
|
||||||
'session_code': 'KOS002',
|
'session_code': 'KOS002',
|
||||||
'session_day': 'Day 1',
|
'session_day': 'Day 1',
|
||||||
'session_room': 'Arena 1A',
|
'session_room': 'Arena 1A',
|
||||||
@ -47,7 +47,7 @@ class Channel9IE(InfoExtractor):
|
|||||||
'title': 'Self-service BI with Power BI - nuclear testing',
|
'title': 'Self-service BI with Power BI - nuclear testing',
|
||||||
'description': 'md5:d1e6ecaafa7fb52a2cacdf9599829f5b',
|
'description': 'md5:d1e6ecaafa7fb52a2cacdf9599829f5b',
|
||||||
'duration': 1540,
|
'duration': 1540,
|
||||||
'thumbnail': 're:http://.*\.jpg',
|
'thumbnail': r're:http://.*\.jpg',
|
||||||
'authors': ['Mike Wilmot'],
|
'authors': ['Mike Wilmot'],
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
@ -59,7 +59,7 @@ class Channel9IE(InfoExtractor):
|
|||||||
'title': 'Ranges for the Standard Library',
|
'title': 'Ranges for the Standard Library',
|
||||||
'description': 'md5:2e6b4917677af3728c5f6d63784c4c5d',
|
'description': 'md5:2e6b4917677af3728c5f6d63784c4c5d',
|
||||||
'duration': 5646,
|
'duration': 5646,
|
||||||
'thumbnail': 're:http://.*\.jpg',
|
'thumbnail': r're:http://.*\.jpg',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
|
@ -13,7 +13,7 @@ class CharlieRoseIE(InfoExtractor):
|
|||||||
'id': '27996',
|
'id': '27996',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Remembering Zaha Hadid',
|
'title': 'Remembering Zaha Hadid',
|
||||||
'thumbnail': 're:^https?://.*\.jpg\?\d+',
|
'thumbnail': r're:^https?://.*\.jpg\?\d+',
|
||||||
'description': 'We revisit past conversations with Zaha Hadid, in memory of the world renowned Iraqi architect.',
|
'description': 'We revisit past conversations with Zaha Hadid, in memory of the world renowned Iraqi architect.',
|
||||||
'subtitles': {
|
'subtitles': {
|
||||||
'en': [{
|
'en': [{
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import ExtractorError
|
from ..utils import ExtractorError
|
||||||
|
|
||||||
@ -31,30 +33,35 @@ class ChaturbateIE(InfoExtractor):
|
|||||||
|
|
||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
m3u8_url = self._search_regex(
|
m3u8_formats = [(m.group('id').lower(), m.group('url')) for m in re.finditer(
|
||||||
r'src=(["\'])(?P<url>http.+?\.m3u8.*?)\1', webpage,
|
r'hlsSource(?P<id>.+?)\s*=\s*(?P<q>["\'])(?P<url>http.+?)(?P=q)', webpage)]
|
||||||
'playlist', default=None, group='url')
|
|
||||||
|
|
||||||
if not m3u8_url:
|
if not m3u8_formats:
|
||||||
error = self._search_regex(
|
error = self._search_regex(
|
||||||
[r'<span[^>]+class=(["\'])desc_span\1[^>]*>(?P<error>[^<]+)</span>',
|
[r'<span[^>]+class=(["\'])desc_span\1[^>]*>(?P<error>[^<]+)</span>',
|
||||||
r'<div[^>]+id=(["\'])defchat\1[^>]*>\s*<p><strong>(?P<error>[^<]+)<'],
|
r'<div[^>]+id=(["\'])defchat\1[^>]*>\s*<p><strong>(?P<error>[^<]+)<'],
|
||||||
webpage, 'error', group='error', default=None)
|
webpage, 'error', group='error', default=None)
|
||||||
if not error:
|
if not error:
|
||||||
if any(p not in webpage for p in (
|
if any(p in webpage for p in (
|
||||||
self._ROOM_OFFLINE, 'offline_tipping', 'tip_offline')):
|
self._ROOM_OFFLINE, 'offline_tipping', 'tip_offline')):
|
||||||
error = self._ROOM_OFFLINE
|
error = self._ROOM_OFFLINE
|
||||||
if error:
|
if error:
|
||||||
raise ExtractorError(error, expected=True)
|
raise ExtractorError(error, expected=True)
|
||||||
raise ExtractorError('Unable to find stream URL')
|
raise ExtractorError('Unable to find stream URL')
|
||||||
|
|
||||||
formats = self._extract_m3u8_formats(m3u8_url, video_id, ext='mp4')
|
formats = []
|
||||||
|
for m3u8_id, m3u8_url in m3u8_formats:
|
||||||
|
formats.extend(self._extract_m3u8_formats(
|
||||||
|
m3u8_url, video_id, ext='mp4',
|
||||||
|
# ffmpeg skips segments for fast m3u8
|
||||||
|
preference=-10 if m3u8_id == 'fast' else None,
|
||||||
|
m3u8_id=m3u8_id, fatal=False, live=True))
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': self._live_title(video_id),
|
'title': self._live_title(video_id),
|
||||||
'thumbnail': 'https://cdn-s.highwebmedia.com/uHK3McUtGCG3SMFcd4ZJsRv8/roomimage/%s.jpg' % video_id,
|
'thumbnail': 'https://roomimg.stream.highwebmedia.com/ri/%s.jpg' % video_id,
|
||||||
'age_limit': self._rta_search(webpage),
|
'age_limit': self._rta_search(webpage),
|
||||||
'is_live': True,
|
'is_live': True,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
|
@ -19,6 +19,7 @@ class ChirbitIE(InfoExtractor):
|
|||||||
'title': 'md5:f542ea253f5255240be4da375c6a5d7e',
|
'title': 'md5:f542ea253f5255240be4da375c6a5d7e',
|
||||||
'description': 'md5:f24a4e22a71763e32da5fed59e47c770',
|
'description': 'md5:f24a4e22a71763e32da5fed59e47c770',
|
||||||
'duration': 306,
|
'duration': 306,
|
||||||
|
'uploader': 'Gerryaudio',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
@ -54,6 +55,9 @@ class ChirbitIE(InfoExtractor):
|
|||||||
duration = parse_duration(self._search_regex(
|
duration = parse_duration(self._search_regex(
|
||||||
r'class=["\']c-length["\'][^>]*>([^<]+)',
|
r'class=["\']c-length["\'][^>]*>([^<]+)',
|
||||||
webpage, 'duration', fatal=False))
|
webpage, 'duration', fatal=False))
|
||||||
|
uploader = self._search_regex(
|
||||||
|
r'id=["\']chirbit-username["\'][^>]*>([^<]+)',
|
||||||
|
webpage, 'uploader', fatal=False)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': audio_id,
|
'id': audio_id,
|
||||||
@ -61,6 +65,7 @@ class ChirbitIE(InfoExtractor):
|
|||||||
'title': title,
|
'title': title,
|
||||||
'description': description,
|
'description': description,
|
||||||
'duration': duration,
|
'duration': duration,
|
||||||
|
'uploader': uploader,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class CliphunterIE(InfoExtractor):
|
|||||||
'id': '1012420',
|
'id': '1012420',
|
||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
'title': 'Fun Jynx Maze solo',
|
'title': 'Fun Jynx Maze solo',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'age_limit': 18,
|
'age_limit': 18,
|
||||||
},
|
},
|
||||||
'skip': 'Video gone',
|
'skip': 'Video gone',
|
||||||
@ -41,7 +41,7 @@ class CliphunterIE(InfoExtractor):
|
|||||||
'id': '2019449',
|
'id': '2019449',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'ShesNew - My booty girlfriend, Victoria Paradice\'s pussy filled with jizz',
|
'title': 'ShesNew - My booty girlfriend, Victoria Paradice\'s pussy filled with jizz',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'age_limit': 18,
|
'age_limit': 18,
|
||||||
},
|
},
|
||||||
}]
|
}]
|
||||||
|
@ -18,7 +18,7 @@ class ClipsyndicateIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Brick Briscoe',
|
'title': 'Brick Briscoe',
|
||||||
'duration': 612,
|
'duration': 612,
|
||||||
'thumbnail': 're:^https?://.+\.jpg',
|
'thumbnail': r're:^https?://.+\.jpg',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://chic.clipsyndicate.com/video/play/5844117/shark_attack',
|
'url': 'http://chic.clipsyndicate.com/video/play/5844117/shark_attack',
|
||||||
|
@ -19,7 +19,7 @@ class ClubicIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Clubic Week 2.0 : le FBI se lance dans la photo d\u0092identité',
|
'title': 'Clubic Week 2.0 : le FBI se lance dans la photo d\u0092identité',
|
||||||
'description': 're:Gueule de bois chez Nokia. Le constructeur a indiqué cette.*',
|
'description': 're:Gueule de bois chez Nokia. Le constructeur a indiqué cette.*',
|
||||||
'thumbnail': 're:^http://img\.clubic\.com/.*\.jpg$',
|
'thumbnail': r're:^http://img\.clubic\.com/.*\.jpg$',
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.clubic.com/video/video-clubic-week-2-0-apple-iphone-6s-et-plus-mais-surtout-le-pencil-469792.html',
|
'url': 'http://www.clubic.com/video/video-clubic-week-2-0-apple-iphone-6s-et-plus-mais-surtout-le-pencil-469792.html',
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .mtv import MTVIE
|
from .mtv import MTVIE
|
||||||
from ..utils import ExtractorError
|
|
||||||
|
|
||||||
|
|
||||||
class CMTIE(MTVIE):
|
class CMTIE(MTVIE):
|
||||||
IE_NAME = 'cmt.com'
|
IE_NAME = 'cmt.com'
|
||||||
_VALID_URL = r'https?://(?:www\.)?cmt\.com/(?:videos|shows)/(?:[^/]+/)*(?P<videoid>\d+)'
|
_VALID_URL = r'https?://(?:www\.)?cmt\.com/(?:videos|shows|(?:full-)?episodes|video-clips)/(?P<id>[^/]+)'
|
||||||
_FEED_URL = 'http://www.cmt.com/sitewide/apps/player/embed/rss/'
|
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.cmt.com/videos/garth-brooks/989124/the-call-featuring-trisha-yearwood.jhtml#artist=30061',
|
'url': 'http://www.cmt.com/videos/garth-brooks/989124/the-call-featuring-trisha-yearwood.jhtml#artist=30061',
|
||||||
@ -33,17 +31,24 @@ class CMTIE(MTVIE):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://www.cmt.com/shows/party-down-south/party-down-south-ep-407-gone-girl/1738172/playlist/#id=1738172',
|
'url': 'http://www.cmt.com/shows/party-down-south/party-down-south-ep-407-gone-girl/1738172/playlist/#id=1738172',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.cmt.com/full-episodes/537qb3/nashville-the-wayfaring-stranger-season-5-ep-501',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.cmt.com/video-clips/t9e4ci/nashville-juliette-in-2-minutes',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _transform_rtmp_url(cls, rtmp_video_url):
|
|
||||||
if 'error_not_available.swf' in rtmp_video_url:
|
|
||||||
raise ExtractorError(
|
|
||||||
'%s said: video is not available' % cls.IE_NAME, expected=True)
|
|
||||||
|
|
||||||
return super(CMTIE, cls)._transform_rtmp_url(rtmp_video_url)
|
|
||||||
|
|
||||||
def _extract_mgid(self, webpage):
|
def _extract_mgid(self, webpage):
|
||||||
return self._search_regex(
|
mgid = self._search_regex(
|
||||||
r'MTVN\.VIDEO\.contentUri\s*=\s*([\'"])(?P<mgid>.+?)\1',
|
r'MTVN\.VIDEO\.contentUri\s*=\s*([\'"])(?P<mgid>.+?)\1',
|
||||||
webpage, 'mgid', group='mgid')
|
webpage, 'mgid', group='mgid', default=None)
|
||||||
|
if not mgid:
|
||||||
|
mgid = self._extract_triforce_mgid(webpage)
|
||||||
|
return mgid
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
mgid = self._extract_mgid(webpage)
|
||||||
|
return self.url_result('http://media.mtvnservices.com/embed/%s' % mgid)
|
||||||
|
@ -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': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 7713.088,
|
'duration': 7713.088,
|
||||||
'timestamp': 1413309600,
|
'timestamp': 1413309600,
|
||||||
'upload_date': '20141014',
|
'upload_date': '20141014',
|
||||||
|
@ -48,15 +48,7 @@ class ComedyCentralFullEpisodesIE(MTVServicesInfoExtractor):
|
|||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
playlist_id = self._match_id(url)
|
playlist_id = self._match_id(url)
|
||||||
webpage = self._download_webpage(url, playlist_id)
|
webpage = self._download_webpage(url, playlist_id)
|
||||||
|
mgid = self._extract_triforce_mgid(webpage, data_zone='t2_lc_promo1')
|
||||||
feed_json = self._search_regex(r'var triforceManifestFeed\s*=\s*(\{.+?\});\n', webpage, 'triforce feeed')
|
|
||||||
feed = self._parse_json(feed_json, playlist_id)
|
|
||||||
zones = feed['manifest']['zones']
|
|
||||||
|
|
||||||
video_zone = zones['t2_lc_promo1']
|
|
||||||
feed = self._download_json(video_zone['feed'], playlist_id)
|
|
||||||
mgid = feed['result']['data']['id']
|
|
||||||
|
|
||||||
videos_info = self._get_videos_info(mgid)
|
videos_info = self._get_videos_info(mgid)
|
||||||
return videos_info
|
return videos_info
|
||||||
|
|
||||||
@ -79,7 +71,7 @@ class ToshIE(MTVServicesInfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Tosh.0|June 9, 2077|2|211|Twitter Users Share Summer Plans',
|
'title': 'Tosh.0|June 9, 2077|2|211|Twitter Users Share Summer Plans',
|
||||||
'description': 'Tosh asked fans to share their summer plans.',
|
'description': 'Tosh asked fans to share their summer plans.',
|
||||||
'thumbnail': 're:^https?://.*\.jpg',
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
# It's really reported to be published on year 2077
|
# It's really reported to be published on year 2077
|
||||||
'upload_date': '20770610',
|
'upload_date': '20770610',
|
||||||
'timestamp': 3390510600,
|
'timestamp': 3390510600,
|
||||||
@ -93,12 +85,6 @@ class ToshIE(MTVServicesInfoExtractor):
|
|||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _transform_rtmp_url(cls, rtmp_video_url):
|
|
||||||
new_urls = super(ToshIE, cls)._transform_rtmp_url(rtmp_video_url)
|
|
||||||
new_urls['rtmp'] = rtmp_video_url.replace('viacomccstrm', 'viacommtvstrm')
|
|
||||||
return new_urls
|
|
||||||
|
|
||||||
|
|
||||||
class ComedyCentralTVIE(MTVServicesInfoExtractor):
|
class ComedyCentralTVIE(MTVServicesInfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?comedycentral\.tv/(?:staffeln|shows)/(?P<id>[^/?#&]+)'
|
_VALID_URL = r'https?://(?:www\.)?comedycentral\.tv/(?:staffeln|shows)/(?P<id>[^/?#&]+)'
|
||||||
|
@ -121,9 +121,19 @@ class InfoExtractor(object):
|
|||||||
download, lower-case.
|
download, lower-case.
|
||||||
"http", "https", "rtsp", "rtmp", "rtmpe",
|
"http", "https", "rtsp", "rtmp", "rtmpe",
|
||||||
"m3u8", "m3u8_native" or "http_dash_segments".
|
"m3u8", "m3u8_native" or "http_dash_segments".
|
||||||
* fragments A list of fragments of the fragmented media,
|
* fragment_base_url
|
||||||
with the following entries:
|
Base URL for fragments. Each fragment's path
|
||||||
* "url" (mandatory) - fragment's URL
|
value (if present) will be relative to
|
||||||
|
this URL.
|
||||||
|
* fragments A list of fragments of a fragmented media.
|
||||||
|
Each fragment entry must contain either an url
|
||||||
|
or a path. If an url is present it should be
|
||||||
|
considered by a client. Otherwise both path and
|
||||||
|
fragment_base_url must be present. Here is
|
||||||
|
the list of all potential fields:
|
||||||
|
* "url" - fragment's URL
|
||||||
|
* "path" - fragment's path relative to
|
||||||
|
fragment_base_url
|
||||||
* "duration" (optional, int or float)
|
* "duration" (optional, int or float)
|
||||||
* "filesize" (optional, int)
|
* "filesize" (optional, int)
|
||||||
* preference Order number of this format. If this field is
|
* preference Order number of this format. If this field is
|
||||||
@ -1015,13 +1025,13 @@ class InfoExtractor(object):
|
|||||||
unique_formats.append(f)
|
unique_formats.append(f)
|
||||||
formats[:] = unique_formats
|
formats[:] = unique_formats
|
||||||
|
|
||||||
def _is_valid_url(self, url, video_id, item='video'):
|
def _is_valid_url(self, url, video_id, item='video', headers={}):
|
||||||
url = self._proto_relative_url(url, scheme='http:')
|
url = self._proto_relative_url(url, scheme='http:')
|
||||||
# For now assume non HTTP(S) URLs always valid
|
# For now assume non HTTP(S) URLs always valid
|
||||||
if not (url.startswith('http://') or url.startswith('https://')):
|
if not (url.startswith('http://') or url.startswith('https://')):
|
||||||
return True
|
return True
|
||||||
try:
|
try:
|
||||||
self._request_webpage(url, video_id, 'Checking %s URL' % item)
|
self._request_webpage(url, video_id, 'Checking %s URL' % item, headers=headers)
|
||||||
return True
|
return True
|
||||||
except ExtractorError as e:
|
except ExtractorError as e:
|
||||||
if isinstance(e.cause, compat_urllib_error.URLError):
|
if isinstance(e.cause, compat_urllib_error.URLError):
|
||||||
@ -1305,8 +1315,8 @@ class InfoExtractor(object):
|
|||||||
'abr': abr,
|
'abr': abr,
|
||||||
})
|
})
|
||||||
f.update(parse_codecs(last_info.get('CODECS')))
|
f.update(parse_codecs(last_info.get('CODECS')))
|
||||||
if audio_in_video_stream.get(last_info.get('AUDIO')) is False:
|
if audio_in_video_stream.get(last_info.get('AUDIO')) is False and f['vcodec'] != 'none':
|
||||||
# TODO: update acodec for for audio only formats with the same GROUP-ID
|
# TODO: update acodec for audio only formats with the same GROUP-ID
|
||||||
f['acodec'] = 'none'
|
f['acodec'] = 'none'
|
||||||
formats.append(f)
|
formats.append(f)
|
||||||
last_info = {}
|
last_info = {}
|
||||||
@ -1627,12 +1637,12 @@ class InfoExtractor(object):
|
|||||||
segment_template = element.find(_add_ns('SegmentTemplate'))
|
segment_template = element.find(_add_ns('SegmentTemplate'))
|
||||||
if segment_template is not None:
|
if segment_template is not None:
|
||||||
extract_common(segment_template)
|
extract_common(segment_template)
|
||||||
media_template = segment_template.get('media')
|
media = segment_template.get('media')
|
||||||
if media_template:
|
if media:
|
||||||
ms_info['media_template'] = media_template
|
ms_info['media'] = media
|
||||||
initialization = segment_template.get('initialization')
|
initialization = segment_template.get('initialization')
|
||||||
if initialization:
|
if initialization:
|
||||||
ms_info['initialization_url'] = initialization
|
ms_info['initialization'] = initialization
|
||||||
else:
|
else:
|
||||||
extract_Initialization(segment_template)
|
extract_Initialization(segment_template)
|
||||||
return ms_info
|
return ms_info
|
||||||
@ -1676,6 +1686,7 @@ class InfoExtractor(object):
|
|||||||
lang = representation_attrib.get('lang')
|
lang = representation_attrib.get('lang')
|
||||||
url_el = representation.find(_add_ns('BaseURL'))
|
url_el = representation.find(_add_ns('BaseURL'))
|
||||||
filesize = int_or_none(url_el.attrib.get('{http://youtube.com/yt/2012/10/10}contentLength') if url_el is not None else None)
|
filesize = int_or_none(url_el.attrib.get('{http://youtube.com/yt/2012/10/10}contentLength') if url_el is not None else None)
|
||||||
|
bandwidth = int_or_none(representation_attrib.get('bandwidth'))
|
||||||
f = {
|
f = {
|
||||||
'format_id': '%s-%s' % (mpd_id, representation_id) if mpd_id else representation_id,
|
'format_id': '%s-%s' % (mpd_id, representation_id) if mpd_id else representation_id,
|
||||||
'url': base_url,
|
'url': base_url,
|
||||||
@ -1683,7 +1694,7 @@ class InfoExtractor(object):
|
|||||||
'ext': mimetype2ext(mime_type),
|
'ext': mimetype2ext(mime_type),
|
||||||
'width': int_or_none(representation_attrib.get('width')),
|
'width': int_or_none(representation_attrib.get('width')),
|
||||||
'height': int_or_none(representation_attrib.get('height')),
|
'height': int_or_none(representation_attrib.get('height')),
|
||||||
'tbr': int_or_none(representation_attrib.get('bandwidth'), 1000),
|
'tbr': int_or_none(bandwidth, 1000),
|
||||||
'asr': int_or_none(representation_attrib.get('audioSamplingRate')),
|
'asr': int_or_none(representation_attrib.get('audioSamplingRate')),
|
||||||
'fps': int_or_none(representation_attrib.get('frameRate')),
|
'fps': int_or_none(representation_attrib.get('frameRate')),
|
||||||
'language': lang if lang not in ('mul', 'und', 'zxx', 'mis') else None,
|
'language': lang if lang not in ('mul', 'und', 'zxx', 'mis') else None,
|
||||||
@ -1692,13 +1703,32 @@ class InfoExtractor(object):
|
|||||||
}
|
}
|
||||||
f.update(parse_codecs(representation_attrib.get('codecs')))
|
f.update(parse_codecs(representation_attrib.get('codecs')))
|
||||||
representation_ms_info = extract_multisegment_info(representation, adaption_set_ms_info)
|
representation_ms_info = extract_multisegment_info(representation, adaption_set_ms_info)
|
||||||
if 'segment_urls' not in representation_ms_info and 'media_template' in representation_ms_info:
|
|
||||||
|
|
||||||
media_template = representation_ms_info['media_template']
|
def prepare_template(template_name, identifiers):
|
||||||
media_template = media_template.replace('$RepresentationID$', representation_id)
|
t = representation_ms_info[template_name]
|
||||||
media_template = re.sub(r'\$(Number|Bandwidth|Time)\$', r'%(\1)d', media_template)
|
t = t.replace('$RepresentationID$', representation_id)
|
||||||
media_template = re.sub(r'\$(Number|Bandwidth|Time)%([^$]+)\$', r'%(\1)\2', media_template)
|
t = re.sub(r'\$(%s)\$' % '|'.join(identifiers), r'%(\1)d', t)
|
||||||
media_template.replace('$$', '$')
|
t = re.sub(r'\$(%s)%%([^$]+)\$' % '|'.join(identifiers), r'%(\1)\2', t)
|
||||||
|
t.replace('$$', '$')
|
||||||
|
return t
|
||||||
|
|
||||||
|
# @initialization is a regular template like @media one
|
||||||
|
# so it should be handled just the same way (see
|
||||||
|
# https://github.com/rg3/youtube-dl/issues/11605)
|
||||||
|
if 'initialization' in representation_ms_info:
|
||||||
|
initialization_template = prepare_template(
|
||||||
|
'initialization',
|
||||||
|
# As per [1, 5.3.9.4.2, Table 15, page 54] $Number$ and
|
||||||
|
# $Time$ shall not be included for @initialization thus
|
||||||
|
# only $Bandwidth$ remains
|
||||||
|
('Bandwidth', ))
|
||||||
|
representation_ms_info['initialization_url'] = initialization_template % {
|
||||||
|
'Bandwidth': bandwidth,
|
||||||
|
}
|
||||||
|
|
||||||
|
if 'segment_urls' not in representation_ms_info and 'media' in representation_ms_info:
|
||||||
|
|
||||||
|
media_template = prepare_template('media', ('Number', 'Bandwidth', 'Time'))
|
||||||
|
|
||||||
# As per [1, 5.3.9.4.4, Table 16, page 55] $Number$ and $Time$
|
# As per [1, 5.3.9.4.4, Table 16, page 55] $Number$ and $Time$
|
||||||
# can't be used at the same time
|
# can't be used at the same time
|
||||||
@ -1710,7 +1740,7 @@ class InfoExtractor(object):
|
|||||||
representation_ms_info['fragments'] = [{
|
representation_ms_info['fragments'] = [{
|
||||||
'url': media_template % {
|
'url': media_template % {
|
||||||
'Number': segment_number,
|
'Number': segment_number,
|
||||||
'Bandwidth': int_or_none(representation_attrib.get('bandwidth')),
|
'Bandwidth': bandwidth,
|
||||||
},
|
},
|
||||||
'duration': segment_duration,
|
'duration': segment_duration,
|
||||||
} for segment_number in range(
|
} for segment_number in range(
|
||||||
@ -1728,7 +1758,7 @@ class InfoExtractor(object):
|
|||||||
def add_segment_url():
|
def add_segment_url():
|
||||||
segment_url = media_template % {
|
segment_url = media_template % {
|
||||||
'Time': segment_time,
|
'Time': segment_time,
|
||||||
'Bandwidth': int_or_none(representation_attrib.get('bandwidth')),
|
'Bandwidth': bandwidth,
|
||||||
'Number': segment_number,
|
'Number': segment_number,
|
||||||
}
|
}
|
||||||
representation_ms_info['fragments'].append({
|
representation_ms_info['fragments'].append({
|
||||||
@ -1751,14 +1781,16 @@ class InfoExtractor(object):
|
|||||||
# Example: https://www.youtube.com/watch?v=iXZV5uAYMJI
|
# Example: https://www.youtube.com/watch?v=iXZV5uAYMJI
|
||||||
# or any YouTube dashsegments video
|
# or any YouTube dashsegments video
|
||||||
fragments = []
|
fragments = []
|
||||||
s_num = 0
|
segment_index = 0
|
||||||
for segment_url in representation_ms_info['segment_urls']:
|
timescale = representation_ms_info['timescale']
|
||||||
s = representation_ms_info['s'][s_num]
|
for s in representation_ms_info['s']:
|
||||||
|
duration = float_or_none(s['d'], timescale)
|
||||||
for r in range(s.get('r', 0) + 1):
|
for r in range(s.get('r', 0) + 1):
|
||||||
fragments.append({
|
fragments.append({
|
||||||
'url': segment_url,
|
'url': representation_ms_info['segment_urls'][segment_index],
|
||||||
'duration': float_or_none(s['d'], representation_ms_info['timescale']),
|
'duration': duration,
|
||||||
})
|
})
|
||||||
|
segment_index += 1
|
||||||
representation_ms_info['fragments'] = fragments
|
representation_ms_info['fragments'] = fragments
|
||||||
# NB: MPD manifest may contain direct URLs to unfragmented media.
|
# NB: MPD manifest may contain direct URLs to unfragmented media.
|
||||||
# No fragments key is present in this case.
|
# No fragments key is present in this case.
|
||||||
@ -1768,7 +1800,7 @@ class InfoExtractor(object):
|
|||||||
'protocol': 'http_dash_segments',
|
'protocol': 'http_dash_segments',
|
||||||
})
|
})
|
||||||
if 'initialization_url' in representation_ms_info:
|
if 'initialization_url' in representation_ms_info:
|
||||||
initialization_url = representation_ms_info['initialization_url'].replace('$RepresentationID$', representation_id)
|
initialization_url = representation_ms_info['initialization_url']
|
||||||
if not f.get('url'):
|
if not f.get('url'):
|
||||||
f['url'] = initialization_url
|
f['url'] = initialization_url
|
||||||
f['fragments'].append({'url': initialization_url})
|
f['fragments'].append({'url': initialization_url})
|
||||||
@ -1927,7 +1959,12 @@ class InfoExtractor(object):
|
|||||||
media_tags = [(media_tag, media_type, '')
|
media_tags = [(media_tag, media_type, '')
|
||||||
for media_tag, media_type
|
for media_tag, media_type
|
||||||
in re.findall(r'(?s)(<(video|audio)[^>]*/>)', webpage)]
|
in re.findall(r'(?s)(<(video|audio)[^>]*/>)', webpage)]
|
||||||
media_tags.extend(re.findall(r'(?s)(<(?P<tag>video|audio)[^>]*>)(.*?)</(?P=tag)>', webpage))
|
media_tags.extend(re.findall(
|
||||||
|
# We only allow video|audio followed by a whitespace or '>'.
|
||||||
|
# Allowing more characters may end up in significant slow down (see
|
||||||
|
# https://github.com/rg3/youtube-dl/issues/11979, example URL:
|
||||||
|
# http://www.porntrex.com/maps/videositemap.xml).
|
||||||
|
r'(?s)(<(?P<tag>video|audio)(?:\s+[^>]*)?>)(.*?)</(?P=tag)>', webpage))
|
||||||
for media_tag, media_type, media_content in media_tags:
|
for media_tag, media_type, media_content in media_tags:
|
||||||
media_info = {
|
media_info = {
|
||||||
'formats': [],
|
'formats': [],
|
||||||
@ -1967,10 +2004,13 @@ class InfoExtractor(object):
|
|||||||
entries.append(media_info)
|
entries.append(media_info)
|
||||||
return entries
|
return entries
|
||||||
|
|
||||||
def _extract_akamai_formats(self, manifest_url, video_id):
|
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')
|
||||||
|
if hds_host:
|
||||||
|
f4m_url = re.sub(r'(https?://)[^/]+', r'\1' + hds_host, f4m_url)
|
||||||
if 'hdcore=' not in f4m_url:
|
if 'hdcore=' not in f4m_url:
|
||||||
f4m_url += ('&' if '?' in f4m_url else '?') + hdcore_sign
|
f4m_url += ('&' if '?' in f4m_url else '?') + hdcore_sign
|
||||||
f4m_formats = self._extract_f4m_formats(
|
f4m_formats = self._extract_f4m_formats(
|
||||||
@ -1978,7 +2018,10 @@ class InfoExtractor(object):
|
|||||||
for entry in f4m_formats:
|
for entry in f4m_formats:
|
||||||
entry.update({'extra_param_to_segment_url': hdcore_sign})
|
entry.update({'extra_param_to_segment_url': hdcore_sign})
|
||||||
formats.extend(f4m_formats)
|
formats.extend(f4m_formats)
|
||||||
m3u8_url = re.sub(r'(https?://.+?)/z/', r'\1/i/', manifest_url).replace('/manifest.f4m', '/master.m3u8')
|
m3u8_url = re.sub(r'(https?://[^/]+)/z/', r'\1/i/', manifest_url).replace('/manifest.f4m', '/master.m3u8')
|
||||||
|
hls_host = hosts.get('hls')
|
||||||
|
if hls_host:
|
||||||
|
m3u8_url = re.sub(r'(https?://)[^/]+', r'\1' + hls_host, m3u8_url)
|
||||||
formats.extend(self._extract_m3u8_formats(
|
formats.extend(self._extract_m3u8_formats(
|
||||||
m3u8_url, video_id, 'mp4', 'm3u8_native',
|
m3u8_url, video_id, 'mp4', 'm3u8_native',
|
||||||
m3u8_id='hls', fatal=False))
|
m3u8_id='hls', fatal=False))
|
||||||
|
@ -20,7 +20,7 @@ class CoubIE(InfoExtractor):
|
|||||||
'id': '5u5n1',
|
'id': '5u5n1',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'The Matrix Moonwalk',
|
'title': 'The Matrix Moonwalk',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 4.6,
|
'duration': 4.6,
|
||||||
'timestamp': 1428527772,
|
'timestamp': 1428527772,
|
||||||
'upload_date': '20150408',
|
'upload_date': '20150408',
|
||||||
|
@ -6,7 +6,7 @@ from ..utils import int_or_none
|
|||||||
|
|
||||||
|
|
||||||
class CrackleIE(InfoExtractor):
|
class CrackleIE(InfoExtractor):
|
||||||
_VALID_URL = r'(?:crackle:|https?://(?:www\.)?crackle\.com/(?:playlist/\d+/|(?:[^/]+/)+))(?P<id>\d+)'
|
_VALID_URL = r'(?:crackle:|https?://(?:(?:www|m)\.)?crackle\.com/(?:playlist/\d+/|(?:[^/]+/)+))(?P<id>\d+)'
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://www.crackle.com/comedians-in-cars-getting-coffee/2498934',
|
'url': 'http://www.crackle.com/comedians-in-cars-getting-coffee/2498934',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@ -14,7 +14,7 @@ class CrackleIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Everybody Respects A Bloody Nose',
|
'title': 'Everybody Respects A Bloody Nose',
|
||||||
'description': 'Jerry is kaffeeklatsching in L.A. with funnyman J.B. Smoove (Saturday Night Live, Real Husbands of Hollywood). They’re headed for brew at 10 Speed Coffee in a 1964 Studebaker Avanti.',
|
'description': 'Jerry is kaffeeklatsching in L.A. with funnyman J.B. Smoove (Saturday Night Live, Real Husbands of Hollywood). They’re headed for brew at 10 Speed Coffee in a 1964 Studebaker Avanti.',
|
||||||
'thumbnail': 're:^https?://.*\.jpg',
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
'duration': 906,
|
'duration': 906,
|
||||||
'series': 'Comedians In Cars Getting Coffee',
|
'series': 'Comedians In Cars Getting Coffee',
|
||||||
'season_number': 8,
|
'season_number': 8,
|
||||||
@ -31,8 +31,32 @@ class CrackleIE(InfoExtractor):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_THUMBNAIL_RES = [
|
||||||
|
(120, 90),
|
||||||
|
(208, 156),
|
||||||
|
(220, 124),
|
||||||
|
(220, 220),
|
||||||
|
(240, 180),
|
||||||
|
(250, 141),
|
||||||
|
(315, 236),
|
||||||
|
(320, 180),
|
||||||
|
(360, 203),
|
||||||
|
(400, 300),
|
||||||
|
(421, 316),
|
||||||
|
(460, 330),
|
||||||
|
(460, 460),
|
||||||
|
(462, 260),
|
||||||
|
(480, 270),
|
||||||
|
(587, 330),
|
||||||
|
(640, 480),
|
||||||
|
(700, 330),
|
||||||
|
(700, 394),
|
||||||
|
(854, 480),
|
||||||
|
(1024, 1024),
|
||||||
|
(1920, 1080),
|
||||||
|
]
|
||||||
|
|
||||||
# extracted from http://legacyweb-us.crackle.com/flash/ReferrerRedirect.ashx
|
# extracted from http://legacyweb-us.crackle.com/flash/ReferrerRedirect.ashx
|
||||||
_THUMBNAIL_TEMPLATE = 'http://images-us-am.crackle.com/%stnl_1920x1080.jpg?ts=20140107233116?c=635333335057637614'
|
|
||||||
_MEDIA_FILE_SLOTS = {
|
_MEDIA_FILE_SLOTS = {
|
||||||
'c544.flv': {
|
'c544.flv': {
|
||||||
'width': 544,
|
'width': 544,
|
||||||
@ -61,17 +85,25 @@ class CrackleIE(InfoExtractor):
|
|||||||
|
|
||||||
item = self._download_xml(
|
item = self._download_xml(
|
||||||
'http://legacyweb-us.crackle.com/app/revamp/vidwallcache.aspx?flags=-1&fm=%s' % video_id,
|
'http://legacyweb-us.crackle.com/app/revamp/vidwallcache.aspx?flags=-1&fm=%s' % video_id,
|
||||||
video_id).find('i')
|
video_id, headers=self.geo_verification_headers()).find('i')
|
||||||
title = item.attrib['t']
|
title = item.attrib['t']
|
||||||
|
|
||||||
subtitles = {}
|
subtitles = {}
|
||||||
formats = self._extract_m3u8_formats(
|
formats = self._extract_m3u8_formats(
|
||||||
'http://content.uplynk.com/ext/%s/%s.m3u8' % (config_doc.attrib['strUplynkOwnerId'], video_id),
|
'http://content.uplynk.com/ext/%s/%s.m3u8' % (config_doc.attrib['strUplynkOwnerId'], video_id),
|
||||||
video_id, 'mp4', m3u8_id='hls', fatal=None)
|
video_id, 'mp4', m3u8_id='hls', fatal=None)
|
||||||
thumbnail = None
|
thumbnails = []
|
||||||
path = item.attrib.get('p')
|
path = item.attrib.get('p')
|
||||||
if path:
|
if path:
|
||||||
thumbnail = self._THUMBNAIL_TEMPLATE % path
|
for width, height in self._THUMBNAIL_RES:
|
||||||
|
res = '%dx%d' % (width, height)
|
||||||
|
thumbnails.append({
|
||||||
|
'id': res,
|
||||||
|
'url': 'http://images-us-am.crackle.com/%stnl_%s.jpg' % (path, res),
|
||||||
|
'width': width,
|
||||||
|
'height': height,
|
||||||
|
'resolution': res,
|
||||||
|
})
|
||||||
http_base_url = 'http://ahttp.crackle.com/' + path
|
http_base_url = 'http://ahttp.crackle.com/' + path
|
||||||
for mfs_path, mfs_info in self._MEDIA_FILE_SLOTS.items():
|
for mfs_path, mfs_info in self._MEDIA_FILE_SLOTS.items():
|
||||||
formats.append({
|
formats.append({
|
||||||
@ -86,10 +118,11 @@ class CrackleIE(InfoExtractor):
|
|||||||
if locale and v:
|
if locale and v:
|
||||||
if locale not in subtitles:
|
if locale not in subtitles:
|
||||||
subtitles[locale] = []
|
subtitles[locale] = []
|
||||||
subtitles[locale] = [{
|
for url_ext, ext in (('vtt', 'vtt'), ('xml', 'tt')):
|
||||||
'url': '%s/%s%s_%s.xml' % (config_doc.attrib['strSubtitleServer'], path, locale, v),
|
subtitles.setdefault(locale, []).append({
|
||||||
'ext': 'ttml',
|
'url': '%s/%s%s_%s.%s' % (config_doc.attrib['strSubtitleServer'], path, locale, v, url_ext),
|
||||||
}]
|
'ext': ext,
|
||||||
|
})
|
||||||
self._sort_formats(formats, ('width', 'height', 'tbr', 'format_id'))
|
self._sort_formats(formats, ('width', 'height', 'tbr', 'format_id'))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -100,7 +133,7 @@ class CrackleIE(InfoExtractor):
|
|||||||
'series': item.attrib.get('sn'),
|
'series': item.attrib.get('sn'),
|
||||||
'season_number': int_or_none(item.attrib.get('se')),
|
'season_number': int_or_none(item.attrib.get('se')),
|
||||||
'episode_number': int_or_none(item.attrib.get('ep')),
|
'episode_number': int_or_none(item.attrib.get('ep')),
|
||||||
'thumbnail': thumbnail,
|
'thumbnails': thumbnails,
|
||||||
'subtitles': subtitles,
|
'subtitles': subtitles,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class CriterionIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Le Samouraï',
|
'title': 'Le Samouraï',
|
||||||
'description': 'md5:a2b4b116326558149bef81f76dcbb93f',
|
'description': 'md5:a2b4b116326558149bef81f76dcbb93f',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ class CrooksAndLiarsIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Fox & Friends Says Protecting Atheists From Discrimination Is Anti-Christian!',
|
'title': 'Fox & Friends Says Protecting Atheists From Discrimination Is Anti-Christian!',
|
||||||
'description': 'md5:e1a46ad1650e3a5ec7196d432799127f',
|
'description': 'md5:e1a46ad1650e3a5ec7196d432799127f',
|
||||||
'thumbnail': 're:^https?://.*\.jpg',
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
'timestamp': 1428207000,
|
'timestamp': 1428207000,
|
||||||
'upload_date': '20150405',
|
'upload_date': '20150405',
|
||||||
'uploader': 'Heather',
|
'uploader': 'Heather',
|
||||||
|
@ -142,7 +142,7 @@ class CrunchyrollIE(CrunchyrollBaseIE):
|
|||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
'title': 'Culture Japan Episode 1 – Rebuilding Japan after the 3.11',
|
'title': 'Culture Japan Episode 1 – Rebuilding Japan after the 3.11',
|
||||||
'description': 'md5:2fbc01f90b87e8e9137296f37b461c12',
|
'description': 'md5:2fbc01f90b87e8e9137296f37b461c12',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'uploader': 'Danny Choo Network',
|
'uploader': 'Danny Choo Network',
|
||||||
'upload_date': '20120213',
|
'upload_date': '20120213',
|
||||||
},
|
},
|
||||||
@ -158,7 +158,7 @@ class CrunchyrollIE(CrunchyrollBaseIE):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Re:ZERO -Starting Life in Another World- Episode 5 – The Morning of Our Promise Is Still Distant',
|
'title': 'Re:ZERO -Starting Life in Another World- Episode 5 – The Morning of Our Promise Is Still Distant',
|
||||||
'description': 'md5:97664de1ab24bbf77a9c01918cb7dca9',
|
'description': 'md5:97664de1ab24bbf77a9c01918cb7dca9',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'uploader': 'TV TOKYO',
|
'uploader': 'TV TOKYO',
|
||||||
'upload_date': '20160508',
|
'upload_date': '20160508',
|
||||||
},
|
},
|
||||||
@ -166,6 +166,25 @@ class CrunchyrollIE(CrunchyrollBaseIE):
|
|||||||
# m3u8 download
|
# m3u8 download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.crunchyroll.com/konosuba-gods-blessing-on-this-wonderful-world/episode-1-give-me-deliverance-from-this-judicial-injustice-727589',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '727589',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': "KONOSUBA -God's blessing on this wonderful world! 2 Episode 1 – Give Me Deliverance from this Judicial Injustice!",
|
||||||
|
'description': 'md5:cbcf05e528124b0f3a0a419fc805ea7d',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
'uploader': 'Kadokawa Pictures Inc.',
|
||||||
|
'upload_date': '20170118',
|
||||||
|
'series': "KONOSUBA -God's blessing on this wonderful world!",
|
||||||
|
'season_number': 2,
|
||||||
|
'episode': 'Give Me Deliverance from this Judicial Injustice!',
|
||||||
|
'episode_number': 1,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.crunchyroll.fr/girl-friend-beta/episode-11-goodbye-la-mode-661697',
|
'url': 'http://www.crunchyroll.fr/girl-friend-beta/episode-11-goodbye-la-mode-661697',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
@ -236,8 +255,7 @@ class CrunchyrollIE(CrunchyrollBaseIE):
|
|||||||
output += 'WrapStyle: %s\n' % sub_root.attrib['wrap_style']
|
output += 'WrapStyle: %s\n' % sub_root.attrib['wrap_style']
|
||||||
output += 'PlayResX: %s\n' % sub_root.attrib['play_res_x']
|
output += 'PlayResX: %s\n' % sub_root.attrib['play_res_x']
|
||||||
output += 'PlayResY: %s\n' % sub_root.attrib['play_res_y']
|
output += 'PlayResY: %s\n' % sub_root.attrib['play_res_y']
|
||||||
output += """ScaledBorderAndShadow: no
|
output += """
|
||||||
|
|
||||||
[V4+ Styles]
|
[V4+ Styles]
|
||||||
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
||||||
"""
|
"""
|
||||||
@ -439,6 +457,18 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
|||||||
|
|
||||||
subtitles = self.extract_subtitles(video_id, webpage)
|
subtitles = self.extract_subtitles(video_id, webpage)
|
||||||
|
|
||||||
|
# webpage provide more accurate data than series_title from XML
|
||||||
|
series = self._html_search_regex(
|
||||||
|
r'id=["\']showmedia_about_episode_num[^>]+>\s*<a[^>]+>([^<]+)',
|
||||||
|
webpage, 'series', default=xpath_text(metadata, 'series_title'))
|
||||||
|
|
||||||
|
episode = xpath_text(metadata, 'episode_title')
|
||||||
|
episode_number = int_or_none(xpath_text(metadata, 'episode_number'))
|
||||||
|
|
||||||
|
season_number = int_or_none(self._search_regex(
|
||||||
|
r'(?s)<h4[^>]+id=["\']showmedia_about_episode_num[^>]+>.+?</h4>\s*<h4>\s*Season (\d+)',
|
||||||
|
webpage, 'season number', default=None))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': video_title,
|
'title': video_title,
|
||||||
@ -446,9 +476,10 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
|||||||
'thumbnail': xpath_text(metadata, 'episode_image_url'),
|
'thumbnail': xpath_text(metadata, 'episode_image_url'),
|
||||||
'uploader': video_uploader,
|
'uploader': video_uploader,
|
||||||
'upload_date': video_upload_date,
|
'upload_date': video_upload_date,
|
||||||
'series': xpath_text(metadata, 'series_title'),
|
'series': series,
|
||||||
'episode': xpath_text(metadata, 'episode_title'),
|
'season_number': season_number,
|
||||||
'episode_number': int_or_none(xpath_text(metadata, 'episode_number')),
|
'episode': episode,
|
||||||
|
'episode_number': episode_number,
|
||||||
'subtitles': subtitles,
|
'subtitles': subtitles,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ from ..utils import (
|
|||||||
ExtractorError,
|
ExtractorError,
|
||||||
)
|
)
|
||||||
from .senateisvp import SenateISVPIE
|
from .senateisvp import SenateISVPIE
|
||||||
|
from .ustream import UstreamIE
|
||||||
|
|
||||||
|
|
||||||
class CSpanIE(InfoExtractor):
|
class CSpanIE(InfoExtractor):
|
||||||
@ -22,14 +23,13 @@ class CSpanIE(InfoExtractor):
|
|||||||
'md5': '94b29a4f131ff03d23471dd6f60b6a1d',
|
'md5': '94b29a4f131ff03d23471dd6f60b6a1d',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '315139',
|
'id': '315139',
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Attorney General Eric Holder on Voting Rights Act Decision',
|
'title': 'Attorney General Eric Holder on Voting Rights Act Decision',
|
||||||
'description': 'Attorney General Eric Holder speaks to reporters following the Supreme Court decision in [Shelby County v. Holder], in which the court ruled that the preclearance provisions of the Voting Rights Act could not be enforced.',
|
|
||||||
},
|
},
|
||||||
|
'playlist_mincount': 2,
|
||||||
'skip': 'Regularly fails on travis, for unknown reasons',
|
'skip': 'Regularly fails on travis, for unknown reasons',
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.c-span.org/video/?c4486943/cspan-international-health-care-models',
|
'url': 'http://www.c-span.org/video/?c4486943/cspan-international-health-care-models',
|
||||||
'md5': '8e5fbfabe6ad0f89f3012a7943c1287b',
|
# md5 is unstable
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'c4486943',
|
'id': 'c4486943',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
@ -38,14 +38,11 @@ class CSpanIE(InfoExtractor):
|
|||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.c-span.org/video/?318608-1/gm-ignition-switch-recall',
|
'url': 'http://www.c-span.org/video/?318608-1/gm-ignition-switch-recall',
|
||||||
'md5': '2ae5051559169baadba13fc35345ae74',
|
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '342759',
|
'id': '342759',
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'General Motors Ignition Switch Recall',
|
'title': 'General Motors Ignition Switch Recall',
|
||||||
'duration': 14848,
|
|
||||||
'description': 'md5:118081aedd24bf1d3b68b3803344e7f3'
|
|
||||||
},
|
},
|
||||||
|
'playlist_mincount': 6,
|
||||||
}, {
|
}, {
|
||||||
# Video from senate.gov
|
# Video from senate.gov
|
||||||
'url': 'http://www.c-span.org/video/?104517-1/immigration-reforms-needed-protect-skilled-american-workers',
|
'url': 'http://www.c-span.org/video/?104517-1/immigration-reforms-needed-protect-skilled-american-workers',
|
||||||
@ -57,12 +54,30 @@ class CSpanIE(InfoExtractor):
|
|||||||
'params': {
|
'params': {
|
||||||
'skip_download': True, # m3u8 downloads
|
'skip_download': True, # m3u8 downloads
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
# Ustream embedded video
|
||||||
|
'url': 'https://www.c-span.org/video/?114917-1/armed-services',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '58428542',
|
||||||
|
'ext': 'flv',
|
||||||
|
'title': 'USHR07 Armed Services Committee',
|
||||||
|
'description': 'hsas00-2118-20150204-1000et-07\n\n\nUSHR07 Armed Services Committee',
|
||||||
|
'timestamp': 1423060374,
|
||||||
|
'upload_date': '20150204',
|
||||||
|
'uploader': 'HouseCommittee',
|
||||||
|
'uploader_id': '12987475',
|
||||||
|
},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
video_type = None
|
video_type = None
|
||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
|
ustream_url = UstreamIE._extract_url(webpage)
|
||||||
|
if ustream_url:
|
||||||
|
return self.url_result(ustream_url, UstreamIE.ie_key())
|
||||||
|
|
||||||
# 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)))
|
||||||
|
@ -28,7 +28,7 @@ class CtsNewsIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': '韓國31歲童顏男 貌如十多歲小孩',
|
'title': '韓國31歲童顏男 貌如十多歲小孩',
|
||||||
'description': '越有年紀的人,越希望看起來年輕一點,而南韓卻有一位31歲的男子,看起來像是11、12歲的小孩,身...',
|
'description': '越有年紀的人,越希望看起來年輕一點,而南韓卻有一位31歲的男子,看起來像是11、12歲的小孩,身...',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'timestamp': 1378205880,
|
'timestamp': 1378205880,
|
||||||
'upload_date': '20130903',
|
'upload_date': '20130903',
|
||||||
}
|
}
|
||||||
@ -41,7 +41,7 @@ class CtsNewsIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'iPhone6熱銷 蘋果財報亮眼',
|
'title': 'iPhone6熱銷 蘋果財報亮眼',
|
||||||
'description': 'md5:f395d4f485487bb0f992ed2c4b07aa7d',
|
'description': 'md5:f395d4f485487bb0f992ed2c4b07aa7d',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'upload_date': '20150128',
|
'upload_date': '20150128',
|
||||||
'uploader_id': 'TBSCTS',
|
'uploader_id': 'TBSCTS',
|
||||||
'uploader': '中華電視公司',
|
'uploader': '中華電視公司',
|
||||||
|
@ -21,7 +21,7 @@ class CultureUnpluggedIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'The Next, Best West',
|
'title': 'The Next, Best West',
|
||||||
'description': 'md5:0423cd00833dea1519cf014e9d0903b1',
|
'description': 'md5:0423cd00833dea1519cf014e9d0903b1',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'creator': 'Coldstream Creative',
|
'creator': 'Coldstream Creative',
|
||||||
'duration': 2203,
|
'duration': 2203,
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
|
@ -58,7 +58,7 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Steam Machine Models, Pricing Listed on Steam Store - IGN News',
|
'title': 'Steam Machine Models, Pricing Listed on Steam Store - IGN News',
|
||||||
'description': 'Several come bundled with the Steam Controller.',
|
'description': 'Several come bundled with the Steam Controller.',
|
||||||
'thumbnail': 're:^https?:.*\.(?:jpg|png)$',
|
'thumbnail': r're:^https?:.*\.(?:jpg|png)$',
|
||||||
'duration': 74,
|
'duration': 74,
|
||||||
'timestamp': 1425657362,
|
'timestamp': 1425657362,
|
||||||
'upload_date': '20150306',
|
'upload_date': '20150306',
|
||||||
|
@ -32,7 +32,7 @@ class DaumIE(InfoExtractor):
|
|||||||
'title': '마크 헌트 vs 안토니오 실바',
|
'title': '마크 헌트 vs 안토니오 실바',
|
||||||
'description': 'Mark Hunt vs Antonio Silva',
|
'description': 'Mark Hunt vs Antonio Silva',
|
||||||
'upload_date': '20131217',
|
'upload_date': '20131217',
|
||||||
'thumbnail': 're:^https?://.*\.(?:jpg|png)',
|
'thumbnail': r're:^https?://.*\.(?:jpg|png)',
|
||||||
'duration': 2117,
|
'duration': 2117,
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
'comment_count': int,
|
'comment_count': int,
|
||||||
@ -45,7 +45,7 @@ class DaumIE(InfoExtractor):
|
|||||||
'title': '1297회, \'아빠 아들로 태어나길 잘 했어\' 민수, 감동의 눈물[아빠 어디가] 20150118',
|
'title': '1297회, \'아빠 아들로 태어나길 잘 했어\' 민수, 감동의 눈물[아빠 어디가] 20150118',
|
||||||
'description': 'md5:79794514261164ff27e36a21ad229fc5',
|
'description': 'md5:79794514261164ff27e36a21ad229fc5',
|
||||||
'upload_date': '20150604',
|
'upload_date': '20150604',
|
||||||
'thumbnail': 're:^https?://.*\.(?:jpg|png)',
|
'thumbnail': r're:^https?://.*\.(?:jpg|png)',
|
||||||
'duration': 154,
|
'duration': 154,
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
'comment_count': int,
|
'comment_count': int,
|
||||||
@ -61,7 +61,7 @@ class DaumIE(InfoExtractor):
|
|||||||
'title': '01-Korean War ( Trouble on the horizon )',
|
'title': '01-Korean War ( Trouble on the horizon )',
|
||||||
'description': '\nKorean War 01\nTrouble on the horizon\n전쟁의 먹구름',
|
'description': '\nKorean War 01\nTrouble on the horizon\n전쟁의 먹구름',
|
||||||
'upload_date': '20080223',
|
'upload_date': '20080223',
|
||||||
'thumbnail': 're:^https?://.*\.(?:jpg|png)',
|
'thumbnail': r're:^https?://.*\.(?:jpg|png)',
|
||||||
'duration': 249,
|
'duration': 249,
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
'comment_count': int,
|
'comment_count': int,
|
||||||
@ -139,7 +139,7 @@ class DaumClipIE(InfoExtractor):
|
|||||||
'title': 'DOTA 2GETHER 시즌2 6회 - 2부',
|
'title': 'DOTA 2GETHER 시즌2 6회 - 2부',
|
||||||
'description': 'DOTA 2GETHER 시즌2 6회 - 2부',
|
'description': 'DOTA 2GETHER 시즌2 6회 - 2부',
|
||||||
'upload_date': '20130831',
|
'upload_date': '20130831',
|
||||||
'thumbnail': 're:^https?://.*\.(?:jpg|png)',
|
'thumbnail': r're:^https?://.*\.(?:jpg|png)',
|
||||||
'duration': 3868,
|
'duration': 3868,
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
},
|
},
|
||||||
|
@ -17,7 +17,7 @@ class DBTVIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Skulle teste ut fornøyelsespark, men kollegaen var bare opptatt av bikinikroppen',
|
'title': 'Skulle teste ut fornøyelsespark, men kollegaen var bare opptatt av bikinikroppen',
|
||||||
'description': 'md5:1504a54606c4dde3e4e61fc97aa857e0',
|
'description': 'md5:1504a54606c4dde3e4e61fc97aa857e0',
|
||||||
'thumbnail': 're:https?://.*\.jpg',
|
'thumbnail': r're:https?://.*\.jpg',
|
||||||
'timestamp': 1404039863,
|
'timestamp': 1404039863,
|
||||||
'upload_date': '20140629',
|
'upload_date': '20140629',
|
||||||
'duration': 69.544,
|
'duration': 69.544,
|
||||||
|
@ -17,7 +17,7 @@ class DctpTvIE(InfoExtractor):
|
|||||||
'title': 'Videoinstallation für eine Kaufhausfassade',
|
'title': 'Videoinstallation für eine Kaufhausfassade',
|
||||||
'description': 'Kurzfilm',
|
'description': 'Kurzfilm',
|
||||||
'upload_date': '20110407',
|
'upload_date': '20110407',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ class DeezerPlaylistIE(InfoExtractor):
|
|||||||
'id': '176747451',
|
'id': '176747451',
|
||||||
'title': 'Best!',
|
'title': 'Best!',
|
||||||
'uploader': 'Anonymous',
|
'uploader': 'Anonymous',
|
||||||
'thumbnail': 're:^https?://cdn-images.deezer.com/images/cover/.*\.jpg$',
|
'thumbnail': r're:^https?://cdn-images.deezer.com/images/cover/.*\.jpg$',
|
||||||
},
|
},
|
||||||
'playlist_count': 30,
|
'playlist_count': 30,
|
||||||
'skip': 'Only available in .de',
|
'skip': 'Only available in .de',
|
||||||
|
@ -17,7 +17,7 @@ class DHMIE(InfoExtractor):
|
|||||||
'title': 'MARSHALL PLAN AT WORK IN WESTERN GERMANY, THE',
|
'title': 'MARSHALL PLAN AT WORK IN WESTERN GERMANY, THE',
|
||||||
'description': 'md5:1fabd480c153f97b07add61c44407c82',
|
'description': 'md5:1fabd480c153f97b07add61c44407c82',
|
||||||
'duration': 660,
|
'duration': 660,
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.dhm.de/filmarchiv/02-mapping-the-wall/peter-g/rolle-1/',
|
'url': 'http://www.dhm.de/filmarchiv/02-mapping-the-wall/peter-g/rolle-1/',
|
||||||
@ -26,7 +26,7 @@ class DHMIE(InfoExtractor):
|
|||||||
'id': 'rolle-1',
|
'id': 'rolle-1',
|
||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
'title': 'ROLLE 1',
|
'title': 'ROLLE 1',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
},
|
},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class DigitekaIE(InfoExtractor):
|
|||||||
'id': 's8uk0r',
|
'id': 's8uk0r',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Loi sur la fin de vie: le texte prévoit un renforcement des directives anticipées',
|
'title': 'Loi sur la fin de vie: le texte prévoit un renforcement des directives anticipées',
|
||||||
'thumbnail': 're:^https?://.*\.jpg',
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
'duration': 74,
|
'duration': 74,
|
||||||
'upload_date': '20150317',
|
'upload_date': '20150317',
|
||||||
'timestamp': 1426604939,
|
'timestamp': 1426604939,
|
||||||
@ -50,7 +50,7 @@ class DigitekaIE(InfoExtractor):
|
|||||||
'id': 'xvpfp8',
|
'id': 'xvpfp8',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Two - C\'est La Vie (clip)',
|
'title': 'Two - C\'est La Vie (clip)',
|
||||||
'thumbnail': 're:^https?://.*\.jpg',
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
'duration': 233,
|
'duration': 233,
|
||||||
'upload_date': '20150224',
|
'upload_date': '20150224',
|
||||||
'timestamp': 1424760500,
|
'timestamp': 1424760500,
|
||||||
|
@ -6,7 +6,6 @@ from ..utils import (
|
|||||||
extract_attributes,
|
extract_attributes,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_age_limit,
|
parse_age_limit,
|
||||||
unescapeHTML,
|
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -49,7 +48,7 @@ class DiscoveryGoIE(InfoExtractor):
|
|||||||
webpage, 'video container'))
|
webpage, 'video container'))
|
||||||
|
|
||||||
video = self._parse_json(
|
video = self._parse_json(
|
||||||
unescapeHTML(container.get('data-video') or container.get('data-json')),
|
container.get('data-video') or container.get('data-json'),
|
||||||
display_id)
|
display_id)
|
||||||
|
|
||||||
title = video['name']
|
title = video['name']
|
||||||
|
115
youtube_dl/extractor/disney.py
Normal file
115
youtube_dl/extractor/disney.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
int_or_none,
|
||||||
|
unified_strdate,
|
||||||
|
compat_str,
|
||||||
|
determine_ext,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DisneyIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'''(?x)
|
||||||
|
https?://(?P<domain>(?:[^/]+\.)?(?:disney\.[a-z]{2,3}(?:\.[a-z]{2})?|disney(?:(?:me|latino)\.com|turkiye\.com\.tr)|starwars\.com))/(?:embed/|(?:[^/]+/)+[\w-]+-)(?P<id>[a-z0-9]{24})'''
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'http://video.disney.com/watch/moana-trailer-545ed1857afee5a0ec239977',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '545ed1857afee5a0ec239977',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Moana - Trailer',
|
||||||
|
'description': 'A fun adventure for the entire Family! Bring home Moana on Digital HD Feb 21 & Blu-ray March 7',
|
||||||
|
'upload_date': '20170112',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'url': 'http://videos.disneylatino.com/ver/spider-man-de-regreso-a-casa-primer-adelanto-543a33a1850bdcfcca13bae2',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://video.en.disneyme.com/watch/future-worm/robo-carp-2001-544b66002aa7353cdd3f5114',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://video.disneyturkiye.com.tr/izle/7c-7-cuceler/kimin-sesi-zaten-5456f3d015f6b36c8afdd0e2',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://disneyjunior.disney.com/embed/546a4798ddba3d1612e4005d',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.starwars.com/embed/54690d1e6c42e5f09a0fb097',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
domain, video_id = re.match(self._VALID_URL, url).groups()
|
||||||
|
webpage = self._download_webpage(
|
||||||
|
'http://%s/embed/%s' % (domain, video_id), video_id)
|
||||||
|
video_data = self._parse_json(self._search_regex(
|
||||||
|
r'Disney\.EmbedVideo=({.+});', webpage, 'embed data'), video_id)['video']
|
||||||
|
|
||||||
|
for external in video_data.get('externals', []):
|
||||||
|
if external.get('source') == 'vevo':
|
||||||
|
return self.url_result('vevo:' + external['data_id'], 'Vevo')
|
||||||
|
|
||||||
|
title = video_data['title']
|
||||||
|
|
||||||
|
formats = []
|
||||||
|
for flavor in video_data.get('flavors', []):
|
||||||
|
flavor_format = flavor.get('format')
|
||||||
|
flavor_url = flavor.get('url')
|
||||||
|
if not flavor_url or not re.match(r'https?://', flavor_url):
|
||||||
|
continue
|
||||||
|
tbr = int_or_none(flavor.get('bitrate'))
|
||||||
|
if tbr == 99999:
|
||||||
|
formats.extend(self._extract_m3u8_formats(
|
||||||
|
flavor_url, video_id, 'mp4', m3u8_id=flavor_format, fatal=False))
|
||||||
|
continue
|
||||||
|
format_id = []
|
||||||
|
if flavor_format:
|
||||||
|
format_id.append(flavor_format)
|
||||||
|
if tbr:
|
||||||
|
format_id.append(compat_str(tbr))
|
||||||
|
ext = determine_ext(flavor_url)
|
||||||
|
if flavor_format == 'applehttp' or ext == 'm3u8':
|
||||||
|
ext = 'mp4'
|
||||||
|
width = int_or_none(flavor.get('width'))
|
||||||
|
height = int_or_none(flavor.get('height'))
|
||||||
|
formats.append({
|
||||||
|
'format_id': '-'.join(format_id),
|
||||||
|
'url': flavor_url,
|
||||||
|
'width': width,
|
||||||
|
'height': height,
|
||||||
|
'tbr': tbr,
|
||||||
|
'ext': ext,
|
||||||
|
'vcodec': 'none' if (width == 0 and height == 0) else None,
|
||||||
|
})
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
subtitles = {}
|
||||||
|
for caption in video_data.get('captions', []):
|
||||||
|
caption_url = caption.get('url')
|
||||||
|
caption_format = caption.get('format')
|
||||||
|
if not caption_url or caption_format.startswith('unknown'):
|
||||||
|
continue
|
||||||
|
subtitles.setdefault(caption.get('language', 'en'), []).append({
|
||||||
|
'url': caption_url,
|
||||||
|
'ext': {
|
||||||
|
'webvtt': 'vtt',
|
||||||
|
}.get(caption_format, caption_format),
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': title,
|
||||||
|
'description': video_data.get('description') or video_data.get('short_desc'),
|
||||||
|
'thumbnail': video_data.get('thumb') or video_data.get('thumb_secure'),
|
||||||
|
'duration': int_or_none(video_data.get('duration_sec')),
|
||||||
|
'upload_date': unified_strdate(video_data.get('publish_date')),
|
||||||
|
'formats': formats,
|
||||||
|
'subtitles': subtitles,
|
||||||
|
}
|
@ -18,7 +18,7 @@ from ..utils import (
|
|||||||
|
|
||||||
class DouyuTVIE(InfoExtractor):
|
class DouyuTVIE(InfoExtractor):
|
||||||
IE_DESC = '斗鱼'
|
IE_DESC = '斗鱼'
|
||||||
_VALID_URL = r'https?://(?:www\.)?douyu(?:tv)?\.com/(?P<id>[A-Za-z0-9]+)'
|
_VALID_URL = r'https?://(?:www\.)?douyu(?:tv)?\.com/(?:[^/]+/)*(?P<id>[A-Za-z0-9]+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.douyutv.com/iseven',
|
'url': 'http://www.douyutv.com/iseven',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@ -26,8 +26,8 @@ class DouyuTVIE(InfoExtractor):
|
|||||||
'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:^清晨醒脑!T-ara根本停不下来! [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
|
||||||
'description': 're:.*m7show@163\.com.*',
|
'description': r're:.*m7show@163\.com.*',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'uploader': '7师傅',
|
'uploader': '7师傅',
|
||||||
'is_live': True,
|
'is_live': True,
|
||||||
},
|
},
|
||||||
@ -42,7 +42,7 @@ class DouyuTVIE(InfoExtractor):
|
|||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
'title': 're:^小漠从零单排记!——CSOL2躲猫猫 [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
|
'title': 're:^小漠从零单排记!——CSOL2躲猫猫 [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
|
||||||
'description': 'md5:746a2f7a253966a06755a912f0acc0d2',
|
'description': 'md5:746a2f7a253966a06755a912f0acc0d2',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'uploader': 'douyu小漠',
|
'uploader': 'douyu小漠',
|
||||||
'is_live': True,
|
'is_live': True,
|
||||||
},
|
},
|
||||||
@ -57,8 +57,8 @@ class DouyuTVIE(InfoExtractor):
|
|||||||
'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:^清晨醒脑!T-ara根本停不下来! [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
|
||||||
'description': 're:.*m7show@163\.com.*',
|
'description': r're:.*m7show@163\.com.*',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'uploader': '7师傅',
|
'uploader': '7师傅',
|
||||||
'is_live': True,
|
'is_live': True,
|
||||||
},
|
},
|
||||||
@ -68,6 +68,10 @@ class DouyuTVIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://www.douyu.com/xiaocang',
|
'url': 'http://www.douyu.com/xiaocang',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# \"room_id\"
|
||||||
|
'url': 'http://www.douyu.com/t/lpl',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
# Decompile core.swf in webpage by ffdec "Search SWFs in memory". core.swf
|
# Decompile core.swf in webpage by ffdec "Search SWFs in memory". core.swf
|
||||||
@ -82,7 +86,7 @@ class DouyuTVIE(InfoExtractor):
|
|||||||
else:
|
else:
|
||||||
page = self._download_webpage(url, video_id)
|
page = self._download_webpage(url, video_id)
|
||||||
room_id = self._html_search_regex(
|
room_id = self._html_search_regex(
|
||||||
r'"room_id"\s*:\s*(\d+),', page, 'room id')
|
r'"room_id\\?"\s*:\s*(\d+),', page, 'room id')
|
||||||
|
|
||||||
room = self._download_json(
|
room = self._download_json(
|
||||||
'http://m.douyu.com/html5/live?roomId=%s' % room_id, video_id,
|
'http://m.douyu.com/html5/live?roomId=%s' % room_id, video_id,
|
||||||
|
@ -66,7 +66,7 @@ class DramaFeverBaseIE(AMPIE):
|
|||||||
|
|
||||||
class DramaFeverIE(DramaFeverBaseIE):
|
class DramaFeverIE(DramaFeverBaseIE):
|
||||||
IE_NAME = 'dramafever'
|
IE_NAME = 'dramafever'
|
||||||
_VALID_URL = r'https?://(?:www\.)?dramafever\.com/drama/(?P<id>[0-9]+/[0-9]+)(?:/|$)'
|
_VALID_URL = r'https?://(?:www\.)?dramafever\.com/(?:[^/]+/)?drama/(?P<id>[0-9]+/[0-9]+)(?:/|$)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.dramafever.com/drama/4512/1/Cooking_with_Shin/',
|
'url': 'http://www.dramafever.com/drama/4512/1/Cooking_with_Shin/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@ -76,7 +76,7 @@ class DramaFeverIE(DramaFeverBaseIE):
|
|||||||
'description': 'md5:a8eec7942e1664a6896fcd5e1287bfd0',
|
'description': 'md5:a8eec7942e1664a6896fcd5e1287bfd0',
|
||||||
'episode': 'Episode 1',
|
'episode': 'Episode 1',
|
||||||
'episode_number': 1,
|
'episode_number': 1,
|
||||||
'thumbnail': 're:^https?://.*\.jpg',
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
'timestamp': 1404336058,
|
'timestamp': 1404336058,
|
||||||
'upload_date': '20140702',
|
'upload_date': '20140702',
|
||||||
'duration': 343,
|
'duration': 343,
|
||||||
@ -94,7 +94,7 @@ class DramaFeverIE(DramaFeverBaseIE):
|
|||||||
'description': 'md5:3ff2ee8fedaef86e076791c909cf2e91',
|
'description': 'md5:3ff2ee8fedaef86e076791c909cf2e91',
|
||||||
'episode': 'Mnet Asian Music Awards 2015 - Part 3',
|
'episode': 'Mnet Asian Music Awards 2015 - Part 3',
|
||||||
'episode_number': 4,
|
'episode_number': 4,
|
||||||
'thumbnail': 're:^https?://.*\.jpg',
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
'timestamp': 1450213200,
|
'timestamp': 1450213200,
|
||||||
'upload_date': '20151215',
|
'upload_date': '20151215',
|
||||||
'duration': 5602,
|
'duration': 5602,
|
||||||
@ -103,6 +103,9 @@ class DramaFeverIE(DramaFeverBaseIE):
|
|||||||
# m3u8 download
|
# m3u8 download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.dramafever.com/zh-cn/drama/4972/15/Doctor_Romantic/',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
@ -148,7 +151,7 @@ class DramaFeverIE(DramaFeverBaseIE):
|
|||||||
|
|
||||||
class DramaFeverSeriesIE(DramaFeverBaseIE):
|
class DramaFeverSeriesIE(DramaFeverBaseIE):
|
||||||
IE_NAME = 'dramafever:series'
|
IE_NAME = 'dramafever:series'
|
||||||
_VALID_URL = r'https?://(?:www\.)?dramafever\.com/drama/(?P<id>[0-9]+)(?:/(?:(?!\d+(?:/|$)).+)?)?$'
|
_VALID_URL = r'https?://(?:www\.)?dramafever\.com/(?:[^/]+/)?drama/(?P<id>[0-9]+)(?:/(?:(?!\d+(?:/|$)).+)?)?$'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.dramafever.com/drama/4512/Cooking_with_Shin/',
|
'url': 'http://www.dramafever.com/drama/4512/Cooking_with_Shin/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
|
@ -20,7 +20,7 @@ class DRBonanzaIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Talkshowet - Leonard Cohen',
|
'title': 'Talkshowet - Leonard Cohen',
|
||||||
'description': 'md5:8f34194fb30cd8c8a30ad8b27b70c0ca',
|
'description': 'md5:8f34194fb30cd8c8a30ad8b27b70c0ca',
|
||||||
'thumbnail': 're:^https?://.*\.(?:gif|jpg)$',
|
'thumbnail': r're:^https?://.*\.(?:gif|jpg)$',
|
||||||
'timestamp': 1295537932,
|
'timestamp': 1295537932,
|
||||||
'upload_date': '20110120',
|
'upload_date': '20110120',
|
||||||
'duration': 3664,
|
'duration': 3664,
|
||||||
@ -36,7 +36,7 @@ class DRBonanzaIE(InfoExtractor):
|
|||||||
'ext': 'mp3',
|
'ext': 'mp3',
|
||||||
'title': 'EM fodbold 1992 Danmark - Tyskland finale Transmission',
|
'title': 'EM fodbold 1992 Danmark - Tyskland finale Transmission',
|
||||||
'description': 'md5:501e5a195749480552e214fbbed16c4e',
|
'description': 'md5:501e5a195749480552e214fbbed16c4e',
|
||||||
'thumbnail': 're:^https?://.*\.(?:gif|jpg)$',
|
'thumbnail': r're:^https?://.*\.(?:gif|jpg)$',
|
||||||
'timestamp': 1223274900,
|
'timestamp': 1223274900,
|
||||||
'upload_date': '20081006',
|
'upload_date': '20081006',
|
||||||
'duration': 7369,
|
'duration': 7369,
|
||||||
|
@ -2,10 +2,19 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .zdf import ZDFIE
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
int_or_none,
|
||||||
|
unified_strdate,
|
||||||
|
xpath_text,
|
||||||
|
determine_ext,
|
||||||
|
qualities,
|
||||||
|
float_or_none,
|
||||||
|
ExtractorError,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DreiSatIE(ZDFIE):
|
class DreiSatIE(InfoExtractor):
|
||||||
IE_NAME = '3sat'
|
IE_NAME = '3sat'
|
||||||
_VALID_URL = r'(?:https?://)?(?:www\.)?3sat\.de/mediathek/(?:index\.php|mediathek\.php)?\?(?:(?:mode|display)=[^&]+&)*obj=(?P<id>[0-9]+)$'
|
_VALID_URL = r'(?:https?://)?(?:www\.)?3sat\.de/mediathek/(?:index\.php|mediathek\.php)?\?(?:(?:mode|display)=[^&]+&)*obj=(?P<id>[0-9]+)$'
|
||||||
_TESTS = [
|
_TESTS = [
|
||||||
@ -31,6 +40,163 @@ class DreiSatIE(ZDFIE):
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def _parse_smil_formats(self, smil, smil_url, video_id, namespace=None, f4m_params=None, transform_rtmp_url=None):
|
||||||
|
param_groups = {}
|
||||||
|
for param_group in smil.findall(self._xpath_ns('./head/paramGroup', namespace)):
|
||||||
|
group_id = param_group.attrib.get(self._xpath_ns('id', 'http://www.w3.org/XML/1998/namespace'))
|
||||||
|
params = {}
|
||||||
|
for param in param_group:
|
||||||
|
params[param.get('name')] = param.get('value')
|
||||||
|
param_groups[group_id] = params
|
||||||
|
|
||||||
|
formats = []
|
||||||
|
for video in smil.findall(self._xpath_ns('.//video', namespace)):
|
||||||
|
src = video.get('src')
|
||||||
|
if not src:
|
||||||
|
continue
|
||||||
|
bitrate = float_or_none(video.get('system-bitrate') or video.get('systemBitrate'), 1000)
|
||||||
|
group_id = video.get('paramGroup')
|
||||||
|
param_group = param_groups[group_id]
|
||||||
|
for proto in param_group['protocols'].split(','):
|
||||||
|
formats.append({
|
||||||
|
'url': '%s://%s' % (proto, param_group['host']),
|
||||||
|
'app': param_group['app'],
|
||||||
|
'play_path': src,
|
||||||
|
'ext': 'flv',
|
||||||
|
'format_id': '%s-%d' % (proto, bitrate),
|
||||||
|
'tbr': bitrate,
|
||||||
|
})
|
||||||
|
self._sort_formats(formats)
|
||||||
|
return formats
|
||||||
|
|
||||||
|
def extract_from_xml_url(self, video_id, xml_url):
|
||||||
|
doc = self._download_xml(
|
||||||
|
xml_url, video_id,
|
||||||
|
note='Downloading video info',
|
||||||
|
errnote='Failed to download video info')
|
||||||
|
|
||||||
|
status_code = doc.find('./status/statuscode')
|
||||||
|
if status_code is not None and status_code.text != 'ok':
|
||||||
|
code = status_code.text
|
||||||
|
if code == 'notVisibleAnymore':
|
||||||
|
message = 'Video %s is not available' % video_id
|
||||||
|
else:
|
||||||
|
message = '%s returned error: %s' % (self.IE_NAME, code)
|
||||||
|
raise ExtractorError(message, expected=True)
|
||||||
|
|
||||||
|
title = doc.find('.//information/title').text
|
||||||
|
description = xpath_text(doc, './/information/detail', 'description')
|
||||||
|
duration = int_or_none(xpath_text(doc, './/details/lengthSec', 'duration'))
|
||||||
|
uploader = xpath_text(doc, './/details/originChannelTitle', 'uploader')
|
||||||
|
uploader_id = xpath_text(doc, './/details/originChannelId', 'uploader id')
|
||||||
|
upload_date = unified_strdate(xpath_text(doc, './/details/airtime', 'upload date'))
|
||||||
|
|
||||||
|
def xml_to_thumbnails(fnode):
|
||||||
|
thumbnails = []
|
||||||
|
for node in fnode:
|
||||||
|
thumbnail_url = node.text
|
||||||
|
if not thumbnail_url:
|
||||||
|
continue
|
||||||
|
thumbnail = {
|
||||||
|
'url': thumbnail_url,
|
||||||
|
}
|
||||||
|
if 'key' in node.attrib:
|
||||||
|
m = re.match('^([0-9]+)x([0-9]+)$', node.attrib['key'])
|
||||||
|
if m:
|
||||||
|
thumbnail['width'] = int(m.group(1))
|
||||||
|
thumbnail['height'] = int(m.group(2))
|
||||||
|
thumbnails.append(thumbnail)
|
||||||
|
return thumbnails
|
||||||
|
|
||||||
|
thumbnails = xml_to_thumbnails(doc.findall('.//teaserimages/teaserimage'))
|
||||||
|
|
||||||
|
format_nodes = doc.findall('.//formitaeten/formitaet')
|
||||||
|
quality = qualities(['veryhigh', 'high', 'med', 'low'])
|
||||||
|
|
||||||
|
def get_quality(elem):
|
||||||
|
return quality(xpath_text(elem, 'quality'))
|
||||||
|
format_nodes.sort(key=get_quality)
|
||||||
|
format_ids = []
|
||||||
|
formats = []
|
||||||
|
for fnode in format_nodes:
|
||||||
|
video_url = fnode.find('url').text
|
||||||
|
is_available = 'http://www.metafilegenerator' not in video_url
|
||||||
|
if not is_available:
|
||||||
|
continue
|
||||||
|
format_id = fnode.attrib['basetype']
|
||||||
|
quality = xpath_text(fnode, './quality', 'quality')
|
||||||
|
format_m = re.match(r'''(?x)
|
||||||
|
(?P<vcodec>[^_]+)_(?P<acodec>[^_]+)_(?P<container>[^_]+)_
|
||||||
|
(?P<proto>[^_]+)_(?P<index>[^_]+)_(?P<indexproto>[^_]+)
|
||||||
|
''', format_id)
|
||||||
|
|
||||||
|
ext = determine_ext(video_url, None) or format_m.group('container')
|
||||||
|
if ext not in ('smil', 'f4m', 'm3u8'):
|
||||||
|
format_id = format_id + '-' + quality
|
||||||
|
if format_id in format_ids:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ext == 'meta':
|
||||||
|
continue
|
||||||
|
elif ext == 'smil':
|
||||||
|
formats.extend(self._extract_smil_formats(
|
||||||
|
video_url, video_id, fatal=False))
|
||||||
|
elif ext == 'm3u8':
|
||||||
|
# the certificates are misconfigured (see
|
||||||
|
# https://github.com/rg3/youtube-dl/issues/8665)
|
||||||
|
if video_url.startswith('https://'):
|
||||||
|
continue
|
||||||
|
formats.extend(self._extract_m3u8_formats(
|
||||||
|
video_url, video_id, 'mp4', m3u8_id=format_id, fatal=False))
|
||||||
|
elif ext == 'f4m':
|
||||||
|
formats.extend(self._extract_f4m_formats(
|
||||||
|
video_url, video_id, f4m_id=format_id, fatal=False))
|
||||||
|
else:
|
||||||
|
proto = format_m.group('proto').lower()
|
||||||
|
|
||||||
|
abr = int_or_none(xpath_text(fnode, './audioBitrate', 'abr'), 1000)
|
||||||
|
vbr = int_or_none(xpath_text(fnode, './videoBitrate', 'vbr'), 1000)
|
||||||
|
|
||||||
|
width = int_or_none(xpath_text(fnode, './width', 'width'))
|
||||||
|
height = int_or_none(xpath_text(fnode, './height', 'height'))
|
||||||
|
|
||||||
|
filesize = int_or_none(xpath_text(fnode, './filesize', 'filesize'))
|
||||||
|
|
||||||
|
format_note = ''
|
||||||
|
if not format_note:
|
||||||
|
format_note = None
|
||||||
|
|
||||||
|
formats.append({
|
||||||
|
'format_id': format_id,
|
||||||
|
'url': video_url,
|
||||||
|
'ext': ext,
|
||||||
|
'acodec': format_m.group('acodec'),
|
||||||
|
'vcodec': format_m.group('vcodec'),
|
||||||
|
'abr': abr,
|
||||||
|
'vbr': vbr,
|
||||||
|
'width': width,
|
||||||
|
'height': height,
|
||||||
|
'filesize': filesize,
|
||||||
|
'format_note': format_note,
|
||||||
|
'protocol': proto,
|
||||||
|
'_available': is_available,
|
||||||
|
})
|
||||||
|
format_ids.append(format_id)
|
||||||
|
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': title,
|
||||||
|
'description': description,
|
||||||
|
'duration': duration,
|
||||||
|
'thumbnails': thumbnails,
|
||||||
|
'uploader': uploader,
|
||||||
|
'uploader_id': uploader_id,
|
||||||
|
'upload_date': upload_date,
|
||||||
|
'formats': formats,
|
||||||
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
video_id = mobj.group('id')
|
video_id = mobj.group('id')
|
||||||
|
@ -22,7 +22,7 @@ class DrTuberIE(InfoExtractor):
|
|||||||
'like_count': int,
|
'like_count': int,
|
||||||
'comment_count': int,
|
'comment_count': int,
|
||||||
'categories': ['Babe', 'Blonde', 'Erotic', 'Outdoor', 'Softcore', 'Solo'],
|
'categories': ['Babe', 'Blonde', 'Erotic', 'Outdoor', 'Softcore', 'Solo'],
|
||||||
'thumbnail': 're:https?://.*\.jpg$',
|
'thumbnail': r're:https?://.*\.jpg$',
|
||||||
'age_limit': 18,
|
'age_limit': 18,
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
@ -9,12 +9,13 @@ from ..utils import (
|
|||||||
mimetype2ext,
|
mimetype2ext,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
remove_end,
|
remove_end,
|
||||||
|
update_url_query,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DRTVIE(InfoExtractor):
|
class DRTVIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?dr\.dk/(?:tv/se|nyheder)/(?:[^/]+/)*(?P<id>[\da-z-]+)(?:[/#?]|$)'
|
_VALID_URL = r'https?://(?:www\.)?dr\.dk/(?:tv/se|nyheder|radio/ondemand)/(?:[^/]+/)*(?P<id>[\da-z-]+)(?:[/#?]|$)'
|
||||||
|
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': '25e659cccc9a2ed956110a299fdf5983',
|
||||||
@ -79,9 +80,10 @@ class DRTVIE(InfoExtractor):
|
|||||||
subtitles = {}
|
subtitles = {}
|
||||||
|
|
||||||
for asset in data['Assets']:
|
for asset in data['Assets']:
|
||||||
if asset.get('Kind') == 'Image':
|
kind = asset.get('Kind')
|
||||||
|
if kind == 'Image':
|
||||||
thumbnail = asset.get('Uri')
|
thumbnail = asset.get('Uri')
|
||||||
elif asset.get('Kind') == 'VideoResource':
|
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'
|
spoken_subtitles = asset.get('Target') == 'SpokenSubtitles'
|
||||||
@ -96,9 +98,13 @@ class DRTVIE(InfoExtractor):
|
|||||||
preference = -1
|
preference = -1
|
||||||
format_id += '-spoken-subtitles'
|
format_id += '-spoken-subtitles'
|
||||||
if target == 'HDS':
|
if target == 'HDS':
|
||||||
formats.extend(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',
|
||||||
video_id, preference, f4m_id=format_id))
|
video_id, preference, f4m_id=format_id)
|
||||||
|
if kind == 'AudioResource':
|
||||||
|
for f in f4m_formats:
|
||||||
|
f['vcodec'] = 'none'
|
||||||
|
formats.extend(f4m_formats)
|
||||||
elif target == 'HLS':
|
elif target == 'HLS':
|
||||||
formats.extend(self._extract_m3u8_formats(
|
formats.extend(self._extract_m3u8_formats(
|
||||||
uri, video_id, 'mp4', entry_protocol='m3u8_native',
|
uri, video_id, 'mp4', entry_protocol='m3u8_native',
|
||||||
@ -112,6 +118,7 @@ class DRTVIE(InfoExtractor):
|
|||||||
'format_id': format_id,
|
'format_id': format_id,
|
||||||
'tbr': int_or_none(bitrate),
|
'tbr': int_or_none(bitrate),
|
||||||
'ext': link.get('FileFormat'),
|
'ext': link.get('FileFormat'),
|
||||||
|
'vcodec': 'none' if kind == 'AudioResource' else None,
|
||||||
})
|
})
|
||||||
subtitles_list = asset.get('SubtitlesList')
|
subtitles_list = asset.get('SubtitlesList')
|
||||||
if isinstance(subtitles_list, list):
|
if isinstance(subtitles_list, list):
|
||||||
@ -144,3 +151,58 @@ class DRTVIE(InfoExtractor):
|
|||||||
'formats': formats,
|
'formats': formats,
|
||||||
'subtitles': subtitles,
|
'subtitles': subtitles,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DRTVLiveIE(InfoExtractor):
|
||||||
|
IE_NAME = 'drtv:live'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?dr\.dk/(?:tv|TV)/live/(?P<id>[\da-z-]+)'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'https://www.dr.dk/tv/live/dr1',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'dr1',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 're:^DR1 [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
channel_id = self._match_id(url)
|
||||||
|
channel_data = self._download_json(
|
||||||
|
'https://www.dr.dk/mu-online/api/1.0/channel/' + channel_id,
|
||||||
|
channel_id)
|
||||||
|
title = self._live_title(channel_data['Title'])
|
||||||
|
|
||||||
|
formats = []
|
||||||
|
for streaming_server in channel_data.get('StreamingServers', []):
|
||||||
|
server = streaming_server.get('Server')
|
||||||
|
if not server:
|
||||||
|
continue
|
||||||
|
link_type = streaming_server.get('LinkType')
|
||||||
|
for quality in streaming_server.get('Qualities', []):
|
||||||
|
for stream in quality.get('Streams', []):
|
||||||
|
stream_path = stream.get('Stream')
|
||||||
|
if not stream_path:
|
||||||
|
continue
|
||||||
|
stream_url = update_url_query(
|
||||||
|
'%s/%s' % (server, stream_path), {'b': ''})
|
||||||
|
if link_type == 'HLS':
|
||||||
|
formats.extend(self._extract_m3u8_formats(
|
||||||
|
stream_url, channel_id, 'mp4',
|
||||||
|
m3u8_id=link_type, fatal=False, live=True))
|
||||||
|
elif link_type == 'HDS':
|
||||||
|
formats.extend(self._extract_f4m_formats(update_url_query(
|
||||||
|
'%s/%s' % (server, stream_path), {'hdcore': '3.7.0'}),
|
||||||
|
channel_id, f4m_id=link_type, fatal=False))
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': channel_id,
|
||||||
|
'title': title,
|
||||||
|
'thumbnail': channel_data.get('PrimaryImageUri'),
|
||||||
|
'formats': formats,
|
||||||
|
'is_live': True,
|
||||||
|
}
|
||||||
|
@ -21,7 +21,7 @@ class DumpertIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Ik heb nieuws voor je',
|
'title': 'Ik heb nieuws voor je',
|
||||||
'description': 'Niet schrikken hoor',
|
'description': 'Niet schrikken hoor',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.dumpert.nl/embed/6675421/dc440fe7/',
|
'url': 'http://www.dumpert.nl/embed/6675421/dc440fe7/',
|
||||||
|
@ -31,7 +31,7 @@ class EaglePlatformIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Навальный вышел на свободу',
|
'title': 'Навальный вышел на свободу',
|
||||||
'description': 'md5:d97861ac9ae77377f3f20eaf9d04b4f5',
|
'description': 'md5:d97861ac9ae77377f3f20eaf9d04b4f5',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 87,
|
'duration': 87,
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
'age_limit': 0,
|
'age_limit': 0,
|
||||||
@ -45,7 +45,7 @@ class EaglePlatformIE(InfoExtractor):
|
|||||||
'id': '12820',
|
'id': '12820',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': "'O Sole Mio",
|
'title': "'O Sole Mio",
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 216,
|
'duration': 216,
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
},
|
},
|
||||||
|
39
youtube_dl/extractor/egghead.py
Normal file
39
youtube_dl/extractor/egghead.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
|
||||||
|
|
||||||
|
class EggheadCourseIE(InfoExtractor):
|
||||||
|
IE_DESC = 'egghead.io course'
|
||||||
|
IE_NAME = 'egghead:course'
|
||||||
|
_VALID_URL = r'https://egghead\.io/courses/(?P<id>[a-zA-Z_0-9-]+)'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'https://egghead.io/courses/professor-frisby-introduces-composable-functional-javascript',
|
||||||
|
'playlist_count': 29,
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'professor-frisby-introduces-composable-functional-javascript',
|
||||||
|
'title': 'Professor Frisby Introduces Composable Functional JavaScript',
|
||||||
|
'description': 're:(?s)^This course teaches the ubiquitous.*You\'ll start composing functionality before you know it.$',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
playlist_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, playlist_id)
|
||||||
|
|
||||||
|
title = self._html_search_regex(r'<h1 class="title">([^<]+)</h1>', webpage, 'title')
|
||||||
|
ul = self._search_regex(r'(?s)<ul class="series-lessons-list">(.*?)</ul>', webpage, 'session list')
|
||||||
|
|
||||||
|
found = re.findall(r'(?s)<a class="[^"]*"\s*href="([^"]+)">\s*<li class="item', ul)
|
||||||
|
entries = [self.url_result(m) for m in found]
|
||||||
|
|
||||||
|
return {
|
||||||
|
'_type': 'playlist',
|
||||||
|
'id': playlist_id,
|
||||||
|
'title': title,
|
||||||
|
'description': self._og_search_description(webpage),
|
||||||
|
'entries': entries,
|
||||||
|
}
|
@ -19,7 +19,7 @@ class EinthusanIE(InfoExtractor):
|
|||||||
'id': '2447',
|
'id': '2447',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Ek Villain',
|
'title': 'Ek Villain',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'description': 'md5:9d29fc91a7abadd4591fb862fa560d93',
|
'description': 'md5:9d29fc91a7abadd4591fb862fa560d93',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -30,7 +30,7 @@ class EinthusanIE(InfoExtractor):
|
|||||||
'id': '1671',
|
'id': '1671',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Soodhu Kavvuum',
|
'title': 'Soodhu Kavvuum',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'description': 'md5:b40f2bf7320b4f9414f3780817b2af8c',
|
'description': 'md5:b40f2bf7320b4f9414f3780817b2af8c',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import unified_strdate
|
from ..utils import strip_jsonp, unified_strdate
|
||||||
|
|
||||||
|
|
||||||
class ElPaisIE(InfoExtractor):
|
class ElPaisIE(InfoExtractor):
|
||||||
@ -29,6 +29,16 @@ class ElPaisIE(InfoExtractor):
|
|||||||
'description': 'Que sí, que las cápsulas son cómodas. Pero si le pides algo más a la vida, quizá deberías aprender a usar bien la cafetera italiana. No tienes más que ver este vídeo y seguir sus siete normas básicas.',
|
'description': 'Que sí, que las cápsulas son cómodas. Pero si le pides algo más a la vida, quizá deberías aprender a usar bien la cafetera italiana. No tienes más que ver este vídeo y seguir sus siete normas básicas.',
|
||||||
'upload_date': '20160303',
|
'upload_date': '20160303',
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
'url': 'http://elpais.com/elpais/2017/01/26/ciencia/1485456786_417876.html',
|
||||||
|
'md5': '9c79923a118a067e1a45789e1e0b0f9c',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1485456786_417876',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Hallado un barco de la antigua Roma que naufragó en Baleares hace 1.800 años',
|
||||||
|
'description': 'La nave portaba cientos de ánforas y se hundió cerca de la isla de Cabrera por razones desconocidas',
|
||||||
|
'upload_date': '20170127',
|
||||||
|
},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
@ -37,6 +47,13 @@ class ElPaisIE(InfoExtractor):
|
|||||||
|
|
||||||
prefix = self._html_search_regex(
|
prefix = self._html_search_regex(
|
||||||
r'var\s+url_cache\s*=\s*"([^"]+)";', webpage, 'URL prefix')
|
r'var\s+url_cache\s*=\s*"([^"]+)";', webpage, 'URL prefix')
|
||||||
|
id_multimedia = self._search_regex(
|
||||||
|
r"id_multimedia\s*=\s*'([^']+)'", webpage, 'ID multimedia', default=None)
|
||||||
|
if id_multimedia:
|
||||||
|
url_info = self._download_json(
|
||||||
|
'http://elpais.com/vdpep/1/?pepid=' + id_multimedia, video_id, transform_source=strip_jsonp)
|
||||||
|
video_suffix = url_info['mp4']
|
||||||
|
else:
|
||||||
video_suffix = self._search_regex(
|
video_suffix = self._search_regex(
|
||||||
r"(?:URLMediaFile|urlVideo_\d+)\s*=\s*url_cache\s*\+\s*'([^']+)'", webpage, 'video URL')
|
r"(?:URLMediaFile|urlVideo_\d+)\s*=\s*url_cache\s*\+\s*'([^']+)'", webpage, 'video URL')
|
||||||
video_url = prefix + video_suffix
|
video_url = prefix + video_suffix
|
||||||
|
@ -22,7 +22,7 @@ class EroProfileIE(InfoExtractor):
|
|||||||
'display_id': 'sexy-babe-softcore',
|
'display_id': 'sexy-babe-softcore',
|
||||||
'ext': 'm4v',
|
'ext': 'm4v',
|
||||||
'title': 'sexy babe softcore',
|
'title': 'sexy babe softcore',
|
||||||
'thumbnail': 're:https?://.*\.jpg',
|
'thumbnail': r're:https?://.*\.jpg',
|
||||||
'age_limit': 18,
|
'age_limit': 18,
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
@ -32,7 +32,7 @@ class EroProfileIE(InfoExtractor):
|
|||||||
'id': '1133519',
|
'id': '1133519',
|
||||||
'ext': 'm4v',
|
'ext': 'm4v',
|
||||||
'title': 'Try It On Pee_cut_2.wmv - 4shared.com - file sharing - download movie file',
|
'title': 'Try It On Pee_cut_2.wmv - 4shared.com - file sharing - download movie file',
|
||||||
'thumbnail': 're:https?://.*\.jpg',
|
'thumbnail': r're:https?://.*\.jpg',
|
||||||
'age_limit': 18,
|
'age_limit': 18,
|
||||||
},
|
},
|
||||||
'skip': 'Requires login',
|
'skip': 'Requires login',
|
||||||
|
@ -45,7 +45,7 @@ class EscapistIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'description': "Baldur's Gate: Original, Modded or Enhanced Edition? I'll break down what you can expect from the new Baldur's Gate: Enhanced Edition.",
|
'description': "Baldur's Gate: Original, Modded or Enhanced Edition? I'll break down what you can expect from the new Baldur's Gate: Enhanced Edition.",
|
||||||
'title': "Breaking Down Baldur's Gate",
|
'title': "Breaking Down Baldur's Gate",
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 264,
|
'duration': 264,
|
||||||
'uploader': 'The Escapist',
|
'uploader': 'The Escapist',
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ class EscapistIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'description': 'This week, Zero Punctuation reviews Evolve.',
|
'description': 'This week, Zero Punctuation reviews Evolve.',
|
||||||
'title': 'Evolve - One vs Multiplayer',
|
'title': 'Evolve - One vs Multiplayer',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 304,
|
'duration': 304,
|
||||||
'uploader': 'The Escapist',
|
'uploader': 'The Escapist',
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ class EsriVideoIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'ArcGIS Online - Developing Applications',
|
'title': 'ArcGIS Online - Developing Applications',
|
||||||
'description': 'Jeremy Bartley demonstrates how to develop applications with ArcGIS Online.',
|
'description': 'Jeremy Bartley demonstrates how to develop applications with ArcGIS Online.',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 185,
|
'duration': 185,
|
||||||
'upload_date': '20120419',
|
'upload_date': '20120419',
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ class EuropaIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'TRADE - Wikileaks on TTIP',
|
'title': 'TRADE - Wikileaks on TTIP',
|
||||||
'description': 'NEW LIVE EC Midday press briefing of 11/08/2015',
|
'description': 'NEW LIVE EC Midday press briefing of 11/08/2015',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'upload_date': '20150811',
|
'upload_date': '20150811',
|
||||||
'duration': 34,
|
'duration': 34,
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
|
@ -17,7 +17,7 @@ class ExpoTVIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'NYX Butter Lipstick Little Susie',
|
'title': 'NYX Butter Lipstick Little Susie',
|
||||||
'description': 'Goes on like butter, but looks better!',
|
'description': 'Goes on like butter, but looks better!',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'uploader': 'Stephanie S.',
|
'uploader': 'Stephanie S.',
|
||||||
'upload_date': '20150520',
|
'upload_date': '20150520',
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
|
@ -30,7 +30,10 @@ from .aenetworks import (
|
|||||||
AENetworksIE,
|
AENetworksIE,
|
||||||
HistoryTopicIE,
|
HistoryTopicIE,
|
||||||
)
|
)
|
||||||
from .afreecatv import AfreecaTVIE
|
from .afreecatv import (
|
||||||
|
AfreecaTVIE,
|
||||||
|
AfreecaTVGlobalIE,
|
||||||
|
)
|
||||||
from .airmozilla import AirMozillaIE
|
from .airmozilla import AirMozillaIE
|
||||||
from .aljazeera import AlJazeeraIE
|
from .aljazeera import AlJazeeraIE
|
||||||
from .alphaporno import AlphaPornoIE
|
from .alphaporno import AlphaPornoIE
|
||||||
@ -38,10 +41,7 @@ from .amcnetworks import AMCNetworksIE
|
|||||||
from .animeondemand import AnimeOnDemandIE
|
from .animeondemand import AnimeOnDemandIE
|
||||||
from .anitube import AnitubeIE
|
from .anitube import AnitubeIE
|
||||||
from .anysex import AnySexIE
|
from .anysex import AnySexIE
|
||||||
from .aol import (
|
from .aol import AolIE
|
||||||
AolIE,
|
|
||||||
AolFeaturesIE,
|
|
||||||
)
|
|
||||||
from .allocine import AllocineIE
|
from .allocine import AllocineIE
|
||||||
from .aparat import AparatIE
|
from .aparat import AparatIE
|
||||||
from .appleconnect import AppleConnectIE
|
from .appleconnect import AppleConnectIE
|
||||||
@ -80,6 +80,10 @@ from .awaan import (
|
|||||||
AWAANLiveIE,
|
AWAANLiveIE,
|
||||||
AWAANSeasonIE,
|
AWAANSeasonIE,
|
||||||
)
|
)
|
||||||
|
from .azmedien import (
|
||||||
|
AZMedienIE,
|
||||||
|
AZMedienPlaylistIE,
|
||||||
|
)
|
||||||
from .azubu import AzubuIE, AzubuLiveIE
|
from .azubu import AzubuIE, AzubuLiveIE
|
||||||
from .baidu import BaiduVideoIE
|
from .baidu import BaiduVideoIE
|
||||||
from .bambuser import BambuserIE, BambuserChannelIE
|
from .bambuser import BambuserIE, BambuserChannelIE
|
||||||
@ -91,6 +95,7 @@ from .bbc import (
|
|||||||
BBCCoUkPlaylistIE,
|
BBCCoUkPlaylistIE,
|
||||||
BBCIE,
|
BBCIE,
|
||||||
)
|
)
|
||||||
|
from .beampro import BeamProLiveIE
|
||||||
from .beeg import BeegIE
|
from .beeg import BeegIE
|
||||||
from .behindkink import BehindKinkIE
|
from .behindkink import BehindKinkIE
|
||||||
from .bellmedia import BellMediaIE
|
from .bellmedia import BellMediaIE
|
||||||
@ -98,7 +103,10 @@ from .beatport import BeatportIE
|
|||||||
from .bet import BetIE
|
from .bet import BetIE
|
||||||
from .bigflix import BigflixIE
|
from .bigflix import BigflixIE
|
||||||
from .bild import BildIE
|
from .bild import BildIE
|
||||||
from .bilibili import BiliBiliIE
|
from .bilibili import (
|
||||||
|
BiliBiliIE,
|
||||||
|
BiliBiliBangumiIE,
|
||||||
|
)
|
||||||
from .biobiochiletv import BioBioChileTVIE
|
from .biobiochiletv import BioBioChileTVIE
|
||||||
from .biqle import BIQLEIE
|
from .biqle import BIQLEIE
|
||||||
from .bleacherreport import (
|
from .bleacherreport import (
|
||||||
@ -240,12 +248,16 @@ from .dramafever import (
|
|||||||
from .dreisat import DreiSatIE
|
from .dreisat import DreiSatIE
|
||||||
from .drbonanza import DRBonanzaIE
|
from .drbonanza import DRBonanzaIE
|
||||||
from .drtuber import DrTuberIE
|
from .drtuber import DrTuberIE
|
||||||
from .drtv import DRTVIE
|
from .drtv import (
|
||||||
|
DRTVIE,
|
||||||
|
DRTVLiveIE,
|
||||||
|
)
|
||||||
from .dvtv import DVTVIE
|
from .dvtv import DVTVIE
|
||||||
from .dumpert import DumpertIE
|
from .dumpert import DumpertIE
|
||||||
from .defense import DefenseGouvFrIE
|
from .defense import DefenseGouvFrIE
|
||||||
from .discovery import DiscoveryIE
|
from .discovery import DiscoveryIE
|
||||||
from .discoverygo import DiscoveryGoIE
|
from .discoverygo import DiscoveryGoIE
|
||||||
|
from .disney import DisneyIE
|
||||||
from .dispeak import DigitallySpeakingIE
|
from .dispeak import DigitallySpeakingIE
|
||||||
from .dropbox import DropboxIE
|
from .dropbox import DropboxIE
|
||||||
from .dw import (
|
from .dw import (
|
||||||
@ -255,6 +267,7 @@ from .dw import (
|
|||||||
from .eagleplatform import EaglePlatformIE
|
from .eagleplatform import EaglePlatformIE
|
||||||
from .ebaumsworld import EbaumsWorldIE
|
from .ebaumsworld import EbaumsWorldIE
|
||||||
from .echomsk import EchoMskIE
|
from .echomsk import EchoMskIE
|
||||||
|
from .egghead import EggheadCourseIE
|
||||||
from .ehow import EHowIE
|
from .ehow import EHowIE
|
||||||
from .eighttracks import EightTracksIE
|
from .eighttracks import EightTracksIE
|
||||||
from .einthusan import EinthusanIE
|
from .einthusan import EinthusanIE
|
||||||
@ -289,6 +302,10 @@ from .fc2 import (
|
|||||||
FC2EmbedIE,
|
FC2EmbedIE,
|
||||||
)
|
)
|
||||||
from .fczenit import FczenitIE
|
from .fczenit import FczenitIE
|
||||||
|
from .filmon import (
|
||||||
|
FilmOnIE,
|
||||||
|
FilmOnChannelIE,
|
||||||
|
)
|
||||||
from .firstpost import FirstpostIE
|
from .firstpost import FirstpostIE
|
||||||
from .firsttv import FirstTVIE
|
from .firsttv import FirstTVIE
|
||||||
from .fivemin import FiveMinIE
|
from .fivemin import FiveMinIE
|
||||||
@ -332,6 +349,7 @@ from .gameone import (
|
|||||||
from .gamersyde import GamersydeIE
|
from .gamersyde import GamersydeIE
|
||||||
from .gamespot import GameSpotIE
|
from .gamespot import GameSpotIE
|
||||||
from .gamestar import GameStarIE
|
from .gamestar import GameStarIE
|
||||||
|
from .gaskrank import GaskrankIE
|
||||||
from .gazeta import GazetaIE
|
from .gazeta import GazetaIE
|
||||||
from .gdcvault import GDCVaultIE
|
from .gdcvault import GDCVaultIE
|
||||||
from .generic import GenericIE
|
from .generic import GenericIE
|
||||||
@ -369,6 +387,7 @@ from .hgtv import (
|
|||||||
)
|
)
|
||||||
from .historicfilms import HistoricFilmsIE
|
from .historicfilms import HistoricFilmsIE
|
||||||
from .hitbox import HitboxIE, HitboxLiveIE
|
from .hitbox import HitboxIE, HitboxLiveIE
|
||||||
|
from .hitrecord import HitRecordIE
|
||||||
from .hornbunny import HornBunnyIE
|
from .hornbunny import HornBunnyIE
|
||||||
from .hotnewhiphop import HotNewHipHopIE
|
from .hotnewhiphop import HotNewHipHopIE
|
||||||
from .hotstar import HotStarIE
|
from .hotstar import HotStarIE
|
||||||
@ -396,6 +415,7 @@ from .imgur import (
|
|||||||
ImgurAlbumIE,
|
ImgurAlbumIE,
|
||||||
)
|
)
|
||||||
from .ina import InaIE
|
from .ina import InaIE
|
||||||
|
from .inc import IncIE
|
||||||
from .indavideo import (
|
from .indavideo import (
|
||||||
IndavideoIE,
|
IndavideoIE,
|
||||||
IndavideoEmbedIE,
|
IndavideoEmbedIE,
|
||||||
@ -406,6 +426,7 @@ from .internetvideoarchive import InternetVideoArchiveIE
|
|||||||
from .iprima import IPrimaIE
|
from .iprima import IPrimaIE
|
||||||
from .iqiyi import IqiyiIE
|
from .iqiyi import IqiyiIE
|
||||||
from .ir90tv import Ir90TvIE
|
from .ir90tv import Ir90TvIE
|
||||||
|
from .itv import ITVIE
|
||||||
from .ivi import (
|
from .ivi import (
|
||||||
IviIE,
|
IviIE,
|
||||||
IviCompilationIE
|
IviCompilationIE
|
||||||
@ -544,6 +565,7 @@ from .mtv import (
|
|||||||
MTVVideoIE,
|
MTVVideoIE,
|
||||||
MTVServicesEmbeddedIE,
|
MTVServicesEmbeddedIE,
|
||||||
MTVDEIE,
|
MTVDEIE,
|
||||||
|
MTV81IE,
|
||||||
)
|
)
|
||||||
from .muenchentv import MuenchenTVIE
|
from .muenchentv import MuenchenTVIE
|
||||||
from .musicplayon import MusicPlayOnIE
|
from .musicplayon import MusicPlayOnIE
|
||||||
@ -593,6 +615,7 @@ from .nextmedia import (
|
|||||||
NextMediaIE,
|
NextMediaIE,
|
||||||
NextMediaActionNewsIE,
|
NextMediaActionNewsIE,
|
||||||
AppleDailyIE,
|
AppleDailyIE,
|
||||||
|
NextTVIE,
|
||||||
)
|
)
|
||||||
from .nfb import NFBIE
|
from .nfb import NFBIE
|
||||||
from .nfl import NFLIE
|
from .nfl import NFLIE
|
||||||
@ -656,6 +679,7 @@ from .nrk import (
|
|||||||
NRKTVIE,
|
NRKTVIE,
|
||||||
NRKTVDirekteIE,
|
NRKTVDirekteIE,
|
||||||
NRKTVEpisodesIE,
|
NRKTVEpisodesIE,
|
||||||
|
NRKTVSeriesIE,
|
||||||
)
|
)
|
||||||
from .ntvde import NTVDeIE
|
from .ntvde import NTVDeIE
|
||||||
from .ntvru import NTVRuIE
|
from .ntvru import NTVRuIE
|
||||||
@ -719,6 +743,7 @@ from .polskieradio import (
|
|||||||
)
|
)
|
||||||
from .porn91 import Porn91IE
|
from .porn91 import Porn91IE
|
||||||
from .porncom import PornComIE
|
from .porncom import PornComIE
|
||||||
|
from .pornflip import PornFlipIE
|
||||||
from .pornhd import PornHdIE
|
from .pornhd import PornHdIE
|
||||||
from .pornhub import (
|
from .pornhub import (
|
||||||
PornHubIE,
|
PornHubIE,
|
||||||
@ -813,7 +838,7 @@ from .sbs import SBSIE
|
|||||||
from .scivee import SciVeeIE
|
from .scivee import SciVeeIE
|
||||||
from .screencast import ScreencastIE
|
from .screencast import ScreencastIE
|
||||||
from .screencastomatic import ScreencastOMaticIE
|
from .screencastomatic import ScreencastOMaticIE
|
||||||
from .screenjunkies import ScreenJunkiesIE
|
from .scrippsnetworks import ScrippsNetworksWatchIE
|
||||||
from .seeker import SeekerIE
|
from .seeker import SeekerIE
|
||||||
from .senateisvp import SenateISVPIE
|
from .senateisvp import SenateISVPIE
|
||||||
from .sendtonews import SendtoNewsIE
|
from .sendtonews import SendtoNewsIE
|
||||||
@ -824,7 +849,6 @@ from .shared import (
|
|||||||
SharedIE,
|
SharedIE,
|
||||||
VivoIE,
|
VivoIE,
|
||||||
)
|
)
|
||||||
from .sharesix import ShareSixIE
|
|
||||||
from .showroomlive import ShowRoomLiveIE
|
from .showroomlive import ShowRoomLiveIE
|
||||||
from .sina import SinaIE
|
from .sina import SinaIE
|
||||||
from .sixplay import SixPlayIE
|
from .sixplay import SixPlayIE
|
||||||
@ -869,12 +893,10 @@ from .spiegeltv import SpiegeltvIE
|
|||||||
from .spike import SpikeIE
|
from .spike import SpikeIE
|
||||||
from .stitcher import StitcherIE
|
from .stitcher import StitcherIE
|
||||||
from .sport5 import Sport5IE
|
from .sport5 import Sport5IE
|
||||||
from .sportbox import (
|
from .sportbox import SportBoxEmbedIE
|
||||||
SportBoxIE,
|
|
||||||
SportBoxEmbedIE,
|
|
||||||
)
|
|
||||||
from .sportdeutschland import SportDeutschlandIE
|
from .sportdeutschland import SportDeutschlandIE
|
||||||
from .sportschau import SportschauIE
|
from .sportschau import SportschauIE
|
||||||
|
from .sprout import SproutIE
|
||||||
from .srgssr import (
|
from .srgssr import (
|
||||||
SRGSSRIE,
|
SRGSSRIE,
|
||||||
SRGSSRPlayIE,
|
SRGSSRPlayIE,
|
||||||
@ -976,6 +998,7 @@ from .tv2 import (
|
|||||||
)
|
)
|
||||||
from .tv3 import TV3IE
|
from .tv3 import TV3IE
|
||||||
from .tv4 import TV4IE
|
from .tv4 import TV4IE
|
||||||
|
from .tva import TVAIE
|
||||||
from .tvanouvelles import (
|
from .tvanouvelles import (
|
||||||
TVANouvellesIE,
|
TVANouvellesIE,
|
||||||
TVANouvellesArticleIE,
|
TVANouvellesArticleIE,
|
||||||
@ -1075,7 +1098,7 @@ from .videomore import (
|
|||||||
VideomoreSeasonIE,
|
VideomoreSeasonIE,
|
||||||
)
|
)
|
||||||
from .videopremium import VideoPremiumIE
|
from .videopremium import VideoPremiumIE
|
||||||
from .videott import VideoTtIE
|
from .videopress import VideoPressIE
|
||||||
from .vidio import VidioIE
|
from .vidio import VidioIE
|
||||||
from .vidme import (
|
from .vidme import (
|
||||||
VidmeIE,
|
VidmeIE,
|
||||||
@ -1120,7 +1143,10 @@ from .vk import (
|
|||||||
VKUserVideosIE,
|
VKUserVideosIE,
|
||||||
VKWallPostIE,
|
VKWallPostIE,
|
||||||
)
|
)
|
||||||
from .vlive import VLiveIE
|
from .vlive import (
|
||||||
|
VLiveIE,
|
||||||
|
VLiveChannelIE
|
||||||
|
)
|
||||||
from .vodlocker import VodlockerIE
|
from .vodlocker import VodlockerIE
|
||||||
from .vodplatform import VODPlatformIE
|
from .vodplatform import VODPlatformIE
|
||||||
from .voicerepublic import VoiceRepublicIE
|
from .voicerepublic import VoiceRepublicIE
|
||||||
|
@ -12,14 +12,16 @@ from ..compat import (
|
|||||||
compat_urllib_parse_unquote_plus,
|
compat_urllib_parse_unquote_plus,
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
clean_html,
|
||||||
error_to_compat_str,
|
error_to_compat_str,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
|
get_element_by_id,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
js_to_json,
|
||||||
limit_length,
|
limit_length,
|
||||||
sanitized_Request,
|
sanitized_Request,
|
||||||
|
try_get,
|
||||||
urlencode_postdata,
|
urlencode_postdata,
|
||||||
get_element_by_id,
|
|
||||||
clean_html,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -71,7 +73,7 @@ class FacebookIE(InfoExtractor):
|
|||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '274175099429670',
|
'id': '274175099429670',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Facebook video #274175099429670',
|
'title': 'Asif Nawab Butt posted a video to his Timeline.',
|
||||||
'uploader': 'Asif Nawab Butt',
|
'uploader': 'Asif Nawab Butt',
|
||||||
'upload_date': '20140506',
|
'upload_date': '20140506',
|
||||||
'timestamp': 1399398998,
|
'timestamp': 1399398998,
|
||||||
@ -132,6 +134,20 @@ class FacebookIE(InfoExtractor):
|
|||||||
'upload_date': '20161030',
|
'upload_date': '20161030',
|
||||||
'uploader': 'CNN',
|
'uploader': 'CNN',
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
# bigPipe.onPageletArrive ... onPageletArrive pagelet_group_mall
|
||||||
|
'url': 'https://www.facebook.com/yaroslav.korpan/videos/1417995061575415/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1417995061575415',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'md5:a7b86ca673f51800cd54687b7f4012fe',
|
||||||
|
'timestamp': 1486648217,
|
||||||
|
'upload_date': '20170209',
|
||||||
|
'uploader': 'Yaroslav Korpan',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://www.facebook.com/video.php?v=10204634152394104',
|
'url': 'https://www.facebook.com/video.php?v=10204634152394104',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
@ -243,14 +259,30 @@ class FacebookIE(InfoExtractor):
|
|||||||
|
|
||||||
video_data = None
|
video_data = None
|
||||||
|
|
||||||
server_js_data = self._parse_json(self._search_regex(
|
def extract_video_data(instances):
|
||||||
r'handleServerJS\(({.+})(?:\);|,")', webpage, 'server js data', default='{}'), video_id)
|
for item in instances:
|
||||||
for item in server_js_data.get('instances', []):
|
|
||||||
if item[1][0] == 'VideoConfig':
|
if item[1][0] == 'VideoConfig':
|
||||||
video_item = item[2][0]
|
video_item = item[2][0]
|
||||||
if video_item.get('video_id') == video_id:
|
if video_item.get('video_id') == video_id:
|
||||||
video_data = video_item['videoData']
|
return video_item['videoData']
|
||||||
break
|
|
||||||
|
server_js_data = self._parse_json(self._search_regex(
|
||||||
|
r'handleServerJS\(({.+})(?:\);|,")', webpage,
|
||||||
|
'server js data', default='{}'), video_id, fatal=False)
|
||||||
|
|
||||||
|
if server_js_data:
|
||||||
|
video_data = extract_video_data(server_js_data.get('instances', []))
|
||||||
|
|
||||||
|
if not video_data:
|
||||||
|
server_js_data = self._parse_json(
|
||||||
|
self._search_regex(
|
||||||
|
r'bigPipe\.onPageletArrive\(({.+?})\)\s*;\s*}\s*\)\s*,\s*["\']onPageletArrive\s+(?:stream_pagelet|pagelet_group_mall)',
|
||||||
|
webpage, 'js data', default='{}'),
|
||||||
|
video_id, transform_source=js_to_json, fatal=False)
|
||||||
|
if server_js_data:
|
||||||
|
video_data = extract_video_data(try_get(
|
||||||
|
server_js_data, lambda x: x['jsmods']['instances'],
|
||||||
|
list) or [])
|
||||||
|
|
||||||
if not video_data:
|
if not video_data:
|
||||||
if not fatal_if_no_video:
|
if not fatal_if_no_video:
|
||||||
@ -300,10 +332,16 @@ class FacebookIE(InfoExtractor):
|
|||||||
video_title = self._html_search_regex(
|
video_title = self._html_search_regex(
|
||||||
r'(?s)<span class="fbPhotosPhotoCaption".*?id="fbPhotoPageCaption"><span class="hasCaption">(.*?)</span>',
|
r'(?s)<span class="fbPhotosPhotoCaption".*?id="fbPhotoPageCaption"><span class="hasCaption">(.*?)</span>',
|
||||||
webpage, 'alternative title', default=None)
|
webpage, 'alternative title', default=None)
|
||||||
video_title = limit_length(video_title, 80)
|
|
||||||
if not video_title:
|
if not video_title:
|
||||||
|
video_title = self._html_search_meta(
|
||||||
|
'description', webpage, 'title')
|
||||||
|
if video_title:
|
||||||
|
video_title = limit_length(video_title, 80)
|
||||||
|
else:
|
||||||
video_title = 'Facebook video #%s' % video_id
|
video_title = 'Facebook video #%s' % video_id
|
||||||
uploader = clean_html(get_element_by_id('fbPhotoPageAuthorName', webpage))
|
uploader = clean_html(get_element_by_id(
|
||||||
|
'fbPhotoPageAuthorName', webpage)) or self._search_regex(
|
||||||
|
r'ownerName\s*:\s*"([^"]+)"', webpage, 'uploader', fatal=False)
|
||||||
timestamp = int_or_none(self._search_regex(
|
timestamp = int_or_none(self._search_regex(
|
||||||
r'<abbr[^>]+data-utime=["\'](\d+)', webpage,
|
r'<abbr[^>]+data-utime=["\'](\d+)', webpage,
|
||||||
'timestamp', default=None))
|
'timestamp', default=None))
|
||||||
|
@ -133,7 +133,7 @@ class FC2EmbedIE(InfoExtractor):
|
|||||||
'id': '201403223kCqB3Ez',
|
'id': '201403223kCqB3Ez',
|
||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
'title': 'プリズン・ブレイク S1-01 マイケル 【吹替】',
|
'title': 'プリズン・ブレイク S1-01 マイケル 【吹替】',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
178
youtube_dl/extractor/filmon.py
Normal file
178
youtube_dl/extractor/filmon.py
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..compat import (
|
||||||
|
compat_str,
|
||||||
|
compat_HTTPError,
|
||||||
|
)
|
||||||
|
from ..utils import (
|
||||||
|
qualities,
|
||||||
|
strip_or_none,
|
||||||
|
int_or_none,
|
||||||
|
ExtractorError,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FilmOnIE(InfoExtractor):
|
||||||
|
IE_NAME = 'filmon'
|
||||||
|
_VALID_URL = r'(?:https?://(?:www\.)?filmon\.com/vod/view/|filmon:)(?P<id>\d+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://www.filmon.com/vod/view/24869-0-plan-9-from-outer-space',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '24869',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Plan 9 From Outer Space',
|
||||||
|
'description': 'Dead human, zombies and vampires',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.filmon.com/vod/view/2825-1-popeye-series-1',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '2825',
|
||||||
|
'title': 'Popeye Series 1',
|
||||||
|
'description': 'The original series of Popeye.',
|
||||||
|
},
|
||||||
|
'playlist_mincount': 8,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = self._download_json(
|
||||||
|
'https://www.filmon.com/api/vod/movie?id=%s' % video_id,
|
||||||
|
video_id)['response']
|
||||||
|
except ExtractorError as e:
|
||||||
|
if isinstance(e.cause, compat_HTTPError):
|
||||||
|
errmsg = self._parse_json(e.cause.read().decode(), video_id)['reason']
|
||||||
|
raise ExtractorError('%s said: %s' % (self.IE_NAME, errmsg), expected=True)
|
||||||
|
raise
|
||||||
|
|
||||||
|
title = response['title']
|
||||||
|
description = strip_or_none(response.get('description'))
|
||||||
|
|
||||||
|
if response.get('type_id') == 1:
|
||||||
|
entries = [self.url_result('filmon:' + episode_id) for episode_id in response.get('episodes', [])]
|
||||||
|
return self.playlist_result(entries, video_id, title, description)
|
||||||
|
|
||||||
|
QUALITY = qualities(('low', 'high'))
|
||||||
|
formats = []
|
||||||
|
for format_id, stream in response.get('streams', {}).items():
|
||||||
|
stream_url = stream.get('url')
|
||||||
|
if not stream_url:
|
||||||
|
continue
|
||||||
|
formats.append({
|
||||||
|
'format_id': format_id,
|
||||||
|
'url': stream_url,
|
||||||
|
'ext': 'mp4',
|
||||||
|
'quality': QUALITY(stream.get('quality')),
|
||||||
|
'protocol': 'm3u8_native',
|
||||||
|
})
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
thumbnails = []
|
||||||
|
poster = response.get('poster', {})
|
||||||
|
thumbs = poster.get('thumbs', {})
|
||||||
|
thumbs['poster'] = poster
|
||||||
|
for thumb_id, thumb in thumbs.items():
|
||||||
|
thumb_url = thumb.get('url')
|
||||||
|
if not thumb_url:
|
||||||
|
continue
|
||||||
|
thumbnails.append({
|
||||||
|
'id': thumb_id,
|
||||||
|
'url': thumb_url,
|
||||||
|
'width': int_or_none(thumb.get('width')),
|
||||||
|
'height': int_or_none(thumb.get('height')),
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': title,
|
||||||
|
'formats': formats,
|
||||||
|
'description': description,
|
||||||
|
'thumbnails': thumbnails,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FilmOnChannelIE(InfoExtractor):
|
||||||
|
IE_NAME = 'filmon:channel'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?filmon\.com/(?:tv|channel)/(?P<id>[a-z0-9-]+)'
|
||||||
|
_TESTS = [{
|
||||||
|
# VOD
|
||||||
|
'url': 'http://www.filmon.com/tv/sports-haters',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '4190',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Sports Haters',
|
||||||
|
'description': 'md5:dabcb4c1d9cfc77085612f1a85f8275d',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
# LIVE
|
||||||
|
'url': 'https://www.filmon.com/channel/filmon-sports',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.filmon.com/tv/2894',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
_THUMBNAIL_RES = [
|
||||||
|
('logo', 56, 28),
|
||||||
|
('big_logo', 106, 106),
|
||||||
|
('extra_big_logo', 300, 300),
|
||||||
|
]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
channel_id = self._match_id(url)
|
||||||
|
|
||||||
|
try:
|
||||||
|
channel_data = self._download_json(
|
||||||
|
'http://www.filmon.com/api-v2/channel/' + channel_id, channel_id)['data']
|
||||||
|
except ExtractorError as e:
|
||||||
|
if isinstance(e.cause, compat_HTTPError):
|
||||||
|
errmsg = self._parse_json(e.cause.read().decode(), channel_id)['message']
|
||||||
|
raise ExtractorError('%s said: %s' % (self.IE_NAME, errmsg), expected=True)
|
||||||
|
raise
|
||||||
|
|
||||||
|
channel_id = compat_str(channel_data['id'])
|
||||||
|
is_live = not channel_data.get('is_vod') and not channel_data.get('is_vox')
|
||||||
|
title = channel_data['title']
|
||||||
|
|
||||||
|
QUALITY = qualities(('low', 'high'))
|
||||||
|
formats = []
|
||||||
|
for stream in channel_data.get('streams', []):
|
||||||
|
stream_url = stream.get('url')
|
||||||
|
if not stream_url:
|
||||||
|
continue
|
||||||
|
if not is_live:
|
||||||
|
formats.extend(self._extract_wowza_formats(
|
||||||
|
stream_url, channel_id, skip_protocols=['dash', 'rtmp', 'rtsp']))
|
||||||
|
continue
|
||||||
|
quality = stream.get('quality')
|
||||||
|
formats.append({
|
||||||
|
'format_id': quality,
|
||||||
|
# this is an m3u8 stream, but we are deliberately not using _extract_m3u8_formats
|
||||||
|
# because it doesn't have bitrate variants anyway
|
||||||
|
'url': stream_url,
|
||||||
|
'ext': 'mp4',
|
||||||
|
'quality': QUALITY(quality),
|
||||||
|
})
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
thumbnails = []
|
||||||
|
for name, width, height in self._THUMBNAIL_RES:
|
||||||
|
thumbnails.append({
|
||||||
|
'id': name,
|
||||||
|
'url': 'http://static.filmon.com/assets/channels/%s/%s.png' % (channel_id, name),
|
||||||
|
'width': width,
|
||||||
|
'height': height,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': channel_id,
|
||||||
|
'display_id': channel_data.get('alias'),
|
||||||
|
'title': self._live_title(title) if is_live else title,
|
||||||
|
'description': channel_data.get('description'),
|
||||||
|
'thumbnails': thumbnails,
|
||||||
|
'formats': formats,
|
||||||
|
'is_live': is_live,
|
||||||
|
}
|
@ -26,7 +26,7 @@ class FirstTVIE(InfoExtractor):
|
|||||||
'id': '40049',
|
'id': '40049',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Гость Людмила Сенчина. Наедине со всеми. Выпуск от 12.02.2015',
|
'title': 'Гость Людмила Сенчина. Наедине со всеми. Выпуск от 12.02.2015',
|
||||||
'thumbnail': 're:^https?://.*\.(?:jpg|JPG)$',
|
'thumbnail': r're:^https?://.*\.(?:jpg|JPG)$',
|
||||||
'upload_date': '20150212',
|
'upload_date': '20150212',
|
||||||
'duration': 2694,
|
'duration': 2694,
|
||||||
},
|
},
|
||||||
@ -37,7 +37,7 @@ class FirstTVIE(InfoExtractor):
|
|||||||
'id': '364746',
|
'id': '364746',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Весенняя аллергия. Доброе утро. Фрагмент выпуска от 07.04.2016',
|
'title': 'Весенняя аллергия. Доброе утро. Фрагмент выпуска от 07.04.2016',
|
||||||
'thumbnail': 're:^https?://.*\.(?:jpg|JPG)$',
|
'thumbnail': r're:^https?://.*\.(?:jpg|JPG)$',
|
||||||
'upload_date': '20160407',
|
'upload_date': '20160407',
|
||||||
'duration': 179,
|
'duration': 179,
|
||||||
'formats': 'mincount:3',
|
'formats': 'mincount:3',
|
||||||
@ -86,18 +86,43 @@ class FirstTVIE(InfoExtractor):
|
|||||||
title = item['title']
|
title = item['title']
|
||||||
quality = qualities(QUALITIES)
|
quality = qualities(QUALITIES)
|
||||||
formats = []
|
formats = []
|
||||||
|
path = None
|
||||||
for f in item.get('mbr', []):
|
for f in item.get('mbr', []):
|
||||||
src = f.get('src')
|
src = f.get('src')
|
||||||
if not src or not isinstance(src, compat_str):
|
if not src or not isinstance(src, compat_str):
|
||||||
continue
|
continue
|
||||||
tbr = int_or_none(self._search_regex(
|
tbr = int_or_none(self._search_regex(
|
||||||
r'_(\d{3,})\.mp4', src, 'tbr', default=None))
|
r'_(\d{3,})\.mp4', src, 'tbr', default=None))
|
||||||
|
if not path:
|
||||||
|
path = self._search_regex(
|
||||||
|
r'//[^/]+/(.+?)_\d+\.mp4', src,
|
||||||
|
'm3u8 path', default=None)
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': src,
|
'url': src,
|
||||||
'format_id': f.get('name'),
|
'format_id': f.get('name'),
|
||||||
'tbr': tbr,
|
'tbr': tbr,
|
||||||
'quality': quality(f.get('name')),
|
'source_preference': quality(f.get('name')),
|
||||||
})
|
})
|
||||||
|
# m3u8 URL format is reverse engineered from [1] (search for
|
||||||
|
# master.m3u8). dashEdges (that is currently balancer-vod.1tv.ru)
|
||||||
|
# is taken from [2].
|
||||||
|
# 1. http://static.1tv.ru/player/eump1tv-current/eump-1tv.all.min.js?rnd=9097422834:formatted
|
||||||
|
# 2. http://static.1tv.ru/player/eump1tv-config/config-main.js?rnd=9097422834
|
||||||
|
if not path and len(formats) == 1:
|
||||||
|
path = self._search_regex(
|
||||||
|
r'//[^/]+/(.+?$)', formats[0]['url'],
|
||||||
|
'm3u8 path', default=None)
|
||||||
|
if path:
|
||||||
|
if len(formats) == 1:
|
||||||
|
m3u8_path = ','
|
||||||
|
else:
|
||||||
|
tbrs = [compat_str(t) for t in sorted(f['tbr'] for f in formats)]
|
||||||
|
m3u8_path = '_,%s,%s' % (','.join(tbrs), '.mp4')
|
||||||
|
formats.extend(self._extract_m3u8_formats(
|
||||||
|
'http://balancer-vod.1tv.ru/%s%s.urlset/master.m3u8'
|
||||||
|
% (path, m3u8_path),
|
||||||
|
display_id, 'mp4',
|
||||||
|
entry_protocol='m3u8_native', m3u8_id='hls', fatal=False))
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
thumbnail = item.get('poster') or self._og_search_thumbnail(webpage)
|
thumbnail = item.get('poster') or self._og_search_thumbnail(webpage)
|
||||||
|
@ -25,7 +25,7 @@ class FiveTVIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Россияне выбрали имя для общенациональной платежной системы',
|
'title': 'Россияне выбрали имя для общенациональной платежной системы',
|
||||||
'description': 'md5:a8aa13e2b7ad36789e9f77a74b6de660',
|
'description': 'md5:a8aa13e2b7ad36789e9f77a74b6de660',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 180,
|
'duration': 180,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
@ -35,7 +35,7 @@ class FiveTVIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': '3D принтер',
|
'title': '3D принтер',
|
||||||
'description': 'md5:d76c736d29ef7ec5c0cf7d7c65ffcb41',
|
'description': 'md5:d76c736d29ef7ec5c0cf7d7c65ffcb41',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 180,
|
'duration': 180,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
@ -44,7 +44,7 @@ class FiveTVIE(InfoExtractor):
|
|||||||
'id': 'glavnoe',
|
'id': 'glavnoe',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Итоги недели с 8 по 14 июня 2015 года',
|
'title': 'Итоги недели с 8 по 14 июня 2015 года',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.5-tv.ru/glavnoe/broadcasts/508645/',
|
'url': 'http://www.5-tv.ru/glavnoe/broadcasts/508645/',
|
||||||
|
@ -19,7 +19,7 @@ class FKTVIE(InfoExtractor):
|
|||||||
'id': '1',
|
'id': '1',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Folge 1 vom 10. April 2007',
|
'title': 'Folge 1 vom 10. April 2007',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ class FlipagramIE(InfoExtractor):
|
|||||||
'filesize': int_or_none(cover.get('size')),
|
'filesize': int_or_none(cover.get('size')),
|
||||||
} for cover in flipagram.get('covers', []) if cover.get('url')]
|
} for cover in flipagram.get('covers', []) if cover.get('url')]
|
||||||
|
|
||||||
# Note that this only retrieves comments that are initally loaded.
|
# Note that this only retrieves comments that are initially loaded.
|
||||||
# For videos with large amounts of comments, most won't be retrieved.
|
# For videos with large amounts of comments, most won't be retrieved.
|
||||||
comments = []
|
comments = []
|
||||||
for comment in video_data.get('comments', {}).get(video_id, {}).get('items', []):
|
for comment in video_data.get('comments', {}).get(video_id, {}).get('items', []):
|
||||||
|
@ -20,7 +20,7 @@ class FoxgayIE(InfoExtractor):
|
|||||||
'title': 'Fuck Turkish-style',
|
'title': 'Fuck Turkish-style',
|
||||||
'description': 'md5:6ae2d9486921891efe89231ace13ffdf',
|
'description': 'md5:6ae2d9486921891efe89231ace13ffdf',
|
||||||
'age_limit': 18,
|
'age_limit': 18,
|
||||||
'thumbnail': 're:https?://.*\.jpg$',
|
'thumbnail': r're:https?://.*\.jpg$',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user