mirror of
https://github.com/ytdl-org/youtube-dl
synced 2025-10-21 23:58:36 +09:00
Compare commits
189 Commits
2019.03.18
...
2019.06.08
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3b2fd09596 | ||
![]() |
e35fc5ebc7 | ||
![]() |
dbb1886114 | ||
![]() |
c2ee6fa66a | ||
![]() |
4831ef7fe4 | ||
![]() |
178663df52 | ||
![]() |
ef19739e64 | ||
![]() |
01b517a20a | ||
![]() |
f4c99cd635 | ||
![]() |
e75220b11a | ||
![]() |
2efefddafd | ||
![]() |
bf3c932663 | ||
![]() |
4c78c3d700 | ||
![]() |
2e11e51c04 | ||
![]() |
1a01639bf9 | ||
![]() |
59ca17b1c8 | ||
![]() |
c94c121a99 | ||
![]() |
0c84002650 | ||
![]() |
c5eb75b35a | ||
![]() |
0e2dd3fcbc | ||
![]() |
26a87972a9 | ||
![]() |
33b2218b2f | ||
![]() |
ead467a9c1 | ||
![]() |
0d29751890 | ||
![]() |
25b83c2a0e | ||
![]() |
3fe774722b | ||
![]() |
f4cc2ca503 | ||
![]() |
11ec06de7f | ||
![]() |
53cd37bac5 | ||
![]() |
f856816b94 | ||
![]() |
8af49fc276 | ||
![]() |
9c5f2988b9 | ||
![]() |
afd4985f72 | ||
![]() |
2c53c0ebc6 | ||
![]() |
bbf1defe58 | ||
![]() |
186d185b6e | ||
![]() |
612300a686 | ||
![]() |
ea75382094 | ||
![]() |
e438e81469 | ||
![]() |
0e6f914b3b | ||
![]() |
6ab30ff50b | ||
![]() |
42c971341b | ||
![]() |
ce2fe4c01c | ||
![]() |
a9e03736df | ||
![]() |
e3c1266f49 | ||
![]() |
82e91d20a0 | ||
![]() |
170d644440 | ||
![]() |
5831742840 | ||
![]() |
a277dd33eb | ||
![]() |
0e0bfd334c | ||
![]() |
e6a25fea23 | ||
![]() |
b7df8f90a7 | ||
![]() |
8ae32e1882 | ||
![]() |
07a2cef782 | ||
![]() |
8ed7a23328 | ||
![]() |
3089bc748c | ||
![]() |
d23e85515a | ||
![]() |
cd37ef44e9 | ||
![]() |
169f8d0fe1 | ||
![]() |
0db2b275dd | ||
![]() |
ab11674502 | ||
![]() |
68b92aa1b4 | ||
![]() |
fd35d8cdfd | ||
![]() |
4eec112740 | ||
![]() |
027ffdca0d | ||
![]() |
a5b92d3590 | ||
![]() |
b45a9e698e | ||
![]() |
71ebd35d50 | ||
![]() |
f8c55c6664 | ||
![]() |
876fed6bf3 | ||
![]() |
c9856648db | ||
![]() |
2533f5b691 | ||
![]() |
e0dde1d8e2 | ||
![]() |
62d10f0d32 | ||
![]() |
274519dd08 | ||
![]() |
091200c368 | ||
![]() |
67bfbe4942 | ||
![]() |
54f3b61216 | ||
![]() |
a61ce71468 | ||
![]() |
026fbedc85 | ||
![]() |
6e07b5a6d5 | ||
![]() |
c464e1df2c | ||
![]() |
92bc97d398 | ||
![]() |
f916abc0ac | ||
![]() |
24510bdcfa | ||
![]() |
ae8c13565e | ||
![]() |
280913800d | ||
![]() |
7ff8ad80f1 | ||
![]() |
4e4db743e7 | ||
![]() |
3545d38bfb | ||
![]() |
2309d6bf92 | ||
![]() |
822b9d9cb0 | ||
![]() |
5caabd3c70 | ||
![]() |
aa05a093bb | ||
![]() |
60e67c5b2c | ||
![]() |
eefa0f2157 | ||
![]() |
6f366ef30c | ||
![]() |
88b547492f | ||
![]() |
00a9a25cf9 | ||
![]() |
97abf05ad3 | ||
![]() |
da668a23bd | ||
![]() |
58ef5e7881 | ||
![]() |
3e7ec5330a | ||
![]() |
98933c14e1 | ||
![]() |
56667d622c | ||
![]() |
50d660479d | ||
![]() |
1fa8893734 | ||
![]() |
15be3eb5e5 | ||
![]() |
e09965d550 | ||
![]() |
3fd86cfe13 | ||
![]() |
fdc2183650 | ||
![]() |
85b6335d55 | ||
![]() |
c25720ef6a | ||
![]() |
c9b19d7a55 | ||
![]() |
47cfa00516 | ||
![]() |
061d1cd948 | ||
![]() |
5de538787d | ||
![]() |
9abeefd527 | ||
![]() |
f3914b06a0 | ||
![]() |
81d989c21e | ||
![]() |
cd6c75b05f | ||
![]() |
9846935256 | ||
![]() |
7fc3b68ad3 | ||
![]() |
c4341ea47e | ||
![]() |
e6c9ae31df | ||
![]() |
6104cc1591 | ||
![]() |
f114e43d38 | ||
![]() |
cb6cd76f7b | ||
![]() |
0b758fea1c | ||
![]() |
3534b6329a | ||
![]() |
174f62992d | ||
![]() |
1038532213 | ||
![]() |
4f1e02ad60 | ||
![]() |
180a9dff1f | ||
![]() |
972d2dd0bc | ||
![]() |
11edb76610 | ||
![]() |
8721b09751 | ||
![]() |
dc27fd8bb8 | ||
![]() |
c912029480 | ||
![]() |
118f7add3b | ||
![]() |
4bc12b8f81 | ||
![]() |
5ca3459828 | ||
![]() |
9c017253e8 | ||
![]() |
9045d28b5e | ||
![]() |
7c2ecbc1cc | ||
![]() |
d562cac9dc | ||
![]() |
9ed06812ec | ||
![]() |
bf6fb8b9dc | ||
![]() |
a46d9e5b41 | ||
![]() |
aa5338118e | ||
![]() |
8410653f24 | ||
![]() |
f4da808036 | ||
![]() |
f412970164 | ||
![]() |
059cd768b9 | ||
![]() |
c701472fc9 | ||
![]() |
19591facea | ||
![]() |
b9aad6c427 | ||
![]() |
9f182c23ba | ||
![]() |
4810655cd6 | ||
![]() |
a7978f8e2a | ||
![]() |
19041a3877 | ||
![]() |
afb7496416 | ||
![]() |
69e6efac16 | ||
![]() |
2bbde1d09a | ||
![]() |
b966740cf7 | ||
![]() |
220828f2d6 | ||
![]() |
977a782110 | ||
![]() |
a2b6f946f1 | ||
![]() |
4f7db46887 | ||
![]() |
d7d86fdd49 | ||
![]() |
f8987163fb | ||
![]() |
313e8b2b18 | ||
![]() |
c0b7d11713 | ||
![]() |
efee62ac7f | ||
![]() |
38287d251d | ||
![]() |
25d9243141 | ||
![]() |
93bb6b1bae | ||
![]() |
b43c5f474a | ||
![]() |
4014a48622 | ||
![]() |
99fe330070 | ||
![]() |
c4c888697e | ||
![]() |
b27a71e66c | ||
![]() |
de74ef83b7 | ||
![]() |
cf3d399727 | ||
![]() |
8cb10807ed | ||
![]() |
b8526c78f9 | ||
![]() |
5e1271c56d | ||
![]() |
050afa60c6 | ||
![]() |
c4580580f5 |
61
.github/ISSUE_TEMPLATE.md
vendored
61
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,61 +0,0 @@
|
|||||||
## Please follow the guide below
|
|
||||||
|
|
||||||
- You will be asked some questions and requested to provide some information, please read them **carefully** and answer honestly
|
|
||||||
- Put an `x` into all the boxes [ ] relevant to your *issue* (like this: `[x]`)
|
|
||||||
- Use the *Preview* tab to see what your issue will actually look like
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Make sure you are using the *latest* version: run `youtube-dl --version` and ensure your version is *2019.03.18*. If it's not, read [this FAQ entry](https://github.com/ytdl-org/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 **2019.03.18**
|
|
||||||
|
|
||||||
### Before submitting an *issue* make sure you have:
|
|
||||||
- [ ] At least skimmed through the [README](https://github.com/ytdl-org/youtube-dl/blob/master/README.md), **most notably** the [FAQ](https://github.com/ytdl-org/youtube-dl#faq) and [BUGS](https://github.com/ytdl-org/youtube-dl#bugs) sections
|
|
||||||
- [ ] [Searched](https://github.com/ytdl-org/youtube-dl/search?type=Issues) the bugtracker for similar issues including closed ones
|
|
||||||
- [ ] Checked that provided video/audio/playlist URLs (if any) are alive and playable in a browser
|
|
||||||
|
|
||||||
### What is the purpose of your *issue*?
|
|
||||||
- [ ] Bug report (encountered problems with youtube-dl)
|
|
||||||
- [ ] Site support request (request for adding support for a new site)
|
|
||||||
- [ ] Feature request (request for a new functionality)
|
|
||||||
- [ ] Question
|
|
||||||
- [ ] Other
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### The following sections concretize particular purposed issues, you can erase any section (the contents between triple ---) not applicable to your *issue*
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### If the purpose of this *issue* is a *bug report*, *site support request* or you are not completely sure provide the full verbose output as follows:
|
|
||||||
|
|
||||||
Add the `-v` flag to **your command line** you run youtube-dl with (`youtube-dl -v <your command line>`), copy the **whole** output and insert it here. It should look similar to one below (replace it with **your** log inserted between triple ```):
|
|
||||||
|
|
||||||
```
|
|
||||||
[debug] System config: []
|
|
||||||
[debug] User config: []
|
|
||||||
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
|
||||||
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
|
||||||
[debug] youtube-dl version 2019.03.18
|
|
||||||
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
|
||||||
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
|
||||||
[debug] Proxy map: {}
|
|
||||||
...
|
|
||||||
<end of log>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### If the purpose of this *issue* is a *site support request* please provide all kinds of example URLs support for which should be included (replace following example URLs by **yours**):
|
|
||||||
- Single video: https://www.youtube.com/watch?v=BaW_jenozKc
|
|
||||||
- Single video: https://youtu.be/BaW_jenozKc
|
|
||||||
- Playlist: https://www.youtube.com/playlist?list=PL4lCao7KL_QFVb7Iudeipvc2BCavECqzc
|
|
||||||
|
|
||||||
Note that **youtube-dl does not support sites dedicated to [copyright infringement](https://github.com/ytdl-org/youtube-dl#can-you-add-support-for-this-anime-video-site-or-site-which-shows-current-movies-for-free)**. In order for site support request to be accepted all provided example URLs should not violate any copyrights.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Description of your *issue*, suggested solution and other information
|
|
||||||
|
|
||||||
Explanation of your *issue* in arbitrary form goes here. Please make sure the [description is worded well enough to be understood](https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient). Provide as much context and examples as possible.
|
|
||||||
If work on your *issue* requires account credentials please provide them or explain how one can obtain them.
|
|
63
.github/ISSUE_TEMPLATE/1_broken_site.md
vendored
Normal file
63
.github/ISSUE_TEMPLATE/1_broken_site.md
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
name: Broken site support
|
||||||
|
about: Report broken or misfunctioning site
|
||||||
|
title: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
WARNING!
|
||||||
|
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.06.08. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
||||||
|
- Make sure that all provided video/audio/playlist URLs (if any) are alive and playable in a browser.
|
||||||
|
- Make sure that all URLs and arguments with special characters are properly quoted or escaped as explained in http://yt-dl.org/escape.
|
||||||
|
- Search the bugtracker for similar issues: http://yt-dl.org/search-issues. DO NOT post duplicates.
|
||||||
|
- Finally, put x into all relevant boxes (like this [x])
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] I'm reporting a broken site support
|
||||||
|
- [ ] I've verified that I'm running youtube-dl version **2019.06.08**
|
||||||
|
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
||||||
|
- [ ] I've checked that all URLs and arguments with special characters are properly quoted or escaped
|
||||||
|
- [ ] I've searched the bugtracker for similar issues including closed ones
|
||||||
|
|
||||||
|
|
||||||
|
## Verbose log
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide the complete verbose output of youtube-dl that clearly demonstrates the problem.
|
||||||
|
Add the `-v` flag to your command line you run youtube-dl with (`youtube-dl -v <your command line>`), copy the WHOLE output and insert it below. It should look similar to this:
|
||||||
|
[debug] System config: []
|
||||||
|
[debug] User config: []
|
||||||
|
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
||||||
|
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
||||||
|
[debug] youtube-dl version 2019.06.08
|
||||||
|
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
||||||
|
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
||||||
|
[debug] Proxy map: {}
|
||||||
|
<more lines>
|
||||||
|
-->
|
||||||
|
|
||||||
|
```
|
||||||
|
PASTE VERBOSE LOG HERE
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide an explanation of your issue in an arbitrary form. Provide any additional information, suggested solution and as much context and examples as possible.
|
||||||
|
If work on your issue requires account credentials please provide them or explain how one can obtain them.
|
||||||
|
-->
|
||||||
|
|
||||||
|
WRITE DESCRIPTION HERE
|
54
.github/ISSUE_TEMPLATE/2_site_support_request.md
vendored
Normal file
54
.github/ISSUE_TEMPLATE/2_site_support_request.md
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
name: Site support request
|
||||||
|
about: Request support for a new site
|
||||||
|
title: ''
|
||||||
|
labels: 'site-support-request'
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
WARNING!
|
||||||
|
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.06.08. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
||||||
|
- Make sure that all provided video/audio/playlist URLs (if any) are alive and playable in a browser.
|
||||||
|
- Make sure that site you are requesting is not dedicated to copyright infringement, see https://yt-dl.org/copyright-infringement. youtube-dl does not support such sites. In order for site support request to be accepted all provided example URLs should not violate any copyrights.
|
||||||
|
- Search the bugtracker for similar site support requests: http://yt-dl.org/search-issues. DO NOT post duplicates.
|
||||||
|
- Finally, put x into all relevant boxes (like this [x])
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] I'm reporting a new site support request
|
||||||
|
- [ ] I've verified that I'm running youtube-dl version **2019.06.08**
|
||||||
|
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
||||||
|
- [ ] I've checked that none of provided URLs violate any copyrights
|
||||||
|
- [ ] I've searched the bugtracker for similar site support requests including closed ones
|
||||||
|
|
||||||
|
|
||||||
|
## Example URLs
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide all kinds of example URLs support for which should be included. Replace following example URLs by yours.
|
||||||
|
-->
|
||||||
|
|
||||||
|
- Single video: https://www.youtube.com/watch?v=BaW_jenozKc
|
||||||
|
- Single video: https://youtu.be/BaW_jenozKc
|
||||||
|
- Playlist: https://www.youtube.com/playlist?list=PL4lCao7KL_QFVb7Iudeipvc2BCavECqzc
|
||||||
|
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide any additional information.
|
||||||
|
If work on your issue requires account credentials please provide them or explain how one can obtain them.
|
||||||
|
-->
|
||||||
|
|
||||||
|
WRITE DESCRIPTION HERE
|
37
.github/ISSUE_TEMPLATE/3_site_feature_request.md
vendored
Normal file
37
.github/ISSUE_TEMPLATE/3_site_feature_request.md
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
name: Site feature request
|
||||||
|
about: Request a new functionality for a site
|
||||||
|
title: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
WARNING!
|
||||||
|
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.06.08. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
||||||
|
- Search the bugtracker for similar site feature requests: http://yt-dl.org/search-issues. DO NOT post duplicates.
|
||||||
|
- Finally, put x into all relevant boxes (like this [x])
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] I'm reporting a site feature request
|
||||||
|
- [ ] I've verified that I'm running youtube-dl version **2019.06.08**
|
||||||
|
- [ ] I've searched the bugtracker for similar site feature requests including closed ones
|
||||||
|
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide an explanation of your site feature request in an arbitrary form. Please make sure the description is worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient. Provide any additional information, suggested solution and as much context and examples as possible.
|
||||||
|
-->
|
||||||
|
|
||||||
|
WRITE DESCRIPTION HERE
|
65
.github/ISSUE_TEMPLATE/4_bug_report.md
vendored
Normal file
65
.github/ISSUE_TEMPLATE/4_bug_report.md
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Report a bug unrelated to any particular site or extractor
|
||||||
|
title: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
WARNING!
|
||||||
|
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.06.08. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
||||||
|
- Make sure that all provided video/audio/playlist URLs (if any) are alive and playable in a browser.
|
||||||
|
- Make sure that all URLs and arguments with special characters are properly quoted or escaped as explained in http://yt-dl.org/escape.
|
||||||
|
- Search the bugtracker for similar issues: http://yt-dl.org/search-issues. DO NOT post duplicates.
|
||||||
|
- Read bugs section in FAQ: http://yt-dl.org/reporting
|
||||||
|
- Finally, put x into all relevant boxes (like this [x])
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] I'm reporting a broken site support issue
|
||||||
|
- [ ] I've verified that I'm running youtube-dl version **2019.06.08**
|
||||||
|
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
||||||
|
- [ ] I've checked that all URLs and arguments with special characters are properly quoted or escaped
|
||||||
|
- [ ] I've searched the bugtracker for similar bug reports including closed ones
|
||||||
|
- [ ] I've read bugs section in FAQ
|
||||||
|
|
||||||
|
|
||||||
|
## Verbose log
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide the complete verbose output of youtube-dl that clearly demonstrates the problem.
|
||||||
|
Add the `-v` flag to your command line you run youtube-dl with (`youtube-dl -v <your command line>`), copy the WHOLE output and insert it below. It should look similar to this:
|
||||||
|
[debug] System config: []
|
||||||
|
[debug] User config: []
|
||||||
|
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
||||||
|
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
||||||
|
[debug] youtube-dl version 2019.06.08
|
||||||
|
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
||||||
|
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
||||||
|
[debug] Proxy map: {}
|
||||||
|
<more lines>
|
||||||
|
-->
|
||||||
|
|
||||||
|
```
|
||||||
|
PASTE VERBOSE LOG HERE
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide an explanation of your issue in an arbitrary form. Please make sure the description is worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient. Provide any additional information, suggested solution and as much context and examples as possible.
|
||||||
|
If work on your issue requires account credentials please provide them or explain how one can obtain them.
|
||||||
|
-->
|
||||||
|
|
||||||
|
WRITE DESCRIPTION HERE
|
38
.github/ISSUE_TEMPLATE/5_feature_request.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/5_feature_request.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Request a new functionality unrelated to any particular site or extractor
|
||||||
|
title: ''
|
||||||
|
labels: 'request'
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
WARNING!
|
||||||
|
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.06.08. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
||||||
|
- Search the bugtracker for similar feature requests: http://yt-dl.org/search-issues. DO NOT post duplicates.
|
||||||
|
- Finally, put x into all relevant boxes (like this [x])
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] I'm reporting a feature request
|
||||||
|
- [ ] I've verified that I'm running youtube-dl version **2019.06.08**
|
||||||
|
- [ ] I've searched the bugtracker for similar feature requests including closed ones
|
||||||
|
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide an explanation of your issue in an arbitrary form. Please make sure the description is worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient. Provide any additional information, suggested solution and as much context and examples as possible.
|
||||||
|
-->
|
||||||
|
|
||||||
|
WRITE DESCRIPTION HERE
|
38
.github/ISSUE_TEMPLATE/6_question.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/6_question.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: Ask question
|
||||||
|
about: Ask youtube-dl related question
|
||||||
|
title: ''
|
||||||
|
labels: 'question'
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
WARNING!
|
||||||
|
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
|
- Look through the README (http://yt-dl.org/readme) and FAQ (http://yt-dl.org/faq) for similar questions
|
||||||
|
- Search the bugtracker for similar questions: http://yt-dl.org/search-issues
|
||||||
|
- Finally, put x into all relevant boxes (like this [x])
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] I'm asking a question
|
||||||
|
- [ ] I've looked through the README and FAQ for similar questions
|
||||||
|
- [ ] I've searched the bugtracker for similar questions including closed ones
|
||||||
|
|
||||||
|
|
||||||
|
## Question
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Ask your question in an arbitrary form. Please make sure it's worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient.
|
||||||
|
-->
|
||||||
|
|
||||||
|
WRITE QUESTION HERE
|
61
.github/ISSUE_TEMPLATE_tmpl.md
vendored
61
.github/ISSUE_TEMPLATE_tmpl.md
vendored
@@ -1,61 +0,0 @@
|
|||||||
## Please follow the guide below
|
|
||||||
|
|
||||||
- You will be asked some questions and requested to provide some information, please read them **carefully** and answer honestly
|
|
||||||
- Put an `x` into all the boxes [ ] relevant to your *issue* (like this: `[x]`)
|
|
||||||
- Use the *Preview* tab to see what your issue will actually look like
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Make sure you are using the *latest* version: run `youtube-dl --version` and ensure your version is *%(version)s*. If it's not, read [this FAQ entry](https://github.com/ytdl-org/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 **%(version)s**
|
|
||||||
|
|
||||||
### Before submitting an *issue* make sure you have:
|
|
||||||
- [ ] At least skimmed through the [README](https://github.com/ytdl-org/youtube-dl/blob/master/README.md), **most notably** the [FAQ](https://github.com/ytdl-org/youtube-dl#faq) and [BUGS](https://github.com/ytdl-org/youtube-dl#bugs) sections
|
|
||||||
- [ ] [Searched](https://github.com/ytdl-org/youtube-dl/search?type=Issues) the bugtracker for similar issues including closed ones
|
|
||||||
- [ ] Checked that provided video/audio/playlist URLs (if any) are alive and playable in a browser
|
|
||||||
|
|
||||||
### What is the purpose of your *issue*?
|
|
||||||
- [ ] Bug report (encountered problems with youtube-dl)
|
|
||||||
- [ ] Site support request (request for adding support for a new site)
|
|
||||||
- [ ] Feature request (request for a new functionality)
|
|
||||||
- [ ] Question
|
|
||||||
- [ ] Other
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### The following sections concretize particular purposed issues, you can erase any section (the contents between triple ---) not applicable to your *issue*
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### If the purpose of this *issue* is a *bug report*, *site support request* or you are not completely sure provide the full verbose output as follows:
|
|
||||||
|
|
||||||
Add the `-v` flag to **your command line** you run youtube-dl with (`youtube-dl -v <your command line>`), copy the **whole** output and insert it here. It should look similar to one below (replace it with **your** log inserted between triple ```):
|
|
||||||
|
|
||||||
```
|
|
||||||
[debug] System config: []
|
|
||||||
[debug] User config: []
|
|
||||||
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
|
||||||
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
|
||||||
[debug] youtube-dl version %(version)s
|
|
||||||
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
|
||||||
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
|
||||||
[debug] Proxy map: {}
|
|
||||||
...
|
|
||||||
<end of log>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### If the purpose of this *issue* is a *site support request* please provide all kinds of example URLs support for which should be included (replace following example URLs by **yours**):
|
|
||||||
- Single video: https://www.youtube.com/watch?v=BaW_jenozKc
|
|
||||||
- Single video: https://youtu.be/BaW_jenozKc
|
|
||||||
- Playlist: https://www.youtube.com/playlist?list=PL4lCao7KL_QFVb7Iudeipvc2BCavECqzc
|
|
||||||
|
|
||||||
Note that **youtube-dl does not support sites dedicated to [copyright infringement](https://github.com/ytdl-org/youtube-dl#can-you-add-support-for-this-anime-video-site-or-site-which-shows-current-movies-for-free)**. In order for site support request to be accepted all provided example URLs should not violate any copyrights.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Description of your *issue*, suggested solution and other information
|
|
||||||
|
|
||||||
Explanation of your *issue* in arbitrary form goes here. Please make sure the [description is worded well enough to be understood](https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient). Provide as much context and examples as possible.
|
|
||||||
If work on your *issue* requires account credentials please provide them or explain how one can obtain them.
|
|
63
.github/ISSUE_TEMPLATE_tmpl/1_broken_site.md
vendored
Normal file
63
.github/ISSUE_TEMPLATE_tmpl/1_broken_site.md
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
name: Broken site support
|
||||||
|
about: Report broken or misfunctioning site
|
||||||
|
title: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
WARNING!
|
||||||
|
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is %(version)s. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
||||||
|
- Make sure that all provided video/audio/playlist URLs (if any) are alive and playable in a browser.
|
||||||
|
- Make sure that all URLs and arguments with special characters are properly quoted or escaped as explained in http://yt-dl.org/escape.
|
||||||
|
- Search the bugtracker for similar issues: http://yt-dl.org/search-issues. DO NOT post duplicates.
|
||||||
|
- Finally, put x into all relevant boxes (like this [x])
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] I'm reporting a broken site support
|
||||||
|
- [ ] I've verified that I'm running youtube-dl version **%(version)s**
|
||||||
|
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
||||||
|
- [ ] I've checked that all URLs and arguments with special characters are properly quoted or escaped
|
||||||
|
- [ ] I've searched the bugtracker for similar issues including closed ones
|
||||||
|
|
||||||
|
|
||||||
|
## Verbose log
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide the complete verbose output of youtube-dl that clearly demonstrates the problem.
|
||||||
|
Add the `-v` flag to your command line you run youtube-dl with (`youtube-dl -v <your command line>`), copy the WHOLE output and insert it below. It should look similar to this:
|
||||||
|
[debug] System config: []
|
||||||
|
[debug] User config: []
|
||||||
|
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
||||||
|
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
||||||
|
[debug] youtube-dl version %(version)s
|
||||||
|
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
||||||
|
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
||||||
|
[debug] Proxy map: {}
|
||||||
|
<more lines>
|
||||||
|
-->
|
||||||
|
|
||||||
|
```
|
||||||
|
PASTE VERBOSE LOG HERE
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide an explanation of your issue in an arbitrary form. Provide any additional information, suggested solution and as much context and examples as possible.
|
||||||
|
If work on your issue requires account credentials please provide them or explain how one can obtain them.
|
||||||
|
-->
|
||||||
|
|
||||||
|
WRITE DESCRIPTION HERE
|
54
.github/ISSUE_TEMPLATE_tmpl/2_site_support_request.md
vendored
Normal file
54
.github/ISSUE_TEMPLATE_tmpl/2_site_support_request.md
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
name: Site support request
|
||||||
|
about: Request support for a new site
|
||||||
|
title: ''
|
||||||
|
labels: 'site-support-request'
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
WARNING!
|
||||||
|
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is %(version)s. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
||||||
|
- Make sure that all provided video/audio/playlist URLs (if any) are alive and playable in a browser.
|
||||||
|
- Make sure that site you are requesting is not dedicated to copyright infringement, see https://yt-dl.org/copyright-infringement. youtube-dl does not support such sites. In order for site support request to be accepted all provided example URLs should not violate any copyrights.
|
||||||
|
- Search the bugtracker for similar site support requests: http://yt-dl.org/search-issues. DO NOT post duplicates.
|
||||||
|
- Finally, put x into all relevant boxes (like this [x])
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] I'm reporting a new site support request
|
||||||
|
- [ ] I've verified that I'm running youtube-dl version **%(version)s**
|
||||||
|
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
||||||
|
- [ ] I've checked that none of provided URLs violate any copyrights
|
||||||
|
- [ ] I've searched the bugtracker for similar site support requests including closed ones
|
||||||
|
|
||||||
|
|
||||||
|
## Example URLs
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide all kinds of example URLs support for which should be included. Replace following example URLs by yours.
|
||||||
|
-->
|
||||||
|
|
||||||
|
- Single video: https://www.youtube.com/watch?v=BaW_jenozKc
|
||||||
|
- Single video: https://youtu.be/BaW_jenozKc
|
||||||
|
- Playlist: https://www.youtube.com/playlist?list=PL4lCao7KL_QFVb7Iudeipvc2BCavECqzc
|
||||||
|
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide any additional information.
|
||||||
|
If work on your issue requires account credentials please provide them or explain how one can obtain them.
|
||||||
|
-->
|
||||||
|
|
||||||
|
WRITE DESCRIPTION HERE
|
37
.github/ISSUE_TEMPLATE_tmpl/3_site_feature_request.md
vendored
Normal file
37
.github/ISSUE_TEMPLATE_tmpl/3_site_feature_request.md
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
name: Site feature request
|
||||||
|
about: Request a new functionality for a site
|
||||||
|
title: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
WARNING!
|
||||||
|
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is %(version)s. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
||||||
|
- Search the bugtracker for similar site feature requests: http://yt-dl.org/search-issues. DO NOT post duplicates.
|
||||||
|
- Finally, put x into all relevant boxes (like this [x])
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] I'm reporting a site feature request
|
||||||
|
- [ ] I've verified that I'm running youtube-dl version **%(version)s**
|
||||||
|
- [ ] I've searched the bugtracker for similar site feature requests including closed ones
|
||||||
|
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide an explanation of your site feature request in an arbitrary form. Please make sure the description is worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient. Provide any additional information, suggested solution and as much context and examples as possible.
|
||||||
|
-->
|
||||||
|
|
||||||
|
WRITE DESCRIPTION HERE
|
65
.github/ISSUE_TEMPLATE_tmpl/4_bug_report.md
vendored
Normal file
65
.github/ISSUE_TEMPLATE_tmpl/4_bug_report.md
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Report a bug unrelated to any particular site or extractor
|
||||||
|
title: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
WARNING!
|
||||||
|
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is %(version)s. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
||||||
|
- Make sure that all provided video/audio/playlist URLs (if any) are alive and playable in a browser.
|
||||||
|
- Make sure that all URLs and arguments with special characters are properly quoted or escaped as explained in http://yt-dl.org/escape.
|
||||||
|
- Search the bugtracker for similar issues: http://yt-dl.org/search-issues. DO NOT post duplicates.
|
||||||
|
- Read bugs section in FAQ: http://yt-dl.org/reporting
|
||||||
|
- Finally, put x into all relevant boxes (like this [x])
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] I'm reporting a broken site support issue
|
||||||
|
- [ ] I've verified that I'm running youtube-dl version **%(version)s**
|
||||||
|
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
||||||
|
- [ ] I've checked that all URLs and arguments with special characters are properly quoted or escaped
|
||||||
|
- [ ] I've searched the bugtracker for similar bug reports including closed ones
|
||||||
|
- [ ] I've read bugs section in FAQ
|
||||||
|
|
||||||
|
|
||||||
|
## Verbose log
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide the complete verbose output of youtube-dl that clearly demonstrates the problem.
|
||||||
|
Add the `-v` flag to your command line you run youtube-dl with (`youtube-dl -v <your command line>`), copy the WHOLE output and insert it below. It should look similar to this:
|
||||||
|
[debug] System config: []
|
||||||
|
[debug] User config: []
|
||||||
|
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
||||||
|
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
||||||
|
[debug] youtube-dl version %(version)s
|
||||||
|
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
||||||
|
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
||||||
|
[debug] Proxy map: {}
|
||||||
|
<more lines>
|
||||||
|
-->
|
||||||
|
|
||||||
|
```
|
||||||
|
PASTE VERBOSE LOG HERE
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide an explanation of your issue in an arbitrary form. Please make sure the description is worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient. Provide any additional information, suggested solution and as much context and examples as possible.
|
||||||
|
If work on your issue requires account credentials please provide them or explain how one can obtain them.
|
||||||
|
-->
|
||||||
|
|
||||||
|
WRITE DESCRIPTION HERE
|
38
.github/ISSUE_TEMPLATE_tmpl/5_feature_request.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE_tmpl/5_feature_request.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Request a new functionality unrelated to any particular site or extractor
|
||||||
|
title: ''
|
||||||
|
labels: 'request'
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
WARNING!
|
||||||
|
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is %(version)s. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
||||||
|
- Search the bugtracker for similar feature requests: http://yt-dl.org/search-issues. DO NOT post duplicates.
|
||||||
|
- Finally, put x into all relevant boxes (like this [x])
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] I'm reporting a feature request
|
||||||
|
- [ ] I've verified that I'm running youtube-dl version **%(version)s**
|
||||||
|
- [ ] I've searched the bugtracker for similar feature requests including closed ones
|
||||||
|
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide an explanation of your issue in an arbitrary form. Please make sure the description is worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient. Provide any additional information, suggested solution and as much context and examples as possible.
|
||||||
|
-->
|
||||||
|
|
||||||
|
WRITE DESCRIPTION HERE
|
@@ -9,6 +9,7 @@ python:
|
|||||||
- "3.6"
|
- "3.6"
|
||||||
- "pypy"
|
- "pypy"
|
||||||
- "pypy3"
|
- "pypy3"
|
||||||
|
dist: trusty
|
||||||
env:
|
env:
|
||||||
- YTDL_TEST_SET=core
|
- YTDL_TEST_SET=core
|
||||||
- YTDL_TEST_SET=download
|
- YTDL_TEST_SET=download
|
||||||
|
194
ChangeLog
194
ChangeLog
@@ -1,3 +1,197 @@
|
|||||||
|
version 2019.06.08
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [downloader/common] Improve rate limit (#21301)
|
||||||
|
* [utils] Improve strip_or_none
|
||||||
|
* [extractor/common] Strip src attribute for HTML5 entries code (#18485,
|
||||||
|
#21169)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [ted] Fix playlist extraction (#20844, #21032)
|
||||||
|
* [vlive:playlist] Fix video extraction when no playlist is found (#20590)
|
||||||
|
+ [vlive] Add CH+ support (#16887, #21209)
|
||||||
|
+ [openload] Add support for oload.website (#21329)
|
||||||
|
+ [tvnow] Extract HD formats (#21201)
|
||||||
|
+ [redbulltv] Add support for rrn:content URLs (#21297)
|
||||||
|
* [youtube] Fix average rating extraction (#21304)
|
||||||
|
+ [bitchute] Extract HTML5 formats (#21306)
|
||||||
|
* [cbsnews] Fix extraction (#9659, #15397)
|
||||||
|
* [vvvvid] Relax URL regular expression (#21299)
|
||||||
|
+ [prosiebensat1] Add support for new API (#21272)
|
||||||
|
+ [vrv] Extract adaptive_hls formats (#21243)
|
||||||
|
* [viki] Switch to HTTPS (#21001)
|
||||||
|
* [LiveLeak] Check if the original videos exist (#21206, #21208)
|
||||||
|
* [rtp] Fix extraction (#15099)
|
||||||
|
* [youtube] Improve DRM protected videos detection (#1774)
|
||||||
|
+ [srgssrplay] Add support for popupvideoplayer URLs (#21155)
|
||||||
|
+ [24video] Add support for porno.24video.net (#21194)
|
||||||
|
+ [24video] Add support for 24video.site (#21193)
|
||||||
|
- [pornflip] Remove extractor
|
||||||
|
- [criterion] Remove extractor (#21195)
|
||||||
|
* [pornhub] Use HTTPS (#21061)
|
||||||
|
* [bitchute] Fix uploader extraction (#21076)
|
||||||
|
* [streamcloud] Reduce waiting time to 6 seconds (#21092)
|
||||||
|
- [novamov] Remove extractors (#21077)
|
||||||
|
+ [openload] Add support for oload.press (#21135)
|
||||||
|
* [vivo] Fix extraction (#18906, #19217)
|
||||||
|
|
||||||
|
|
||||||
|
version 2019.05.20
|
||||||
|
|
||||||
|
Core
|
||||||
|
+ [extractor/common] Move workaround for applying first Set-Cookie header
|
||||||
|
into a separate _apply_first_set_cookie_header method
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [safari] Fix authentication (#21090)
|
||||||
|
* [vk] Use _apply_first_set_cookie_header
|
||||||
|
* [vrt] Fix extraction (#20527)
|
||||||
|
+ [canvas] Add support for vrtnieuws and sporza site ids and extract
|
||||||
|
AES HLS formats
|
||||||
|
+ [vrv] Extract captions (#19238)
|
||||||
|
* [tele5] Improve video id extraction
|
||||||
|
* [tele5] Relax URL regular expression (#21020, #21063)
|
||||||
|
* [svtplay] Update API URL (#21075)
|
||||||
|
+ [yahoo:gyao] Add X-User-Agent header to dam proxy requests (#21071)
|
||||||
|
|
||||||
|
|
||||||
|
version 2019.05.11
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [utils] Transliterate "þ" as "th" (#20897)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [cloudflarestream] Add support for videodelivery.net (#21049)
|
||||||
|
+ [byutv] Add support for DVR videos (#20574, #20676)
|
||||||
|
+ [gfycat] Add support for URLs with tags (#20696, #20731)
|
||||||
|
+ [openload] Add support for verystream.com (#20701, #20967)
|
||||||
|
* [youtube] Use sp field value for signature field name (#18841, #18927,
|
||||||
|
#21028)
|
||||||
|
+ [yahoo:gyao] Extend URL regular expression (#21008)
|
||||||
|
* [youtube] Fix channel id extraction (#20982, #21003)
|
||||||
|
+ [sky] Add support for news.sky.com (#13055)
|
||||||
|
+ [youtube:entrylistbase] Retry on 5xx HTTP errors (#20965)
|
||||||
|
+ [francetvinfo] Extend video id extraction (#20619, #20740)
|
||||||
|
* [4tube] Update token hosts (#20918)
|
||||||
|
* [hotstar] Move to API v2 (#20931)
|
||||||
|
* [fox] Fix API error handling under python 2 (#20925)
|
||||||
|
+ [redbulltv] Extend URL regular expression (#20922)
|
||||||
|
|
||||||
|
|
||||||
|
version 2019.04.30
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [openload] Use real Chrome versions (#20902)
|
||||||
|
- [youtube] Remove info el for get_video_info request
|
||||||
|
* [youtube] Improve extraction robustness
|
||||||
|
- [dramafever] Remove extractor (#20868)
|
||||||
|
* [adn] Fix subtitle extraction (#12724)
|
||||||
|
+ [ccc] Extract creator (#20355)
|
||||||
|
+ [ccc:playlist] Add support for media.ccc.de playlists (#14601, #20355)
|
||||||
|
+ [sverigesradio] Add support for sverigesradio.se (#18635)
|
||||||
|
+ [cinemax] Add support for cinemax.com
|
||||||
|
* [sixplay] Try extracting non-DRM protected manifests (#20849)
|
||||||
|
+ [youtube] Extract Youtube Music Auto-generated metadata (#20599, #20742)
|
||||||
|
- [wrzuta] Remove extractor (#20684, #20801)
|
||||||
|
* [twitch] Prefer source format (#20850)
|
||||||
|
+ [twitcasting] Add support for private videos (#20843)
|
||||||
|
* [reddit] Validate thumbnail URL (#20030)
|
||||||
|
* [yandexmusic] Fix track URL extraction (#20820)
|
||||||
|
|
||||||
|
|
||||||
|
version 2019.04.24
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [youtube] Fix extraction (#20758, #20759, #20761, #20762, #20764, #20766,
|
||||||
|
#20767, #20769, #20771, #20768, #20770)
|
||||||
|
* [toutv] Fix extraction and extract series info (#20757)
|
||||||
|
+ [vrv] Add support for movie listings (#19229)
|
||||||
|
+ [youtube] Print error when no data is available (#20737)
|
||||||
|
+ [soundcloud] Add support for new rendition and improve extraction (#20699)
|
||||||
|
+ [ooyala] Add support for geo verification proxy
|
||||||
|
+ [nrl] Add support for nrl.com (#15991)
|
||||||
|
+ [vimeo] Extract live archive source format (#19144)
|
||||||
|
+ [vimeo] Add support for live streams and improve info extraction (#19144)
|
||||||
|
+ [ntvcojp] Add support for cu.ntv.co.jp
|
||||||
|
+ [nhk] Extract RTMPT format
|
||||||
|
+ [nhk] Add support for audio URLs
|
||||||
|
+ [udemy] Add another course id extraction pattern (#20491)
|
||||||
|
+ [openload] Add support for oload.services (#20691)
|
||||||
|
+ [openload] Add support for openloed.co (#20691, #20693)
|
||||||
|
* [bravotv] Fix extraction (#19213)
|
||||||
|
|
||||||
|
|
||||||
|
version 2019.04.17
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [openload] Randomize User-Agent (closes #20688)
|
||||||
|
+ [openload] Add support for oladblock domains (#20471)
|
||||||
|
* [adn] Fix subtitle extraction (#12724)
|
||||||
|
+ [aol] Add support for localized websites
|
||||||
|
+ [yahoo] Add support GYAO episode URLs
|
||||||
|
+ [yahoo] Add support for streaming.yahoo.co.jp (#5811, #7098)
|
||||||
|
+ [yahoo] Add support for gyao.yahoo.co.jp
|
||||||
|
* [aenetworks] Fix history topic extraction and extract more formats
|
||||||
|
+ [cbs] Extract smpte and vtt subtitles
|
||||||
|
+ [streamango] Add support for streamcherry.com (#20592)
|
||||||
|
+ [yourporn] Add support for sxyprn.com (#20646)
|
||||||
|
* [mgtv] Fix extraction (#20650)
|
||||||
|
* [linkedin:learning] Use urljoin for form action URL (#20431)
|
||||||
|
+ [gdc] Add support for kaltura embeds (#20575)
|
||||||
|
* [dispeak] Improve mp4 bitrate extraction
|
||||||
|
* [kaltura] Sanitize embed URLs
|
||||||
|
* [jwplatfom] Do not match manifest URLs (#20596)
|
||||||
|
* [aol] Restrict URL regular expression and improve format extraction
|
||||||
|
+ [tiktok] Add support for new URL schema (#20573)
|
||||||
|
+ [stv:player] Add support for player.stv.tv (#20586)
|
||||||
|
|
||||||
|
|
||||||
|
version 2019.04.07
|
||||||
|
|
||||||
|
Core
|
||||||
|
+ [downloader/external] Pass rtmp_conn to ffmpeg
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [ruutu] Add support for audio podcasts (#20473, #20545)
|
||||||
|
+ [xvideos] Extract all thumbnails (#20432)
|
||||||
|
+ [platzi] Add support for platzi.com (#20562)
|
||||||
|
* [dvtv] Fix extraction (#18514, #19174)
|
||||||
|
+ [vrv] Add basic support for individual movie links (#19229)
|
||||||
|
+ [bfi:player] Add support for player.bfi.org.uk (#19235)
|
||||||
|
* [hbo] Fix extraction and extract subtitles (#14629, #13709)
|
||||||
|
* [youtube] Extract srv[1-3] subtitle formats (#20566)
|
||||||
|
* [adultswim] Fix extraction (#18025)
|
||||||
|
* [teamcoco] Fix extraction and add suport for subdomains (#17099, #20339)
|
||||||
|
* [adn] Fix subtitle compatibility with ffmpeg
|
||||||
|
* [adn] Fix extraction and add support for positioning styles (#20549)
|
||||||
|
* [vk] Use unique video id (#17848)
|
||||||
|
* [newstube] Fix extraction
|
||||||
|
* [rtl2] Actualize extraction
|
||||||
|
+ [adobeconnect] Add support for adobeconnect.com (#20283)
|
||||||
|
+ [gaia] Add support for authentication (#14605)
|
||||||
|
+ [mediasite] Add support for dashed ids and named catalogs (#20531)
|
||||||
|
|
||||||
|
|
||||||
|
version 2019.04.01
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [utils] Improve int_or_none and float_or_none (#20403)
|
||||||
|
* Check for valid --min-sleep-interval when --max-sleep-interval is specified
|
||||||
|
(#20435)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [weibo] Extend URL regular expression (#20496)
|
||||||
|
+ [xhamster] Add support for xhamster.one (#20508)
|
||||||
|
+ [mediasite] Add support for catalogs (#20507)
|
||||||
|
+ [teamtreehouse] Add support for teamtreehouse.com (#9836)
|
||||||
|
+ [ina] Add support for audio URLs
|
||||||
|
* [ina] Improve extraction
|
||||||
|
* [cwtv] Fix episode number extraction (#20461)
|
||||||
|
* [npo] Improve DRM detection
|
||||||
|
+ [pornhub] Add support for DASH formats (#20403)
|
||||||
|
* [svtplay] Update API endpoint (#20430)
|
||||||
|
|
||||||
|
|
||||||
version 2019.03.18
|
version 2019.03.18
|
||||||
|
|
||||||
Core
|
Core
|
||||||
|
10
Makefile
10
Makefile
@@ -1,7 +1,7 @@
|
|||||||
all: youtube-dl README.md CONTRIBUTING.md README.txt youtube-dl.1 youtube-dl.bash-completion youtube-dl.zsh youtube-dl.fish supportedsites
|
all: youtube-dl README.md CONTRIBUTING.md README.txt youtube-dl.1 youtube-dl.bash-completion youtube-dl.zsh youtube-dl.fish supportedsites
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf youtube-dl.1.temp.md youtube-dl.1 youtube-dl.bash-completion README.txt MANIFEST build/ dist/ .coverage cover/ youtube-dl.tar.gz youtube-dl.zsh youtube-dl.fish youtube_dl/extractor/lazy_extractors.py *.dump *.part* *.ytdl *.info.json *.mp4 *.m4a *.flv *.mp3 *.avi *.mkv *.webm *.3gp *.wav *.ape *.swf *.jpg *.png CONTRIBUTING.md.tmp ISSUE_TEMPLATE.md.tmp youtube-dl youtube-dl.exe
|
rm -rf youtube-dl.1.temp.md youtube-dl.1 youtube-dl.bash-completion README.txt MANIFEST build/ dist/ .coverage cover/ youtube-dl.tar.gz youtube-dl.zsh youtube-dl.fish youtube_dl/extractor/lazy_extractors.py *.dump *.part* *.ytdl *.info.json *.mp4 *.m4a *.flv *.mp3 *.avi *.mkv *.webm *.3gp *.wav *.ape *.swf *.jpg *.png CONTRIBUTING.md.tmp youtube-dl youtube-dl.exe
|
||||||
find . -name "*.pyc" -delete
|
find . -name "*.pyc" -delete
|
||||||
find . -name "*.class" -delete
|
find . -name "*.class" -delete
|
||||||
|
|
||||||
@@ -78,8 +78,12 @@ README.md: youtube_dl/*.py youtube_dl/*/*.py
|
|||||||
CONTRIBUTING.md: README.md
|
CONTRIBUTING.md: README.md
|
||||||
$(PYTHON) devscripts/make_contributing.py README.md CONTRIBUTING.md
|
$(PYTHON) devscripts/make_contributing.py README.md CONTRIBUTING.md
|
||||||
|
|
||||||
.github/ISSUE_TEMPLATE.md: devscripts/make_issue_template.py .github/ISSUE_TEMPLATE_tmpl.md youtube_dl/version.py
|
issuetemplates: devscripts/make_issue_template.py .github/ISSUE_TEMPLATE_tmpl/1_broken_site.md .github/ISSUE_TEMPLATE_tmpl/2_site_support_request.md .github/ISSUE_TEMPLATE_tmpl/3_site_feature_request.md .github/ISSUE_TEMPLATE_tmpl/4_bug_report.md .github/ISSUE_TEMPLATE_tmpl/5_feature_request.md youtube_dl/version.py
|
||||||
$(PYTHON) devscripts/make_issue_template.py .github/ISSUE_TEMPLATE_tmpl.md .github/ISSUE_TEMPLATE.md
|
$(PYTHON) devscripts/make_issue_template.py .github/ISSUE_TEMPLATE_tmpl/1_broken_site.md .github/ISSUE_TEMPLATE/1_broken_site.md
|
||||||
|
$(PYTHON) devscripts/make_issue_template.py .github/ISSUE_TEMPLATE_tmpl/2_site_support_request.md .github/ISSUE_TEMPLATE/2_site_support_request.md
|
||||||
|
$(PYTHON) devscripts/make_issue_template.py .github/ISSUE_TEMPLATE_tmpl/3_site_feature_request.md .github/ISSUE_TEMPLATE/3_site_feature_request.md
|
||||||
|
$(PYTHON) devscripts/make_issue_template.py .github/ISSUE_TEMPLATE_tmpl/4_bug_report.md .github/ISSUE_TEMPLATE/4_bug_report.md
|
||||||
|
$(PYTHON) devscripts/make_issue_template.py .github/ISSUE_TEMPLATE_tmpl/5_feature_request.md .github/ISSUE_TEMPLATE/5_feature_request.md
|
||||||
|
|
||||||
supportedsites:
|
supportedsites:
|
||||||
$(PYTHON) devscripts/make_supportedsites.py docs/supportedsites.md
|
$(PYTHON) devscripts/make_supportedsites.py docs/supportedsites.md
|
||||||
|
@@ -642,6 +642,7 @@ The simplest case is requesting a specific format, for example with `-f 22` you
|
|||||||
You can also use a file extension (currently `3gp`, `aac`, `flv`, `m4a`, `mp3`, `mp4`, `ogg`, `wav`, `webm` are supported) to download the best quality format of a particular file extension served as a single file, e.g. `-f webm` will download the best quality format with the `webm` extension served as a single file.
|
You can also use a file extension (currently `3gp`, `aac`, `flv`, `m4a`, `mp3`, `mp4`, `ogg`, `wav`, `webm` are supported) to download the best quality format of a particular file extension served as a single file, e.g. `-f webm` will download the best quality format with the `webm` extension served as a single file.
|
||||||
|
|
||||||
You can also use special names to select particular edge case formats:
|
You can also use special names to select particular edge case formats:
|
||||||
|
|
||||||
- `best`: Select the best quality format represented by a single file with video and audio.
|
- `best`: Select the best quality format represented by a single file with video and audio.
|
||||||
- `worst`: Select the worst quality format represented by a single file with video and audio.
|
- `worst`: Select the worst quality format represented by a single file with video and audio.
|
||||||
- `bestvideo`: Select the best quality video-only format (e.g. DASH video). May not be available.
|
- `bestvideo`: Select the best quality video-only format (e.g. DASH video). May not be available.
|
||||||
@@ -658,6 +659,7 @@ If you want to download several formats of the same video use a comma as a separ
|
|||||||
You can also filter the video formats by putting a condition in brackets, as in `-f "best[height=720]"` (or `-f "[filesize>10M]"`).
|
You can also filter the video formats by putting a condition in brackets, as in `-f "best[height=720]"` (or `-f "[filesize>10M]"`).
|
||||||
|
|
||||||
The following numeric meta fields can be used with comparisons `<`, `<=`, `>`, `>=`, `=` (equals), `!=` (not equals):
|
The following numeric meta fields can be used with comparisons `<`, `<=`, `>`, `>=`, `=` (equals), `!=` (not equals):
|
||||||
|
|
||||||
- `filesize`: The number of bytes, if known in advance
|
- `filesize`: The number of bytes, if known in advance
|
||||||
- `width`: Width of the video, if known
|
- `width`: Width of the video, if known
|
||||||
- `height`: Height of the video, if known
|
- `height`: Height of the video, if known
|
||||||
@@ -668,6 +670,7 @@ The following numeric meta fields can be used with comparisons `<`, `<=`, `>`, `
|
|||||||
- `fps`: Frame rate
|
- `fps`: Frame rate
|
||||||
|
|
||||||
Also filtering work for comparisons `=` (equals), `^=` (starts with), `$=` (ends with), `*=` (contains) and following string meta fields:
|
Also filtering work for comparisons `=` (equals), `^=` (starts with), `$=` (ends with), `*=` (contains) and following string meta fields:
|
||||||
|
|
||||||
- `ext`: File extension
|
- `ext`: File extension
|
||||||
- `acodec`: Name of the audio codec in use
|
- `acodec`: Name of the audio codec in use
|
||||||
- `vcodec`: Name of the video codec in use
|
- `vcodec`: Name of the video codec in use
|
||||||
@@ -697,7 +700,7 @@ Note that on Windows you may need to use double quotes instead of single.
|
|||||||
# Download best mp4 format available or any other best if no mp4 available
|
# Download best mp4 format available or any other best if no mp4 available
|
||||||
$ youtube-dl -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best'
|
$ youtube-dl -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best'
|
||||||
|
|
||||||
# Download best format available but not better that 480p
|
# Download best format available but no better than 480p
|
||||||
$ youtube-dl -f 'bestvideo[height<=480]+bestaudio/best[height<=480]'
|
$ youtube-dl -f 'bestvideo[height<=480]+bestaudio/best[height<=480]'
|
||||||
|
|
||||||
# Download best video only format but no bigger than 50 MB
|
# Download best video only format but no bigger than 50 MB
|
||||||
|
@@ -45,12 +45,12 @@ for test in gettestcases():
|
|||||||
|
|
||||||
RESULT = ('.' + domain + '\n' in LIST or '\n' + domain + '\n' in LIST)
|
RESULT = ('.' + domain + '\n' in LIST or '\n' + domain + '\n' in LIST)
|
||||||
|
|
||||||
if RESULT and ('info_dict' not in test or 'age_limit' not in test['info_dict'] or
|
if RESULT and ('info_dict' not in test or 'age_limit' not in test['info_dict']
|
||||||
test['info_dict']['age_limit'] != 18):
|
or test['info_dict']['age_limit'] != 18):
|
||||||
print('\nPotential missing age_limit check: {0}'.format(test['name']))
|
print('\nPotential missing age_limit check: {0}'.format(test['name']))
|
||||||
|
|
||||||
elif not RESULT and ('info_dict' in test and 'age_limit' in test['info_dict'] and
|
elif not RESULT and ('info_dict' in test and 'age_limit' in test['info_dict']
|
||||||
test['info_dict']['age_limit'] == 18):
|
and test['info_dict']['age_limit'] == 18):
|
||||||
print('\nPotential false negative: {0}'.format(test['name']))
|
print('\nPotential false negative: {0}'.format(test['name']))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@@ -78,8 +78,8 @@ sed -i "s/__version__ = '.*'/__version__ = '$version'/" youtube_dl/version.py
|
|||||||
sed -i "s/<unreleased>/$version/" ChangeLog
|
sed -i "s/<unreleased>/$version/" ChangeLog
|
||||||
|
|
||||||
/bin/echo -e "\n### Committing documentation, templates and youtube_dl/version.py..."
|
/bin/echo -e "\n### Committing documentation, templates and youtube_dl/version.py..."
|
||||||
make README.md CONTRIBUTING.md .github/ISSUE_TEMPLATE.md supportedsites
|
make README.md CONTRIBUTING.md issuetemplates supportedsites
|
||||||
git add README.md CONTRIBUTING.md .github/ISSUE_TEMPLATE.md docs/supportedsites.md youtube_dl/version.py ChangeLog
|
git add README.md CONTRIBUTING.md .github/ISSUE_TEMPLATE/1_broken_site.md .github/ISSUE_TEMPLATE/2_site_support_request.md .github/ISSUE_TEMPLATE/3_site_feature_request.md .github/ISSUE_TEMPLATE/4_bug_report.md .github/ISSUE_TEMPLATE/5_feature_request.md .github/ISSUE_TEMPLATE/6_question.md docs/supportedsites.md youtube_dl/version.py ChangeLog
|
||||||
git commit $gpg_sign_commits -m "release $version"
|
git commit $gpg_sign_commits -m "release $version"
|
||||||
|
|
||||||
/bin/echo -e "\n### Now tagging, signing and pushing..."
|
/bin/echo -e "\n### Now tagging, signing and pushing..."
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
- **acast:channel**
|
- **acast:channel**
|
||||||
- **AddAnime**
|
- **AddAnime**
|
||||||
- **ADN**: Anime Digital Network
|
- **ADN**: Anime Digital Network
|
||||||
|
- **AdobeConnect**
|
||||||
- **AdobeTV**
|
- **AdobeTV**
|
||||||
- **AdobeTVChannel**
|
- **AdobeTVChannel**
|
||||||
- **AdobeTVShow**
|
- **AdobeTVShow**
|
||||||
@@ -45,6 +46,7 @@
|
|||||||
- **anderetijden**: npo.nl, ntr.nl, omroepwnl.nl, zapp.nl and npo3.nl
|
- **anderetijden**: npo.nl, ntr.nl, omroepwnl.nl, zapp.nl and npo3.nl
|
||||||
- **AnimeOnDemand**
|
- **AnimeOnDemand**
|
||||||
- **Anvato**
|
- **Anvato**
|
||||||
|
- **aol.com**
|
||||||
- **APA**
|
- **APA**
|
||||||
- **Aparat**
|
- **Aparat**
|
||||||
- **AppleConnect**
|
- **AppleConnect**
|
||||||
@@ -76,7 +78,6 @@
|
|||||||
- **AudioBoom**
|
- **AudioBoom**
|
||||||
- **audiomack**
|
- **audiomack**
|
||||||
- **audiomack:album**
|
- **audiomack:album**
|
||||||
- **auroravid**: AuroraVid
|
|
||||||
- **AWAAN**
|
- **AWAAN**
|
||||||
- **awaan:live**
|
- **awaan:live**
|
||||||
- **awaan:season**
|
- **awaan:season**
|
||||||
@@ -101,6 +102,7 @@
|
|||||||
- **Bellator**
|
- **Bellator**
|
||||||
- **BellMedia**
|
- **BellMedia**
|
||||||
- **Bet**
|
- **Bet**
|
||||||
|
- **bfi:player**
|
||||||
- **Bigflix**
|
- **Bigflix**
|
||||||
- **Bild**: Bild.de
|
- **Bild**: Bild.de
|
||||||
- **BiliBili**
|
- **BiliBili**
|
||||||
@@ -147,6 +149,7 @@
|
|||||||
- **CBSInteractive**
|
- **CBSInteractive**
|
||||||
- **CBSLocal**
|
- **CBSLocal**
|
||||||
- **cbsnews**: CBS News
|
- **cbsnews**: CBS News
|
||||||
|
- **cbsnews:embed**
|
||||||
- **cbsnews:livevideo**: CBS News Live Videos
|
- **cbsnews:livevideo**: CBS News Live Videos
|
||||||
- **CBSSports**
|
- **CBSSports**
|
||||||
- **CCMA**
|
- **CCMA**
|
||||||
@@ -161,6 +164,7 @@
|
|||||||
- **chirbit**
|
- **chirbit**
|
||||||
- **chirbit:profile**
|
- **chirbit:profile**
|
||||||
- **Cinchcast**
|
- **Cinchcast**
|
||||||
|
- **Cinemax**
|
||||||
- **CiscoLiveSearch**
|
- **CiscoLiveSearch**
|
||||||
- **CiscoLiveSession**
|
- **CiscoLiveSession**
|
||||||
- **CJSW**
|
- **CJSW**
|
||||||
@@ -170,7 +174,6 @@
|
|||||||
- **Clipsyndicate**
|
- **Clipsyndicate**
|
||||||
- **CloserToTruth**
|
- **CloserToTruth**
|
||||||
- **CloudflareStream**
|
- **CloudflareStream**
|
||||||
- **cloudtime**: CloudTime
|
|
||||||
- **Cloudy**
|
- **Cloudy**
|
||||||
- **Clubic**
|
- **Clubic**
|
||||||
- **Clyp**
|
- **Clyp**
|
||||||
@@ -190,7 +193,6 @@
|
|||||||
- **Coub**
|
- **Coub**
|
||||||
- **Cracked**
|
- **Cracked**
|
||||||
- **Crackle**
|
- **Crackle**
|
||||||
- **Criterion**
|
|
||||||
- **CrooksAndLiars**
|
- **CrooksAndLiars**
|
||||||
- **crunchyroll**
|
- **crunchyroll**
|
||||||
- **crunchyroll:playlist**
|
- **crunchyroll:playlist**
|
||||||
@@ -198,6 +200,7 @@
|
|||||||
- **CSpan**: C-SPAN
|
- **CSpan**: C-SPAN
|
||||||
- **CtsNews**: 華視新聞
|
- **CtsNews**: 華視新聞
|
||||||
- **CTVNews**
|
- **CTVNews**
|
||||||
|
- **cu.ntv.co.jp**: Nippon Television Network
|
||||||
- **Culturebox**
|
- **Culturebox**
|
||||||
- **CultureUnplugged**
|
- **CultureUnplugged**
|
||||||
- **curiositystream**
|
- **curiositystream**
|
||||||
@@ -233,8 +236,6 @@
|
|||||||
- **DouyuTV**: 斗鱼
|
- **DouyuTV**: 斗鱼
|
||||||
- **DPlay**
|
- **DPlay**
|
||||||
- **DPlayIt**
|
- **DPlayIt**
|
||||||
- **dramafever**
|
|
||||||
- **dramafever:series**
|
|
||||||
- **DRBonanza**
|
- **DRBonanza**
|
||||||
- **Dropbox**
|
- **Dropbox**
|
||||||
- **DrTuber**
|
- **DrTuber**
|
||||||
@@ -345,7 +346,6 @@
|
|||||||
- **Groupon**
|
- **Groupon**
|
||||||
- **Hark**
|
- **Hark**
|
||||||
- **hbo**
|
- **hbo**
|
||||||
- **hbo:episode**
|
|
||||||
- **HearThisAt**
|
- **HearThisAt**
|
||||||
- **Heise**
|
- **Heise**
|
||||||
- **HellPorno**
|
- **HellPorno**
|
||||||
@@ -485,9 +485,12 @@
|
|||||||
- **MatchTV**
|
- **MatchTV**
|
||||||
- **MDR**: MDR.DE and KiKA
|
- **MDR**: MDR.DE and KiKA
|
||||||
- **media.ccc.de**
|
- **media.ccc.de**
|
||||||
|
- **media.ccc.de:lists**
|
||||||
- **Medialaan**
|
- **Medialaan**
|
||||||
- **Mediaset**
|
- **Mediaset**
|
||||||
- **Mediasite**
|
- **Mediasite**
|
||||||
|
- **MediasiteCatalog**
|
||||||
|
- **MediasiteNamedCatalog**
|
||||||
- **Medici**
|
- **Medici**
|
||||||
- **megaphone.fm**: megaphone.fm embedded players
|
- **megaphone.fm**: megaphone.fm embedded players
|
||||||
- **Meipai**: 美拍
|
- **Meipai**: 美拍
|
||||||
@@ -604,7 +607,6 @@
|
|||||||
- **nowness**
|
- **nowness**
|
||||||
- **nowness:playlist**
|
- **nowness:playlist**
|
||||||
- **nowness:series**
|
- **nowness:series**
|
||||||
- **nowvideo**: NowVideo
|
|
||||||
- **Noz**
|
- **Noz**
|
||||||
- **npo**: npo.nl, ntr.nl, omroepwnl.nl, zapp.nl and npo3.nl
|
- **npo**: npo.nl, ntr.nl, omroepwnl.nl, zapp.nl and npo3.nl
|
||||||
- **npo.nl:live**
|
- **npo.nl:live**
|
||||||
@@ -620,6 +622,7 @@
|
|||||||
- **NRKTVEpisodes**
|
- **NRKTVEpisodes**
|
||||||
- **NRKTVSeason**
|
- **NRKTVSeason**
|
||||||
- **NRKTVSeries**
|
- **NRKTVSeries**
|
||||||
|
- **NRLTV**
|
||||||
- **ntv.ru**
|
- **ntv.ru**
|
||||||
- **Nuvid**
|
- **Nuvid**
|
||||||
- **NYTimes**
|
- **NYTimes**
|
||||||
@@ -629,7 +632,6 @@
|
|||||||
- **OdaTV**
|
- **OdaTV**
|
||||||
- **Odnoklassniki**
|
- **Odnoklassniki**
|
||||||
- **OktoberfestTV**
|
- **OktoberfestTV**
|
||||||
- **on.aol.com**
|
|
||||||
- **OnDemandKorea**
|
- **OnDemandKorea**
|
||||||
- **onet.pl**
|
- **onet.pl**
|
||||||
- **onet.tv**
|
- **onet.tv**
|
||||||
@@ -670,6 +672,8 @@
|
|||||||
- **Piksel**
|
- **Piksel**
|
||||||
- **Pinkbike**
|
- **Pinkbike**
|
||||||
- **Pladform**
|
- **Pladform**
|
||||||
|
- **Platzi**
|
||||||
|
- **PlatziCourse**
|
||||||
- **play.fm**
|
- **play.fm**
|
||||||
- **PlayPlusTV**
|
- **PlayPlusTV**
|
||||||
- **PlaysTV**
|
- **PlaysTV**
|
||||||
@@ -686,7 +690,6 @@
|
|||||||
- **PopcornTV**
|
- **PopcornTV**
|
||||||
- **PornCom**
|
- **PornCom**
|
||||||
- **PornerBros**
|
- **PornerBros**
|
||||||
- **PornFlip**
|
|
||||||
- **PornHd**
|
- **PornHd**
|
||||||
- **PornHub**: PornHub and Thumbzilla
|
- **PornHub**: PornHub and Thumbzilla
|
||||||
- **PornHubPlaylist**
|
- **PornHubPlaylist**
|
||||||
@@ -727,6 +730,7 @@
|
|||||||
- **RBMARadio**
|
- **RBMARadio**
|
||||||
- **RDS**: RDS.ca
|
- **RDS**: RDS.ca
|
||||||
- **RedBullTV**
|
- **RedBullTV**
|
||||||
|
- **RedBullTVRrnContent**
|
||||||
- **Reddit**
|
- **Reddit**
|
||||||
- **RedditR**
|
- **RedditR**
|
||||||
- **RedTube**
|
- **RedTube**
|
||||||
@@ -798,6 +802,7 @@
|
|||||||
- **ShowRoomLive**
|
- **ShowRoomLive**
|
||||||
- **Sina**
|
- **Sina**
|
||||||
- **SkylineWebcams**
|
- **SkylineWebcams**
|
||||||
|
- **SkyNews**
|
||||||
- **skynewsarabia:article**
|
- **skynewsarabia:article**
|
||||||
- **skynewsarabia:video**
|
- **skynewsarabia:video**
|
||||||
- **SkySports**
|
- **SkySports**
|
||||||
@@ -848,7 +853,10 @@
|
|||||||
- **StreamCZ**
|
- **StreamCZ**
|
||||||
- **StreetVoice**
|
- **StreetVoice**
|
||||||
- **StretchInternet**
|
- **StretchInternet**
|
||||||
|
- **stv:player**
|
||||||
- **SunPorno**
|
- **SunPorno**
|
||||||
|
- **sverigesradio:episode**
|
||||||
|
- **sverigesradio:publication**
|
||||||
- **SVT**
|
- **SVT**
|
||||||
- **SVTPage**
|
- **SVTPage**
|
||||||
- **SVTPlay**: SVT Play and Öppet arkiv
|
- **SVTPlay**: SVT Play and Öppet arkiv
|
||||||
@@ -869,6 +877,7 @@
|
|||||||
- **teachertube:user:collection**: teachertube.com user and collection videos
|
- **teachertube:user:collection**: teachertube.com user and collection videos
|
||||||
- **TeachingChannel**
|
- **TeachingChannel**
|
||||||
- **Teamcoco**
|
- **Teamcoco**
|
||||||
|
- **TeamTreeHouse**
|
||||||
- **TechTalks**
|
- **TechTalks**
|
||||||
- **techtv.mit.edu**
|
- **techtv.mit.edu**
|
||||||
- **ted**
|
- **ted**
|
||||||
@@ -988,6 +997,7 @@
|
|||||||
- **Vbox7**
|
- **Vbox7**
|
||||||
- **VeeHD**
|
- **VeeHD**
|
||||||
- **Veoh**
|
- **Veoh**
|
||||||
|
- **verystream**
|
||||||
- **Vessel**
|
- **Vessel**
|
||||||
- **Vesti**: Вести.Ru
|
- **Vesti**: Вести.Ru
|
||||||
- **Vevo**
|
- **Vevo**
|
||||||
@@ -1011,7 +1021,6 @@
|
|||||||
- **videomore:video**
|
- **videomore:video**
|
||||||
- **VideoPremium**
|
- **VideoPremium**
|
||||||
- **VideoPress**
|
- **VideoPress**
|
||||||
- **videoweed**: VideoWeed
|
|
||||||
- **Vidio**
|
- **Vidio**
|
||||||
- **VidLii**
|
- **VidLii**
|
||||||
- **vidme**
|
- **vidme**
|
||||||
@@ -1058,7 +1067,7 @@
|
|||||||
- **VoxMediaVolume**
|
- **VoxMediaVolume**
|
||||||
- **vpro**: npo.nl, ntr.nl, omroepwnl.nl, zapp.nl and npo3.nl
|
- **vpro**: npo.nl, ntr.nl, omroepwnl.nl, zapp.nl and npo3.nl
|
||||||
- **Vrak**
|
- **Vrak**
|
||||||
- **VRT**: deredactie.be, sporza.be, cobra.be and cobra.canvas.be
|
- **VRT**: VRT NWS, Flanders News, Flandern Info and Sporza
|
||||||
- **VrtNU**: VrtNU.be
|
- **VrtNU**: VrtNU.be
|
||||||
- **vrv**
|
- **vrv**
|
||||||
- **vrv:series**
|
- **vrv:series**
|
||||||
@@ -1088,13 +1097,10 @@
|
|||||||
- **Weibo**
|
- **Weibo**
|
||||||
- **WeiboMobile**
|
- **WeiboMobile**
|
||||||
- **WeiqiTV**: WQTV
|
- **WeiqiTV**: WQTV
|
||||||
- **wholecloud**: WholeCloud
|
|
||||||
- **Wimp**
|
- **Wimp**
|
||||||
- **Wistia**
|
- **Wistia**
|
||||||
- **wnl**: npo.nl, ntr.nl, omroepwnl.nl, zapp.nl and npo3.nl
|
- **wnl**: npo.nl, ntr.nl, omroepwnl.nl, zapp.nl and npo3.nl
|
||||||
- **WorldStarHipHop**
|
- **WorldStarHipHop**
|
||||||
- **wrzuta.pl**
|
|
||||||
- **wrzuta.pl:playlist**
|
|
||||||
- **WSJ**: Wall Street Journal
|
- **WSJ**: Wall Street Journal
|
||||||
- **WSJArticle**
|
- **WSJArticle**
|
||||||
- **WWE**
|
- **WWE**
|
||||||
@@ -1118,6 +1124,8 @@
|
|||||||
- **XVideos**
|
- **XVideos**
|
||||||
- **XXXYMovies**
|
- **XXXYMovies**
|
||||||
- **Yahoo**: Yahoo screen and movies
|
- **Yahoo**: Yahoo screen and movies
|
||||||
|
- **yahoo:gyao**
|
||||||
|
- **yahoo:gyao:player**
|
||||||
- **YandexDisk**
|
- **YandexDisk**
|
||||||
- **yandexmusic:album**: Яндекс.Музыка - Альбом
|
- **yandexmusic:album**: Яндекс.Музыка - Альбом
|
||||||
- **yandexmusic:playlist**: Яндекс.Музыка - Плейлист
|
- **yandexmusic:playlist**: Яндекс.Музыка - Плейлист
|
||||||
|
@@ -3,4 +3,4 @@ universal = True
|
|||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
exclude = youtube_dl/extractor/__init__.py,devscripts/buildserver.py,devscripts/lazy_load_template.py,devscripts/make_issue_template.py,setup.py,build,.git,venv
|
exclude = youtube_dl/extractor/__init__.py,devscripts/buildserver.py,devscripts/lazy_load_template.py,devscripts/make_issue_template.py,setup.py,build,.git,venv
|
||||||
ignore = E402,E501,E731,E741
|
ignore = E402,E501,E731,E741,W503
|
||||||
|
@@ -44,16 +44,16 @@ class TestAES(unittest.TestCase):
|
|||||||
def test_decrypt_text(self):
|
def test_decrypt_text(self):
|
||||||
password = intlist_to_bytes(self.key).decode('utf-8')
|
password = intlist_to_bytes(self.key).decode('utf-8')
|
||||||
encrypted = base64.b64encode(
|
encrypted = base64.b64encode(
|
||||||
intlist_to_bytes(self.iv[:8]) +
|
intlist_to_bytes(self.iv[:8])
|
||||||
b'\x17\x15\x93\xab\x8d\x80V\xcdV\xe0\t\xcdo\xc2\xa5\xd8ksM\r\xe27N\xae'
|
+ b'\x17\x15\x93\xab\x8d\x80V\xcdV\xe0\t\xcdo\xc2\xa5\xd8ksM\r\xe27N\xae'
|
||||||
).decode('utf-8')
|
).decode('utf-8')
|
||||||
decrypted = (aes_decrypt_text(encrypted, password, 16))
|
decrypted = (aes_decrypt_text(encrypted, password, 16))
|
||||||
self.assertEqual(decrypted, self.secret_msg)
|
self.assertEqual(decrypted, self.secret_msg)
|
||||||
|
|
||||||
password = intlist_to_bytes(self.key).decode('utf-8')
|
password = intlist_to_bytes(self.key).decode('utf-8')
|
||||||
encrypted = base64.b64encode(
|
encrypted = base64.b64encode(
|
||||||
intlist_to_bytes(self.iv[:8]) +
|
intlist_to_bytes(self.iv[:8])
|
||||||
b'\x0b\xe6\xa4\xd9z\x0e\xb8\xb9\xd0\xd4i_\x85\x1d\x99\x98_\xe5\x80\xe7.\xbf\xa5\x83'
|
+ b'\x0b\xe6\xa4\xd9z\x0e\xb8\xb9\xd0\xd4i_\x85\x1d\x99\x98_\xe5\x80\xe7.\xbf\xa5\x83'
|
||||||
).decode('utf-8')
|
).decode('utf-8')
|
||||||
decrypted = (aes_decrypt_text(encrypted, password, 32))
|
decrypted = (aes_decrypt_text(encrypted, password, 32))
|
||||||
self.assertEqual(decrypted, self.secret_msg)
|
self.assertEqual(decrypted, self.secret_msg)
|
||||||
|
@@ -34,8 +34,8 @@ def _make_testfunc(testfile):
|
|||||||
def test_func(self):
|
def test_func(self):
|
||||||
as_file = os.path.join(TEST_DIR, testfile)
|
as_file = os.path.join(TEST_DIR, testfile)
|
||||||
swf_file = os.path.join(TEST_DIR, test_id + '.swf')
|
swf_file = os.path.join(TEST_DIR, test_id + '.swf')
|
||||||
if ((not os.path.exists(swf_file)) or
|
if ((not os.path.exists(swf_file))
|
||||||
os.path.getmtime(swf_file) < os.path.getmtime(as_file)):
|
or os.path.getmtime(swf_file) < os.path.getmtime(as_file)):
|
||||||
# Recompile
|
# Recompile
|
||||||
try:
|
try:
|
||||||
subprocess.check_call([
|
subprocess.check_call([
|
||||||
|
@@ -33,11 +33,13 @@ from youtube_dl.utils import (
|
|||||||
ExtractorError,
|
ExtractorError,
|
||||||
find_xpath_attr,
|
find_xpath_attr,
|
||||||
fix_xml_ampersands,
|
fix_xml_ampersands,
|
||||||
|
float_or_none,
|
||||||
get_element_by_class,
|
get_element_by_class,
|
||||||
get_element_by_attribute,
|
get_element_by_attribute,
|
||||||
get_elements_by_class,
|
get_elements_by_class,
|
||||||
get_elements_by_attribute,
|
get_elements_by_attribute,
|
||||||
InAdvancePagedList,
|
InAdvancePagedList,
|
||||||
|
int_or_none,
|
||||||
intlist_to_bytes,
|
intlist_to_bytes,
|
||||||
is_html,
|
is_html,
|
||||||
js_to_json,
|
js_to_json,
|
||||||
@@ -71,6 +73,7 @@ from youtube_dl.utils import (
|
|||||||
smuggle_url,
|
smuggle_url,
|
||||||
str_to_int,
|
str_to_int,
|
||||||
strip_jsonp,
|
strip_jsonp,
|
||||||
|
strip_or_none,
|
||||||
timeconvert,
|
timeconvert,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
unified_strdate,
|
unified_strdate,
|
||||||
@@ -181,7 +184,7 @@ class TestUtil(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(sanitize_filename(
|
self.assertEqual(sanitize_filename(
|
||||||
'ÂÃÄÀÁÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖŐØŒÙÚÛÜŰÝÞßàáâãäåæçèéêëìíîïðñòóôõöőøœùúûüűýþÿ', restricted=True),
|
'ÂÃÄÀÁÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖŐØŒÙÚÛÜŰÝÞßàáâãäåæçèéêëìíîïðñòóôõöőøœùúûüűýþÿ', restricted=True),
|
||||||
'AAAAAAAECEEEEIIIIDNOOOOOOOOEUUUUUYPssaaaaaaaeceeeeiiiionooooooooeuuuuuypy')
|
'AAAAAAAECEEEEIIIIDNOOOOOOOOEUUUUUYTHssaaaaaaaeceeeeiiiionooooooooeuuuuuythy')
|
||||||
|
|
||||||
def test_sanitize_ids(self):
|
def test_sanitize_ids(self):
|
||||||
self.assertEqual(sanitize_filename('_n_cd26wFpw', is_id=True), '_n_cd26wFpw')
|
self.assertEqual(sanitize_filename('_n_cd26wFpw', is_id=True), '_n_cd26wFpw')
|
||||||
@@ -468,6 +471,21 @@ class TestUtil(unittest.TestCase):
|
|||||||
shell_quote(args),
|
shell_quote(args),
|
||||||
"""ffmpeg -i 'ñ€ß'"'"'.mp4'""" if compat_os_name != 'nt' else '''ffmpeg -i "ñ€ß'.mp4"''')
|
"""ffmpeg -i 'ñ€ß'"'"'.mp4'""" if compat_os_name != 'nt' else '''ffmpeg -i "ñ€ß'.mp4"''')
|
||||||
|
|
||||||
|
def test_float_or_none(self):
|
||||||
|
self.assertEqual(float_or_none('42.42'), 42.42)
|
||||||
|
self.assertEqual(float_or_none('42'), 42.0)
|
||||||
|
self.assertEqual(float_or_none(''), None)
|
||||||
|
self.assertEqual(float_or_none(None), None)
|
||||||
|
self.assertEqual(float_or_none([]), None)
|
||||||
|
self.assertEqual(float_or_none(set()), None)
|
||||||
|
|
||||||
|
def test_int_or_none(self):
|
||||||
|
self.assertEqual(int_or_none('42'), 42)
|
||||||
|
self.assertEqual(int_or_none(''), None)
|
||||||
|
self.assertEqual(int_or_none(None), None)
|
||||||
|
self.assertEqual(int_or_none([]), None)
|
||||||
|
self.assertEqual(int_or_none(set()), None)
|
||||||
|
|
||||||
def test_str_to_int(self):
|
def test_str_to_int(self):
|
||||||
self.assertEqual(str_to_int('123,456'), 123456)
|
self.assertEqual(str_to_int('123,456'), 123456)
|
||||||
self.assertEqual(str_to_int('123.456'), 123456)
|
self.assertEqual(str_to_int('123.456'), 123456)
|
||||||
@@ -735,6 +753,18 @@ class TestUtil(unittest.TestCase):
|
|||||||
d = json.loads(stripped)
|
d = json.loads(stripped)
|
||||||
self.assertEqual(d, {'status': 'success'})
|
self.assertEqual(d, {'status': 'success'})
|
||||||
|
|
||||||
|
def test_strip_or_none(self):
|
||||||
|
self.assertEqual(strip_or_none(' abc'), 'abc')
|
||||||
|
self.assertEqual(strip_or_none('abc '), 'abc')
|
||||||
|
self.assertEqual(strip_or_none(' abc '), 'abc')
|
||||||
|
self.assertEqual(strip_or_none('\tabc\t'), 'abc')
|
||||||
|
self.assertEqual(strip_or_none('\n\tabc\n\t'), 'abc')
|
||||||
|
self.assertEqual(strip_or_none('abc'), 'abc')
|
||||||
|
self.assertEqual(strip_or_none(''), '')
|
||||||
|
self.assertEqual(strip_or_none(None), None)
|
||||||
|
self.assertEqual(strip_or_none(42), None)
|
||||||
|
self.assertEqual(strip_or_none([]), None)
|
||||||
|
|
||||||
def test_uppercase_escape(self):
|
def test_uppercase_escape(self):
|
||||||
self.assertEqual(uppercase_escape('aä'), 'aä')
|
self.assertEqual(uppercase_escape('aä'), 'aä')
|
||||||
self.assertEqual(uppercase_escape('\\U0001d550'), '𝕐')
|
self.assertEqual(uppercase_escape('\\U0001d550'), '𝕐')
|
||||||
|
@@ -309,6 +309,8 @@ class YoutubeDL(object):
|
|||||||
The following options are used by the post processors:
|
The following options are used by the post processors:
|
||||||
prefer_ffmpeg: If False, use avconv instead of ffmpeg if both are available,
|
prefer_ffmpeg: If False, use avconv instead of ffmpeg if both are available,
|
||||||
otherwise prefer ffmpeg.
|
otherwise prefer ffmpeg.
|
||||||
|
ffmpeg_location: Location of the ffmpeg/avconv binary; either the path
|
||||||
|
to the binary or its containing directory.
|
||||||
postprocessor_args: A list of additional command-line arguments for the
|
postprocessor_args: A list of additional command-line arguments for the
|
||||||
postprocessor.
|
postprocessor.
|
||||||
|
|
||||||
@@ -398,9 +400,9 @@ class YoutubeDL(object):
|
|||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if (sys.platform != 'win32' and
|
if (sys.platform != 'win32'
|
||||||
sys.getfilesystemencoding() in ['ascii', 'ANSI_X3.4-1968'] and
|
and sys.getfilesystemencoding() in ['ascii', 'ANSI_X3.4-1968']
|
||||||
not params.get('restrictfilenames', False)):
|
and not params.get('restrictfilenames', False)):
|
||||||
# Unicode filesystem API will throw errors (#1474, #13027)
|
# Unicode filesystem API will throw errors (#1474, #13027)
|
||||||
self.report_warning(
|
self.report_warning(
|
||||||
'Assuming --restrict-filenames since file system encoding '
|
'Assuming --restrict-filenames since file system encoding '
|
||||||
@@ -438,9 +440,9 @@ class YoutubeDL(object):
|
|||||||
if re.match(r'^-[0-9A-Za-z_-]{10}$', a)]
|
if re.match(r'^-[0-9A-Za-z_-]{10}$', a)]
|
||||||
if idxs:
|
if idxs:
|
||||||
correct_argv = (
|
correct_argv = (
|
||||||
['youtube-dl'] +
|
['youtube-dl']
|
||||||
[a for i, a in enumerate(argv) if i not in idxs] +
|
+ [a for i, a in enumerate(argv) if i not in idxs]
|
||||||
['--'] + [argv[i] for i in idxs]
|
+ ['--'] + [argv[i] for i in idxs]
|
||||||
)
|
)
|
||||||
self.report_warning(
|
self.report_warning(
|
||||||
'Long argument string detected. '
|
'Long argument string detected. '
|
||||||
@@ -848,8 +850,8 @@ class YoutubeDL(object):
|
|||||||
if result_type in ('url', 'url_transparent'):
|
if result_type in ('url', 'url_transparent'):
|
||||||
ie_result['url'] = sanitize_url(ie_result['url'])
|
ie_result['url'] = sanitize_url(ie_result['url'])
|
||||||
extract_flat = self.params.get('extract_flat', False)
|
extract_flat = self.params.get('extract_flat', False)
|
||||||
if ((extract_flat == 'in_playlist' and 'playlist' in extra_info) or
|
if ((extract_flat == 'in_playlist' and 'playlist' in extra_info)
|
||||||
extract_flat is True):
|
or extract_flat is True):
|
||||||
if self.params.get('forcejson', False):
|
if self.params.get('forcejson', False):
|
||||||
self.to_stdout(json.dumps(ie_result))
|
self.to_stdout(json.dumps(ie_result))
|
||||||
return ie_result
|
return ie_result
|
||||||
@@ -1617,9 +1619,9 @@ class YoutubeDL(object):
|
|||||||
# https://github.com/ytdl-org/youtube-dl/issues/10083).
|
# https://github.com/ytdl-org/youtube-dl/issues/10083).
|
||||||
incomplete_formats = (
|
incomplete_formats = (
|
||||||
# All formats are video-only or
|
# All formats are video-only or
|
||||||
all(f.get('vcodec') != 'none' and f.get('acodec') == 'none' for f in formats) or
|
all(f.get('vcodec') != 'none' and f.get('acodec') == 'none' for f in formats)
|
||||||
# all formats are audio-only
|
# all formats are audio-only
|
||||||
all(f.get('vcodec') == 'none' and f.get('acodec') != 'none' for f in formats))
|
or all(f.get('vcodec') == 'none' and f.get('acodec') != 'none' for f in formats))
|
||||||
|
|
||||||
ctx = {
|
ctx = {
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
@@ -1945,8 +1947,8 @@ class YoutubeDL(object):
|
|||||||
else:
|
else:
|
||||||
assert fixup_policy in ('ignore', 'never')
|
assert fixup_policy in ('ignore', 'never')
|
||||||
|
|
||||||
if (info_dict.get('requested_formats') is None and
|
if (info_dict.get('requested_formats') is None
|
||||||
info_dict.get('container') == 'm4a_dash'):
|
and info_dict.get('container') == 'm4a_dash'):
|
||||||
if fixup_policy == 'warn':
|
if fixup_policy == 'warn':
|
||||||
self.report_warning(
|
self.report_warning(
|
||||||
'%s: writing DASH m4a. '
|
'%s: writing DASH m4a. '
|
||||||
@@ -1965,9 +1967,9 @@ class YoutubeDL(object):
|
|||||||
else:
|
else:
|
||||||
assert fixup_policy in ('ignore', 'never')
|
assert fixup_policy in ('ignore', 'never')
|
||||||
|
|
||||||
if (info_dict.get('protocol') == 'm3u8_native' or
|
if (info_dict.get('protocol') == 'm3u8_native'
|
||||||
info_dict.get('protocol') == 'm3u8' and
|
or info_dict.get('protocol') == 'm3u8'
|
||||||
self.params.get('hls_prefer_native')):
|
and self.params.get('hls_prefer_native')):
|
||||||
if fixup_policy == 'warn':
|
if fixup_policy == 'warn':
|
||||||
self.report_warning('%s: malformed AAC bitstream detected.' % (
|
self.report_warning('%s: malformed AAC bitstream detected.' % (
|
||||||
info_dict['id']))
|
info_dict['id']))
|
||||||
@@ -1993,10 +1995,10 @@ class YoutubeDL(object):
|
|||||||
def download(self, url_list):
|
def download(self, url_list):
|
||||||
"""Download a given list of URLs."""
|
"""Download a given list of URLs."""
|
||||||
outtmpl = self.params.get('outtmpl', DEFAULT_OUTTMPL)
|
outtmpl = self.params.get('outtmpl', DEFAULT_OUTTMPL)
|
||||||
if (len(url_list) > 1 and
|
if (len(url_list) > 1
|
||||||
outtmpl != '-' and
|
and outtmpl != '-'
|
||||||
'%' not in outtmpl and
|
and '%' not in outtmpl
|
||||||
self.params.get('max_downloads') != 1):
|
and self.params.get('max_downloads') != 1):
|
||||||
raise SameFileError(outtmpl)
|
raise SameFileError(outtmpl)
|
||||||
|
|
||||||
for url in url_list:
|
for url in url_list:
|
||||||
@@ -2141,8 +2143,8 @@ class YoutubeDL(object):
|
|||||||
if res:
|
if res:
|
||||||
res += ', '
|
res += ', '
|
||||||
res += '%s container' % fdict['container']
|
res += '%s container' % fdict['container']
|
||||||
if (fdict.get('vcodec') is not None and
|
if (fdict.get('vcodec') is not None
|
||||||
fdict.get('vcodec') != 'none'):
|
and fdict.get('vcodec') != 'none'):
|
||||||
if res:
|
if res:
|
||||||
res += ', '
|
res += ', '
|
||||||
res += fdict['vcodec']
|
res += fdict['vcodec']
|
||||||
|
@@ -166,6 +166,8 @@ def _real_main(argv=None):
|
|||||||
if opts.max_sleep_interval is not None:
|
if opts.max_sleep_interval is not None:
|
||||||
if opts.max_sleep_interval < 0:
|
if opts.max_sleep_interval < 0:
|
||||||
parser.error('max sleep interval must be positive or 0')
|
parser.error('max sleep interval must be positive or 0')
|
||||||
|
if opts.sleep_interval is None:
|
||||||
|
parser.error('min sleep interval must be specified, use --min-sleep-interval')
|
||||||
if opts.max_sleep_interval < opts.sleep_interval:
|
if opts.max_sleep_interval < opts.sleep_interval:
|
||||||
parser.error('max sleep interval must be greater than or equal to min sleep interval')
|
parser.error('max sleep interval must be greater than or equal to min sleep interval')
|
||||||
else:
|
else:
|
||||||
@@ -228,14 +230,14 @@ def _real_main(argv=None):
|
|||||||
if opts.allsubtitles and not opts.writeautomaticsub:
|
if opts.allsubtitles and not opts.writeautomaticsub:
|
||||||
opts.writesubtitles = True
|
opts.writesubtitles = True
|
||||||
|
|
||||||
outtmpl = ((opts.outtmpl is not None and opts.outtmpl) or
|
outtmpl = ((opts.outtmpl is not None and opts.outtmpl)
|
||||||
(opts.format == '-1' and opts.usetitle and '%(title)s-%(id)s-%(format)s.%(ext)s') or
|
or (opts.format == '-1' and opts.usetitle and '%(title)s-%(id)s-%(format)s.%(ext)s')
|
||||||
(opts.format == '-1' and '%(id)s-%(format)s.%(ext)s') or
|
or (opts.format == '-1' and '%(id)s-%(format)s.%(ext)s')
|
||||||
(opts.usetitle and opts.autonumber and '%(autonumber)s-%(title)s-%(id)s.%(ext)s') or
|
or (opts.usetitle and opts.autonumber and '%(autonumber)s-%(title)s-%(id)s.%(ext)s')
|
||||||
(opts.usetitle and '%(title)s-%(id)s.%(ext)s') or
|
or (opts.usetitle and '%(title)s-%(id)s.%(ext)s')
|
||||||
(opts.useid and '%(id)s.%(ext)s') or
|
or (opts.useid and '%(id)s.%(ext)s')
|
||||||
(opts.autonumber and '%(autonumber)s-%(id)s.%(ext)s') or
|
or (opts.autonumber and '%(autonumber)s-%(id)s.%(ext)s')
|
||||||
DEFAULT_OUTTMPL)
|
or DEFAULT_OUTTMPL)
|
||||||
if not os.path.splitext(outtmpl)[1] and opts.extractaudio:
|
if not os.path.splitext(outtmpl)[1] and opts.extractaudio:
|
||||||
parser.error('Cannot download a video and extract audio into the same'
|
parser.error('Cannot download a video and extract audio into the same'
|
||||||
' file! Use "{0}.%(ext)s" instead of "{0}" as the output'
|
' file! Use "{0}.%(ext)s" instead of "{0}" as the output'
|
||||||
|
@@ -2649,9 +2649,9 @@ else:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
args = shlex.split('中文')
|
args = shlex.split('中文')
|
||||||
assert (isinstance(args, list) and
|
assert (isinstance(args, list)
|
||||||
isinstance(args[0], compat_str) and
|
and isinstance(args[0], compat_str)
|
||||||
args[0] == '中文')
|
and args[0] == '中文')
|
||||||
compat_shlex_split = shlex.split
|
compat_shlex_split = shlex.split
|
||||||
except (AssertionError, UnicodeEncodeError):
|
except (AssertionError, UnicodeEncodeError):
|
||||||
# Working around shlex issue with unicode strings on some python 2
|
# Working around shlex issue with unicode strings on some python 2
|
||||||
|
@@ -176,7 +176,9 @@ class FileDownloader(object):
|
|||||||
return
|
return
|
||||||
speed = float(byte_counter) / elapsed
|
speed = float(byte_counter) / elapsed
|
||||||
if speed > rate_limit:
|
if speed > rate_limit:
|
||||||
time.sleep(max((byte_counter // rate_limit) - elapsed, 0))
|
sleep_time = float(byte_counter) / rate_limit - elapsed
|
||||||
|
if sleep_time > 0:
|
||||||
|
time.sleep(sleep_time)
|
||||||
|
|
||||||
def temp_name(self, filename):
|
def temp_name(self, filename):
|
||||||
"""Returns a temporary filename for the given filename."""
|
"""Returns a temporary filename for the given filename."""
|
||||||
@@ -330,15 +332,15 @@ class FileDownloader(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
nooverwrites_and_exists = (
|
nooverwrites_and_exists = (
|
||||||
self.params.get('nooverwrites', False) and
|
self.params.get('nooverwrites', False)
|
||||||
os.path.exists(encodeFilename(filename))
|
and os.path.exists(encodeFilename(filename))
|
||||||
)
|
)
|
||||||
|
|
||||||
if not hasattr(filename, 'write'):
|
if not hasattr(filename, 'write'):
|
||||||
continuedl_and_exists = (
|
continuedl_and_exists = (
|
||||||
self.params.get('continuedl', True) and
|
self.params.get('continuedl', True)
|
||||||
os.path.isfile(encodeFilename(filename)) and
|
and os.path.isfile(encodeFilename(filename))
|
||||||
not self.params.get('nopart', False)
|
and not self.params.get('nopart', False)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check file already present
|
# Check file already present
|
||||||
|
@@ -289,6 +289,7 @@ class FFmpegFD(ExternalFD):
|
|||||||
tc_url = info_dict.get('tc_url')
|
tc_url = info_dict.get('tc_url')
|
||||||
flash_version = info_dict.get('flash_version')
|
flash_version = info_dict.get('flash_version')
|
||||||
live = info_dict.get('rtmp_live', False)
|
live = info_dict.get('rtmp_live', False)
|
||||||
|
conn = info_dict.get('rtmp_conn')
|
||||||
if player_url is not None:
|
if player_url is not None:
|
||||||
args += ['-rtmp_swfverify', player_url]
|
args += ['-rtmp_swfverify', player_url]
|
||||||
if page_url is not None:
|
if page_url is not None:
|
||||||
@@ -303,6 +304,11 @@ class FFmpegFD(ExternalFD):
|
|||||||
args += ['-rtmp_flashver', flash_version]
|
args += ['-rtmp_flashver', flash_version]
|
||||||
if live:
|
if live:
|
||||||
args += ['-rtmp_live', 'live']
|
args += ['-rtmp_live', 'live']
|
||||||
|
if isinstance(conn, list):
|
||||||
|
for entry in conn:
|
||||||
|
args += ['-rtmp_conn', entry]
|
||||||
|
elif isinstance(conn, compat_str):
|
||||||
|
args += ['-rtmp_conn', conn]
|
||||||
|
|
||||||
args += ['-i', url, '-c', 'copy']
|
args += ['-i', url, '-c', 'copy']
|
||||||
|
|
||||||
|
@@ -238,8 +238,8 @@ def write_metadata_tag(stream, metadata):
|
|||||||
|
|
||||||
|
|
||||||
def remove_encrypted_media(media):
|
def remove_encrypted_media(media):
|
||||||
return list(filter(lambda e: 'drmAdditionalHeaderId' not in e.attrib and
|
return list(filter(lambda e: 'drmAdditionalHeaderId' not in e.attrib
|
||||||
'drmAdditionalHeaderSetId' not in e.attrib,
|
and 'drmAdditionalHeaderSetId' not in e.attrib,
|
||||||
media))
|
media))
|
||||||
|
|
||||||
|
|
||||||
@@ -267,8 +267,8 @@ class F4mFD(FragmentFD):
|
|||||||
media = doc.findall(_add_ns('media'))
|
media = doc.findall(_add_ns('media'))
|
||||||
if not media:
|
if not media:
|
||||||
self.report_error('No media found')
|
self.report_error('No media found')
|
||||||
for e in (doc.findall(_add_ns('drmAdditionalHeader')) +
|
for e in (doc.findall(_add_ns('drmAdditionalHeader'))
|
||||||
doc.findall(_add_ns('drmAdditionalHeaderSet'))):
|
+ doc.findall(_add_ns('drmAdditionalHeaderSet'))):
|
||||||
# If id attribute is missing it's valid for all media nodes
|
# If id attribute is missing it's valid for all media nodes
|
||||||
# without drmAdditionalHeaderId or drmAdditionalHeaderSetId attribute
|
# without drmAdditionalHeaderId or drmAdditionalHeaderSetId attribute
|
||||||
if 'id' not in e.attrib:
|
if 'id' not in e.attrib:
|
||||||
|
@@ -219,8 +219,8 @@ class FragmentFD(FileDownloader):
|
|||||||
frag_total_bytes = s.get('total_bytes') or 0
|
frag_total_bytes = s.get('total_bytes') or 0
|
||||||
if not ctx['live']:
|
if not ctx['live']:
|
||||||
estimated_size = (
|
estimated_size = (
|
||||||
(ctx['complete_frags_downloaded_bytes'] + frag_total_bytes) /
|
(ctx['complete_frags_downloaded_bytes'] + frag_total_bytes)
|
||||||
(state['fragment_index'] + 1) * total_frags)
|
/ (state['fragment_index'] + 1) * total_frags)
|
||||||
state['total_bytes_estimate'] = estimated_size
|
state['total_bytes_estimate'] = estimated_size
|
||||||
|
|
||||||
if s['status'] == 'finished':
|
if s['status'] == 'finished':
|
||||||
|
@@ -76,12 +76,12 @@ class HlsFD(FragmentFD):
|
|||||||
return fd.real_download(filename, info_dict)
|
return fd.real_download(filename, info_dict)
|
||||||
|
|
||||||
def is_ad_fragment_start(s):
|
def is_ad_fragment_start(s):
|
||||||
return (s.startswith('#ANVATO-SEGMENT-INFO') and 'type=ad' in s or
|
return (s.startswith('#ANVATO-SEGMENT-INFO') and 'type=ad' in s
|
||||||
s.startswith('#UPLYNK-SEGMENT') and s.endswith(',ad'))
|
or s.startswith('#UPLYNK-SEGMENT') and s.endswith(',ad'))
|
||||||
|
|
||||||
def is_ad_fragment_end(s):
|
def is_ad_fragment_end(s):
|
||||||
return (s.startswith('#ANVATO-SEGMENT-INFO') and 'type=master' in s or
|
return (s.startswith('#ANVATO-SEGMENT-INFO') and 'type=master' in s
|
||||||
s.startswith('#UPLYNK-SEGMENT') and s.endswith(',segment'))
|
or s.startswith('#UPLYNK-SEGMENT') and s.endswith(',segment'))
|
||||||
|
|
||||||
media_frags = 0
|
media_frags = 0
|
||||||
ad_frags = 0
|
ad_frags = 0
|
||||||
|
@@ -46,8 +46,8 @@ class HttpFD(FileDownloader):
|
|||||||
|
|
||||||
is_test = self.params.get('test', False)
|
is_test = self.params.get('test', False)
|
||||||
chunk_size = self._TEST_FILE_SIZE if is_test else (
|
chunk_size = self._TEST_FILE_SIZE if is_test else (
|
||||||
info_dict.get('downloader_options', {}).get('http_chunk_size') or
|
info_dict.get('downloader_options', {}).get('http_chunk_size')
|
||||||
self.params.get('http_chunk_size') or 0)
|
or self.params.get('http_chunk_size') or 0)
|
||||||
|
|
||||||
ctx.open_mode = 'wb'
|
ctx.open_mode = 'wb'
|
||||||
ctx.resume_len = 0
|
ctx.resume_len = 0
|
||||||
@@ -123,11 +123,11 @@ class HttpFD(FileDownloader):
|
|||||||
content_len = int_or_none(content_range_m.group(3))
|
content_len = int_or_none(content_range_m.group(3))
|
||||||
accept_content_len = (
|
accept_content_len = (
|
||||||
# Non-chunked download
|
# Non-chunked download
|
||||||
not ctx.chunk_size or
|
not ctx.chunk_size
|
||||||
# Chunked download and requested piece or
|
# Chunked download and requested piece or
|
||||||
# its part is promised to be served
|
# its part is promised to be served
|
||||||
content_range_end == range_end or
|
or content_range_end == range_end
|
||||||
content_len < range_end)
|
or content_len < range_end)
|
||||||
if accept_content_len:
|
if accept_content_len:
|
||||||
ctx.data_len = content_len
|
ctx.data_len = content_len
|
||||||
return
|
return
|
||||||
@@ -152,8 +152,8 @@ class HttpFD(FileDownloader):
|
|||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
# Examine the reported length
|
# Examine the reported length
|
||||||
if (content_length is not None and
|
if (content_length is not None
|
||||||
(ctx.resume_len - 100 < int(content_length) < ctx.resume_len + 100)):
|
and (ctx.resume_len - 100 < int(content_length) < ctx.resume_len + 100)):
|
||||||
# The file had already been fully downloaded.
|
# The file had already been fully downloaded.
|
||||||
# Explanation to the above condition: in issue #175 it was revealed that
|
# Explanation to the above condition: in issue #175 it was revealed that
|
||||||
# YouTube sometimes adds or removes a few bytes from the end of the file,
|
# YouTube sometimes adds or removes a few bytes from the end of the file,
|
||||||
|
@@ -59,9 +59,9 @@ class AddAnimeIE(InfoExtractor):
|
|||||||
parsed_url = compat_urllib_parse_urlparse(url)
|
parsed_url = compat_urllib_parse_urlparse(url)
|
||||||
av_val = av_res + len(parsed_url.netloc)
|
av_val = av_res + len(parsed_url.netloc)
|
||||||
confirm_url = (
|
confirm_url = (
|
||||||
parsed_url.scheme + '://' + parsed_url.netloc +
|
parsed_url.scheme + '://' + parsed_url.netloc
|
||||||
action + '?' +
|
+ action + '?'
|
||||||
compat_urllib_parse_urlencode({
|
+ compat_urllib_parse_urlencode({
|
||||||
'jschl_vc': vc, 'jschl_answer': compat_str(av_val)}))
|
'jschl_vc': vc, 'jschl_answer': compat_str(av_val)}))
|
||||||
self._download_webpage(
|
self._download_webpage(
|
||||||
confirm_url, video_id,
|
confirm_url, video_id,
|
||||||
|
@@ -21,7 +21,6 @@ from ..utils import (
|
|||||||
intlist_to_bytes,
|
intlist_to_bytes,
|
||||||
long_to_bytes,
|
long_to_bytes,
|
||||||
pkcs1pad,
|
pkcs1pad,
|
||||||
srt_subtitles_timecode,
|
|
||||||
strip_or_none,
|
strip_or_none,
|
||||||
urljoin,
|
urljoin,
|
||||||
)
|
)
|
||||||
@@ -42,6 +41,18 @@ class ADNIE(InfoExtractor):
|
|||||||
}
|
}
|
||||||
_BASE_URL = 'http://animedigitalnetwork.fr'
|
_BASE_URL = 'http://animedigitalnetwork.fr'
|
||||||
_RSA_KEY = (0xc35ae1e4356b65a73b551493da94b8cb443491c0aa092a357a5aee57ffc14dda85326f42d716e539a34542a0d3f363adf16c5ec222d713d5997194030ee2e4f0d1fb328c01a81cf6868c090d50de8e169c6b13d1675b9eeed1cbc51e1fffca9b38af07f37abd790924cd3bee59d0257cfda4fe5f3f0534877e21ce5821447d1b, 65537)
|
_RSA_KEY = (0xc35ae1e4356b65a73b551493da94b8cb443491c0aa092a357a5aee57ffc14dda85326f42d716e539a34542a0d3f363adf16c5ec222d713d5997194030ee2e4f0d1fb328c01a81cf6868c090d50de8e169c6b13d1675b9eeed1cbc51e1fffca9b38af07f37abd790924cd3bee59d0257cfda4fe5f3f0534877e21ce5821447d1b, 65537)
|
||||||
|
_POS_ALIGN_MAP = {
|
||||||
|
'start': 1,
|
||||||
|
'end': 3,
|
||||||
|
}
|
||||||
|
_LINE_ALIGN_MAP = {
|
||||||
|
'middle': 8,
|
||||||
|
'end': 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _ass_subtitles_timecode(seconds):
|
||||||
|
return '%01d:%02d:%02d.%02d' % (seconds / 3600, (seconds % 3600) / 60, seconds % 60, (seconds % 1) * 100)
|
||||||
|
|
||||||
def _get_subtitles(self, sub_path, video_id):
|
def _get_subtitles(self, sub_path, video_id):
|
||||||
if not sub_path:
|
if not sub_path:
|
||||||
@@ -49,14 +60,20 @@ class ADNIE(InfoExtractor):
|
|||||||
|
|
||||||
enc_subtitles = self._download_webpage(
|
enc_subtitles = self._download_webpage(
|
||||||
urljoin(self._BASE_URL, sub_path),
|
urljoin(self._BASE_URL, sub_path),
|
||||||
video_id, fatal=False)
|
video_id, 'Downloading subtitles location', fatal=False) or '{}'
|
||||||
|
subtitle_location = (self._parse_json(enc_subtitles, video_id, fatal=False) or {}).get('location')
|
||||||
|
if subtitle_location:
|
||||||
|
enc_subtitles = self._download_webpage(
|
||||||
|
urljoin(self._BASE_URL, subtitle_location),
|
||||||
|
video_id, 'Downloading subtitles data', fatal=False,
|
||||||
|
headers={'Origin': 'https://animedigitalnetwork.fr'})
|
||||||
if not enc_subtitles:
|
if not enc_subtitles:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# http://animedigitalnetwork.fr/components/com_vodvideo/videojs/adn-vjs.min.js
|
# http://animedigitalnetwork.fr/components/com_vodvideo/videojs/adn-vjs.min.js
|
||||||
dec_subtitles = intlist_to_bytes(aes_cbc_decrypt(
|
dec_subtitles = intlist_to_bytes(aes_cbc_decrypt(
|
||||||
bytes_to_intlist(compat_b64decode(enc_subtitles[24:])),
|
bytes_to_intlist(compat_b64decode(enc_subtitles[24:])),
|
||||||
bytes_to_intlist(binascii.unhexlify(self._K + '9032ad7083106400')),
|
bytes_to_intlist(binascii.unhexlify(self._K + '4b8ef13ec1872730')),
|
||||||
bytes_to_intlist(compat_b64decode(enc_subtitles[:24]))
|
bytes_to_intlist(compat_b64decode(enc_subtitles[:24]))
|
||||||
))
|
))
|
||||||
subtitles_json = self._parse_json(
|
subtitles_json = self._parse_json(
|
||||||
@@ -67,23 +84,27 @@ class ADNIE(InfoExtractor):
|
|||||||
|
|
||||||
subtitles = {}
|
subtitles = {}
|
||||||
for sub_lang, sub in subtitles_json.items():
|
for sub_lang, sub in subtitles_json.items():
|
||||||
srt = ''
|
ssa = '''[Script Info]
|
||||||
for num, current in enumerate(sub):
|
ScriptType:V4.00
|
||||||
start, end, text = (
|
[V4 Styles]
|
||||||
|
Format: Name,Fontname,Fontsize,PrimaryColour,SecondaryColour,TertiaryColour,BackColour,Bold,Italic,BorderStyle,Outline,Shadow,Alignment,MarginL,MarginR,MarginV,AlphaLevel,Encoding
|
||||||
|
Style: Default,Arial,18,16777215,16777215,16777215,0,-1,0,1,1,0,2,20,20,20,0,0
|
||||||
|
[Events]
|
||||||
|
Format: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text'''
|
||||||
|
for current in sub:
|
||||||
|
start, end, text, line_align, position_align = (
|
||||||
float_or_none(current.get('startTime')),
|
float_or_none(current.get('startTime')),
|
||||||
float_or_none(current.get('endTime')),
|
float_or_none(current.get('endTime')),
|
||||||
current.get('text'))
|
current.get('text'), current.get('lineAlign'),
|
||||||
|
current.get('positionAlign'))
|
||||||
if start is None or end is None or text is None:
|
if start is None or end is None or text is None:
|
||||||
continue
|
continue
|
||||||
srt += os.linesep.join(
|
alignment = self._POS_ALIGN_MAP.get(position_align, 2) + self._LINE_ALIGN_MAP.get(line_align, 0)
|
||||||
(
|
ssa += os.linesep + 'Dialogue: Marked=0,%s,%s,Default,,0,0,0,,%s%s' % (
|
||||||
'%d' % num,
|
self._ass_subtitles_timecode(start),
|
||||||
'%s --> %s' % (
|
self._ass_subtitles_timecode(end),
|
||||||
srt_subtitles_timecode(start),
|
'{\\a%d}' % alignment if alignment != 2 else '',
|
||||||
srt_subtitles_timecode(end)),
|
text.replace('\n', '\\N').replace('<i>', '{\\i1}').replace('</i>', '{\\i0}'))
|
||||||
text,
|
|
||||||
os.linesep,
|
|
||||||
))
|
|
||||||
|
|
||||||
if sub_lang == 'vostf':
|
if sub_lang == 'vostf':
|
||||||
sub_lang = 'fr'
|
sub_lang = 'fr'
|
||||||
@@ -91,8 +112,8 @@ class ADNIE(InfoExtractor):
|
|||||||
'ext': 'json',
|
'ext': 'json',
|
||||||
'data': json.dumps(sub),
|
'data': json.dumps(sub),
|
||||||
}, {
|
}, {
|
||||||
'ext': 'srt',
|
'ext': 'ssa',
|
||||||
'data': srt,
|
'data': ssa,
|
||||||
}])
|
}])
|
||||||
return subtitles
|
return subtitles
|
||||||
|
|
||||||
@@ -100,7 +121,15 @@ class ADNIE(InfoExtractor):
|
|||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, video_id)
|
||||||
player_config = self._parse_json(self._search_regex(
|
player_config = self._parse_json(self._search_regex(
|
||||||
r'playerConfig\s*=\s*({.+});', webpage, 'player config'), video_id)
|
r'playerConfig\s*=\s*({.+});', webpage,
|
||||||
|
'player config', default='{}'), video_id, fatal=False)
|
||||||
|
if not player_config:
|
||||||
|
config_url = urljoin(self._BASE_URL, self._search_regex(
|
||||||
|
r'(?:id="player"|class="[^"]*adn-player-container[^"]*")[^>]+data-url="([^"]+)"',
|
||||||
|
webpage, 'config url'))
|
||||||
|
player_config = self._download_json(
|
||||||
|
config_url, video_id,
|
||||||
|
'Downloading player config JSON metadata')['player']
|
||||||
|
|
||||||
video_info = {}
|
video_info = {}
|
||||||
video_info_str = self._search_regex(
|
video_info_str = self._search_regex(
|
||||||
@@ -129,12 +158,15 @@ class ADNIE(InfoExtractor):
|
|||||||
encrypted_message = long_to_bytes(pow(bytes_to_long(padded_message), e, n))
|
encrypted_message = long_to_bytes(pow(bytes_to_long(padded_message), e, n))
|
||||||
authorization = base64.b64encode(encrypted_message).decode()
|
authorization = base64.b64encode(encrypted_message).decode()
|
||||||
links_data = self._download_json(
|
links_data = self._download_json(
|
||||||
urljoin(self._BASE_URL, links_url), video_id, headers={
|
urljoin(self._BASE_URL, links_url), video_id,
|
||||||
|
'Downloading links JSON metadata', headers={
|
||||||
'Authorization': 'Bearer ' + authorization,
|
'Authorization': 'Bearer ' + authorization,
|
||||||
})
|
})
|
||||||
links = links_data.get('links') or {}
|
links = links_data.get('links') or {}
|
||||||
metas = metas or links_data.get('meta') or {}
|
metas = metas or links_data.get('meta') or {}
|
||||||
sub_path = (sub_path or links_data.get('subtitles')) + '&token=' + token
|
sub_path = sub_path or links_data.get('subtitles') or \
|
||||||
|
'index.php?option=com_vodapi&task=subtitles.getJSON&format=json&id=' + video_id
|
||||||
|
sub_path += '&token=' + token
|
||||||
error = links_data.get('error')
|
error = links_data.get('error')
|
||||||
title = metas.get('title') or video_info['title']
|
title = metas.get('title') or video_info['title']
|
||||||
|
|
||||||
@@ -142,9 +174,11 @@ class ADNIE(InfoExtractor):
|
|||||||
for format_id, qualities in links.items():
|
for format_id, qualities in links.items():
|
||||||
if not isinstance(qualities, dict):
|
if not isinstance(qualities, dict):
|
||||||
continue
|
continue
|
||||||
for load_balancer_url in qualities.values():
|
for quality, load_balancer_url in qualities.items():
|
||||||
load_balancer_data = self._download_json(
|
load_balancer_data = self._download_json(
|
||||||
load_balancer_url, video_id, fatal=False) or {}
|
load_balancer_url, video_id,
|
||||||
|
'Downloading %s %s JSON metadata' % (format_id, quality),
|
||||||
|
fatal=False) or {}
|
||||||
m3u8_url = load_balancer_data.get('location')
|
m3u8_url = load_balancer_data.get('location')
|
||||||
if not m3u8_url:
|
if not m3u8_url:
|
||||||
continue
|
continue
|
||||||
|
37
youtube_dl/extractor/adobeconnect.py
Normal file
37
youtube_dl/extractor/adobeconnect.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..compat import (
|
||||||
|
compat_parse_qs,
|
||||||
|
compat_urlparse,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AdobeConnectIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://\w+\.adobeconnect\.com/(?P<id>[\w-]+)'
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
title = self._html_search_regex(r'<title>(.+?)</title>', webpage, 'title')
|
||||||
|
qs = compat_parse_qs(self._search_regex(r"swfUrl\s*=\s*'([^']+)'", webpage, 'swf url').split('?')[1])
|
||||||
|
is_live = qs.get('isLive', ['false'])[0] == 'true'
|
||||||
|
formats = []
|
||||||
|
for con_string in qs['conStrings'][0].split(','):
|
||||||
|
formats.append({
|
||||||
|
'format_id': con_string.split('://')[0],
|
||||||
|
'app': compat_urlparse.quote('?' + con_string.split('?')[1] + 'flvplayerapp/' + qs['appInstance'][0]),
|
||||||
|
'ext': 'flv',
|
||||||
|
'play_path': 'mp4:' + qs['streamName'][0],
|
||||||
|
'rtmp_conn': 'S:' + qs['ticket'][0],
|
||||||
|
'rtmp_live': is_live,
|
||||||
|
'url': con_string,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': self._live_title(title) if is_live else title,
|
||||||
|
'formats': formats,
|
||||||
|
'is_live': is_live,
|
||||||
|
}
|
@@ -1,13 +1,19 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .turner import TurnerBaseIE
|
from .turner import TurnerBaseIE
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
determine_ext,
|
||||||
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
mimetype2ext,
|
||||||
|
parse_age_limit,
|
||||||
|
parse_iso8601,
|
||||||
strip_or_none,
|
strip_or_none,
|
||||||
url_or_none,
|
try_get,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -21,8 +27,8 @@ class AdultSwimIE(TurnerBaseIE):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Rick and Morty - Pilot',
|
'title': 'Rick and Morty - Pilot',
|
||||||
'description': 'Rick moves in with his daughter\'s family and establishes himself as a bad influence on his grandson, Morty.',
|
'description': 'Rick moves in with his daughter\'s family and establishes himself as a bad influence on his grandson, Morty.',
|
||||||
'timestamp': 1493267400,
|
'timestamp': 1543294800,
|
||||||
'upload_date': '20170427',
|
'upload_date': '20181127',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
# m3u8 download
|
# m3u8 download
|
||||||
@@ -43,6 +49,7 @@ class AdultSwimIE(TurnerBaseIE):
|
|||||||
# m3u8 download
|
# m3u8 download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
|
'skip': '404 Not Found',
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.adultswim.com/videos/decker/inside-decker-a-new-hero/',
|
'url': 'http://www.adultswim.com/videos/decker/inside-decker-a-new-hero/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@@ -61,9 +68,9 @@ class AdultSwimIE(TurnerBaseIE):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://www.adultswim.com/videos/attack-on-titan',
|
'url': 'http://www.adultswim.com/videos/attack-on-titan',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'b7A69dzfRzuaXIECdxW8XQ',
|
'id': 'attack-on-titan',
|
||||||
'title': 'Attack on Titan',
|
'title': 'Attack on Titan',
|
||||||
'description': 'md5:6c8e003ea0777b47013e894767f5e114',
|
'description': 'md5:41caa9416906d90711e31dc00cb7db7e',
|
||||||
},
|
},
|
||||||
'playlist_mincount': 12,
|
'playlist_mincount': 12,
|
||||||
}, {
|
}, {
|
||||||
@@ -78,83 +85,118 @@ class AdultSwimIE(TurnerBaseIE):
|
|||||||
# m3u8 download
|
# m3u8 download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
|
'skip': '404 Not Found',
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
show_path, episode_path = re.match(self._VALID_URL, url).groups()
|
show_path, episode_path = re.match(self._VALID_URL, url).groups()
|
||||||
display_id = episode_path or show_path
|
display_id = episode_path or show_path
|
||||||
webpage = self._download_webpage(url, display_id)
|
query = '''query {
|
||||||
initial_data = self._parse_json(self._search_regex(
|
getShowBySlug(slug:"%s") {
|
||||||
r'AS_INITIAL_DATA(?:__)?\s*=\s*({.+?});',
|
%%s
|
||||||
webpage, 'initial data'), display_id)
|
}
|
||||||
|
}''' % show_path
|
||||||
is_stream = show_path == 'streams'
|
if episode_path:
|
||||||
if is_stream:
|
query = query % '''title
|
||||||
if not episode_path:
|
getVideoBySlug(slug:"%s") {
|
||||||
episode_path = 'live-stream'
|
_id
|
||||||
|
auth
|
||||||
video_data = next(stream for stream_path, stream in initial_data['streams'].items() if stream_path == episode_path)
|
description
|
||||||
video_id = video_data.get('stream')
|
duration
|
||||||
|
episodeNumber
|
||||||
if not video_id:
|
launchDate
|
||||||
entries = []
|
mediaID
|
||||||
for episode in video_data.get('archiveEpisodes', []):
|
seasonNumber
|
||||||
episode_url = url_or_none(episode.get('url'))
|
poster
|
||||||
if not episode_url:
|
title
|
||||||
continue
|
tvRating
|
||||||
entries.append(self.url_result(
|
}''' % episode_path
|
||||||
episode_url, 'AdultSwim', episode.get('id')))
|
['getVideoBySlug']
|
||||||
return self.playlist_result(
|
|
||||||
entries, video_data.get('id'), video_data.get('title'),
|
|
||||||
strip_or_none(video_data.get('description')))
|
|
||||||
else:
|
else:
|
||||||
show_data = initial_data['show']
|
query = query % '''metaDescription
|
||||||
|
title
|
||||||
|
videos(first:1000,sort:["episode_number"]) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
_id
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}'''
|
||||||
|
show_data = self._download_json(
|
||||||
|
'https://www.adultswim.com/api/search', display_id,
|
||||||
|
data=json.dumps({'query': query}).encode(),
|
||||||
|
headers={'Content-Type': 'application/json'})['data']['getShowBySlug']
|
||||||
|
if episode_path:
|
||||||
|
video_data = show_data['getVideoBySlug']
|
||||||
|
video_id = video_data['_id']
|
||||||
|
episode_title = title = video_data['title']
|
||||||
|
series = show_data.get('title')
|
||||||
|
if series:
|
||||||
|
title = '%s - %s' % (series, title)
|
||||||
|
info = {
|
||||||
|
'id': video_id,
|
||||||
|
'title': title,
|
||||||
|
'description': strip_or_none(video_data.get('description')),
|
||||||
|
'duration': float_or_none(video_data.get('duration')),
|
||||||
|
'formats': [],
|
||||||
|
'subtitles': {},
|
||||||
|
'age_limit': parse_age_limit(video_data.get('tvRating')),
|
||||||
|
'thumbnail': video_data.get('poster'),
|
||||||
|
'timestamp': parse_iso8601(video_data.get('launchDate')),
|
||||||
|
'series': series,
|
||||||
|
'season_number': int_or_none(video_data.get('seasonNumber')),
|
||||||
|
'episode': episode_title,
|
||||||
|
'episode_number': int_or_none(video_data.get('episodeNumber')),
|
||||||
|
}
|
||||||
|
|
||||||
if not episode_path:
|
auth = video_data.get('auth')
|
||||||
entries = []
|
media_id = video_data.get('mediaID')
|
||||||
for video in show_data.get('videos', []):
|
if media_id:
|
||||||
slug = video.get('slug')
|
info.update(self._extract_ngtv_info(media_id, {
|
||||||
if not slug:
|
# CDN_TOKEN_APP_ID from:
|
||||||
|
# https://d2gg02c3xr550i.cloudfront.net/assets/asvp.e9c8bef24322d060ef87.bundle.js
|
||||||
|
'appId': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBJZCI6ImFzLXR2ZS1kZXNrdG9wLXB0enQ2bSIsInByb2R1Y3QiOiJ0dmUiLCJuZXR3b3JrIjoiYXMiLCJwbGF0Zm9ybSI6ImRlc2t0b3AiLCJpYXQiOjE1MzI3MDIyNzl9.BzSCk-WYOZ2GMCIaeVb8zWnzhlgnXuJTCu0jGp_VaZE',
|
||||||
|
}, {
|
||||||
|
'url': url,
|
||||||
|
'site_name': 'AdultSwim',
|
||||||
|
'auth_required': auth,
|
||||||
|
}))
|
||||||
|
|
||||||
|
if not auth:
|
||||||
|
extract_data = self._download_json(
|
||||||
|
'https://www.adultswim.com/api/shows/v1/videos/' + video_id,
|
||||||
|
video_id, query={'fields': 'stream'}, fatal=False) or {}
|
||||||
|
assets = try_get(extract_data, lambda x: x['data']['video']['stream']['assets'], list) or []
|
||||||
|
for asset in assets:
|
||||||
|
asset_url = asset.get('url')
|
||||||
|
if not asset_url:
|
||||||
continue
|
continue
|
||||||
entries.append(self.url_result(
|
ext = determine_ext(asset_url, mimetype2ext(asset.get('mime_type')))
|
||||||
'http://adultswim.com/videos/%s/%s' % (show_path, slug),
|
if ext == 'm3u8':
|
||||||
'AdultSwim', video.get('id')))
|
info['formats'].extend(self._extract_m3u8_formats(
|
||||||
return self.playlist_result(
|
asset_url, video_id, 'mp4', m3u8_id='hls', fatal=False))
|
||||||
entries, show_data.get('id'), show_data.get('title'),
|
elif ext == 'f4m':
|
||||||
strip_or_none(show_data.get('metadata', {}).get('description')))
|
continue
|
||||||
|
# info['formats'].extend(self._extract_f4m_formats(
|
||||||
|
# asset_url, video_id, f4m_id='hds', fatal=False))
|
||||||
|
elif ext in ('scc', 'ttml', 'vtt'):
|
||||||
|
info['subtitles'].setdefault('en', []).append({
|
||||||
|
'url': asset_url,
|
||||||
|
})
|
||||||
|
self._sort_formats(info['formats'])
|
||||||
|
|
||||||
video_data = show_data['sluggedVideo']
|
return info
|
||||||
video_id = video_data['id']
|
else:
|
||||||
|
entries = []
|
||||||
info = self._extract_cvp_info(
|
for edge in show_data.get('videos', {}).get('edges', []):
|
||||||
'http://www.adultswim.com/videos/api/v0/assets?platform=desktop&id=' + video_id,
|
video = edge.get('node') or {}
|
||||||
video_id, {
|
slug = video.get('slug')
|
||||||
'secure': {
|
if not slug:
|
||||||
'media_src': 'http://androidhls-secure.cdn.turner.com/adultswim/big',
|
continue
|
||||||
'tokenizer_src': 'http://www.adultswim.com/astv/mvpd/processors/services/token_ipadAdobe.do',
|
entries.append(self.url_result(
|
||||||
},
|
'http://adultswim.com/videos/%s/%s' % (show_path, slug),
|
||||||
}, {
|
'AdultSwim', video.get('_id')))
|
||||||
'url': url,
|
return self.playlist_result(
|
||||||
'site_name': 'AdultSwim',
|
entries, show_path, show_data.get('title'),
|
||||||
'auth_required': video_data.get('auth'),
|
strip_or_none(show_data.get('metaDescription')))
|
||||||
})
|
|
||||||
|
|
||||||
info.update({
|
|
||||||
'id': video_id,
|
|
||||||
'display_id': display_id,
|
|
||||||
'description': info.get('description') or strip_or_none(video_data.get('description')),
|
|
||||||
})
|
|
||||||
if not is_stream:
|
|
||||||
info.update({
|
|
||||||
'duration': info.get('duration') or int_or_none(video_data.get('duration')),
|
|
||||||
'timestamp': info.get('timestamp') or int_or_none(video_data.get('launch_date')),
|
|
||||||
'season_number': info.get('season_number') or int_or_none(video_data.get('season_number')),
|
|
||||||
'episode': info['title'],
|
|
||||||
'episode_number': info.get('episode_number') or int_or_none(video_data.get('episode_number')),
|
|
||||||
})
|
|
||||||
|
|
||||||
info['series'] = video_data.get('collection_title') or info.get('series')
|
|
||||||
if info['series'] and info['series'] != info['title']:
|
|
||||||
info['title'] = '%s - %s' % (info['series'], info['title'])
|
|
||||||
|
|
||||||
return info
|
|
||||||
|
@@ -1,14 +1,15 @@
|
|||||||
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .theplatform import ThePlatformIE
|
from .theplatform import ThePlatformIE
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
extract_attributes,
|
||||||
|
ExtractorError,
|
||||||
|
int_or_none,
|
||||||
smuggle_url,
|
smuggle_url,
|
||||||
update_url_query,
|
update_url_query,
|
||||||
unescapeHTML,
|
|
||||||
extract_attributes,
|
|
||||||
get_element_by_attribute,
|
|
||||||
)
|
)
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_urlparse,
|
compat_urlparse,
|
||||||
@@ -19,6 +20,43 @@ class AENetworksBaseIE(ThePlatformIE):
|
|||||||
_THEPLATFORM_KEY = 'crazyjava'
|
_THEPLATFORM_KEY = 'crazyjava'
|
||||||
_THEPLATFORM_SECRET = 's3cr3t'
|
_THEPLATFORM_SECRET = 's3cr3t'
|
||||||
|
|
||||||
|
def _extract_aen_smil(self, smil_url, video_id, auth=None):
|
||||||
|
query = {'mbr': 'true'}
|
||||||
|
if auth:
|
||||||
|
query['auth'] = auth
|
||||||
|
TP_SMIL_QUERY = [{
|
||||||
|
'assetTypes': 'high_video_ak',
|
||||||
|
'switch': 'hls_high_ak'
|
||||||
|
}, {
|
||||||
|
'assetTypes': 'high_video_s3'
|
||||||
|
}, {
|
||||||
|
'assetTypes': 'high_video_s3',
|
||||||
|
'switch': 'hls_ingest_fastly'
|
||||||
|
}]
|
||||||
|
formats = []
|
||||||
|
subtitles = {}
|
||||||
|
last_e = None
|
||||||
|
for q in TP_SMIL_QUERY:
|
||||||
|
q.update(query)
|
||||||
|
m_url = update_url_query(smil_url, q)
|
||||||
|
m_url = self._sign_url(m_url, self._THEPLATFORM_KEY, self._THEPLATFORM_SECRET)
|
||||||
|
try:
|
||||||
|
tp_formats, tp_subtitles = self._extract_theplatform_smil(
|
||||||
|
m_url, video_id, 'Downloading %s SMIL data' % (q.get('switch') or q['assetTypes']))
|
||||||
|
except ExtractorError as e:
|
||||||
|
last_e = e
|
||||||
|
continue
|
||||||
|
formats.extend(tp_formats)
|
||||||
|
subtitles = self._merge_subtitles(subtitles, tp_subtitles)
|
||||||
|
if last_e and not formats:
|
||||||
|
raise last_e
|
||||||
|
self._sort_formats(formats)
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'formats': formats,
|
||||||
|
'subtitles': subtitles,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class AENetworksIE(AENetworksBaseIE):
|
class AENetworksIE(AENetworksBaseIE):
|
||||||
IE_NAME = 'aenetworks'
|
IE_NAME = 'aenetworks'
|
||||||
@@ -33,22 +71,25 @@ class AENetworksIE(AENetworksBaseIE):
|
|||||||
(?:
|
(?:
|
||||||
shows/(?P<show_path>[^/]+(?:/[^/]+){0,2})|
|
shows/(?P<show_path>[^/]+(?:/[^/]+){0,2})|
|
||||||
movies/(?P<movie_display_id>[^/]+)(?:/full-movie)?|
|
movies/(?P<movie_display_id>[^/]+)(?:/full-movie)?|
|
||||||
specials/(?P<special_display_id>[^/]+)/full-special|
|
specials/(?P<special_display_id>[^/]+)/(?:full-special|preview-)|
|
||||||
collections/[^/]+/(?P<collection_display_id>[^/]+)
|
collections/[^/]+/(?P<collection_display_id>[^/]+)
|
||||||
)
|
)
|
||||||
'''
|
'''
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.history.com/shows/mountain-men/season-1/episode-1',
|
'url': 'http://www.history.com/shows/mountain-men/season-1/episode-1',
|
||||||
'md5': 'a97a65f7e823ae10e9244bc5433d5fe6',
|
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '22253814',
|
'id': '22253814',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Winter Is Coming',
|
'title': 'Winter is Coming',
|
||||||
'description': 'md5:641f424b7a19d8e24f26dea22cf59d74',
|
'description': 'md5:641f424b7a19d8e24f26dea22cf59d74',
|
||||||
'timestamp': 1338306241,
|
'timestamp': 1338306241,
|
||||||
'upload_date': '20120529',
|
'upload_date': '20120529',
|
||||||
'uploader': 'AENE-NEW',
|
'uploader': 'AENE-NEW',
|
||||||
},
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
'add_ie': ['ThePlatform'],
|
'add_ie': ['ThePlatform'],
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.history.com/shows/ancient-aliens/season-1',
|
'url': 'http://www.history.com/shows/ancient-aliens/season-1',
|
||||||
@@ -84,6 +125,9 @@ class AENetworksIE(AENetworksBaseIE):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'https://www.historyvault.com/collections/america-the-story-of-us/westward',
|
'url': 'https://www.historyvault.com/collections/america-the-story-of-us/westward',
|
||||||
'only_matching': True
|
'only_matching': True
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.aetv.com/specials/hunting-jonbenets-killer-the-untold-story/preview-hunting-jonbenets-killer-the-untold-story',
|
||||||
|
'only_matching': True
|
||||||
}]
|
}]
|
||||||
_DOMAIN_TO_REQUESTOR_ID = {
|
_DOMAIN_TO_REQUESTOR_ID = {
|
||||||
'history.com': 'HISTORY',
|
'history.com': 'HISTORY',
|
||||||
@@ -124,11 +168,6 @@ class AENetworksIE(AENetworksBaseIE):
|
|||||||
return self.playlist_result(
|
return self.playlist_result(
|
||||||
entries, self._html_search_meta('aetn:SeasonId', webpage))
|
entries, self._html_search_meta('aetn:SeasonId', webpage))
|
||||||
|
|
||||||
query = {
|
|
||||||
'mbr': 'true',
|
|
||||||
'assetTypes': 'high_video_ak',
|
|
||||||
'switch': 'hls_high_ak',
|
|
||||||
}
|
|
||||||
video_id = self._html_search_meta('aetn:VideoID', webpage)
|
video_id = self._html_search_meta('aetn:VideoID', webpage)
|
||||||
media_url = self._search_regex(
|
media_url = self._search_regex(
|
||||||
[r"media_url\s*=\s*'(?P<url>[^']+)'",
|
[r"media_url\s*=\s*'(?P<url>[^']+)'",
|
||||||
@@ -138,64 +177,39 @@ class AENetworksIE(AENetworksBaseIE):
|
|||||||
theplatform_metadata = self._download_theplatform_metadata(self._search_regex(
|
theplatform_metadata = self._download_theplatform_metadata(self._search_regex(
|
||||||
r'https?://link\.theplatform\.com/s/([^?]+)', media_url, 'theplatform_path'), video_id)
|
r'https?://link\.theplatform\.com/s/([^?]+)', media_url, 'theplatform_path'), video_id)
|
||||||
info = self._parse_theplatform_metadata(theplatform_metadata)
|
info = self._parse_theplatform_metadata(theplatform_metadata)
|
||||||
|
auth = None
|
||||||
if theplatform_metadata.get('AETN$isBehindWall'):
|
if theplatform_metadata.get('AETN$isBehindWall'):
|
||||||
requestor_id = self._DOMAIN_TO_REQUESTOR_ID[domain]
|
requestor_id = self._DOMAIN_TO_REQUESTOR_ID[domain]
|
||||||
resource = self._get_mvpd_resource(
|
resource = self._get_mvpd_resource(
|
||||||
requestor_id, theplatform_metadata['title'],
|
requestor_id, theplatform_metadata['title'],
|
||||||
theplatform_metadata.get('AETN$PPL_pplProgramId') or theplatform_metadata.get('AETN$PPL_pplProgramId_OLD'),
|
theplatform_metadata.get('AETN$PPL_pplProgramId') or theplatform_metadata.get('AETN$PPL_pplProgramId_OLD'),
|
||||||
theplatform_metadata['ratings'][0]['rating'])
|
theplatform_metadata['ratings'][0]['rating'])
|
||||||
query['auth'] = self._extract_mvpd_auth(
|
auth = self._extract_mvpd_auth(
|
||||||
url, video_id, requestor_id, resource)
|
url, video_id, requestor_id, resource)
|
||||||
info.update(self._search_json_ld(webpage, video_id, fatal=False))
|
info.update(self._search_json_ld(webpage, video_id, fatal=False))
|
||||||
media_url = update_url_query(media_url, query)
|
info.update(self._extract_aen_smil(media_url, video_id, auth))
|
||||||
media_url = self._sign_url(media_url, self._THEPLATFORM_KEY, self._THEPLATFORM_SECRET)
|
|
||||||
formats, subtitles = self._extract_theplatform_smil(media_url, video_id)
|
|
||||||
self._sort_formats(formats)
|
|
||||||
info.update({
|
|
||||||
'id': video_id,
|
|
||||||
'formats': formats,
|
|
||||||
'subtitles': subtitles,
|
|
||||||
})
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
class HistoryTopicIE(AENetworksBaseIE):
|
class HistoryTopicIE(AENetworksBaseIE):
|
||||||
IE_NAME = 'history:topic'
|
IE_NAME = 'history:topic'
|
||||||
IE_DESC = 'History.com Topic'
|
IE_DESC = 'History.com Topic'
|
||||||
_VALID_URL = r'https?://(?:www\.)?history\.com/topics/(?:[^/]+/)?(?P<topic_id>[^/]+)(?:/[^/]+(?:/(?P<video_display_id>[^/?#]+))?)?'
|
_VALID_URL = r'https?://(?:www\.)?history\.com/topics/[^/]+/(?P<id>[\w+-]+?)-video'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.history.com/topics/valentines-day/history-of-valentines-day/videos/bet-you-didnt-know-valentines-day?m=528e394da93ae&s=undefined&f=1&free=false',
|
'url': 'https://www.history.com/topics/valentines-day/history-of-valentines-day-video',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '40700995724',
|
'id': '40700995724',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': "Bet You Didn't Know: Valentine's Day",
|
'title': "History of Valentine’s Day",
|
||||||
'description': 'md5:7b57ea4829b391995b405fa60bd7b5f7',
|
'description': 'md5:7b57ea4829b391995b405fa60bd7b5f7',
|
||||||
'timestamp': 1375819729,
|
'timestamp': 1375819729,
|
||||||
'upload_date': '20130806',
|
'upload_date': '20130806',
|
||||||
'uploader': 'AENE-NEW',
|
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
# m3u8 download
|
# m3u8 download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
'add_ie': ['ThePlatform'],
|
'add_ie': ['ThePlatform'],
|
||||||
}, {
|
|
||||||
'url': 'http://www.history.com/topics/world-war-i/world-war-i-history/videos',
|
|
||||||
'info_dict':
|
|
||||||
{
|
|
||||||
'id': 'world-war-i-history',
|
|
||||||
'title': 'World War I History',
|
|
||||||
},
|
|
||||||
'playlist_mincount': 23,
|
|
||||||
}, {
|
|
||||||
'url': 'http://www.history.com/topics/world-war-i-history/videos',
|
|
||||||
'only_matching': True,
|
|
||||||
}, {
|
|
||||||
'url': 'http://www.history.com/topics/world-war-i/world-war-i-history',
|
|
||||||
'only_matching': True,
|
|
||||||
}, {
|
|
||||||
'url': 'http://www.history.com/topics/world-war-i/world-war-i-history/speeches',
|
|
||||||
'only_matching': True,
|
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def theplatform_url_result(self, theplatform_url, video_id, query):
|
def theplatform_url_result(self, theplatform_url, video_id, query):
|
||||||
@@ -215,27 +229,19 @@ class HistoryTopicIE(AENetworksBaseIE):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
topic_id, video_display_id = re.match(self._VALID_URL, url).groups()
|
display_id = self._match_id(url)
|
||||||
if video_display_id:
|
webpage = self._download_webpage(url, display_id)
|
||||||
webpage = self._download_webpage(url, video_display_id)
|
video_id = self._search_regex(
|
||||||
release_url, video_id = re.search(r"_videoPlayer.play\('([^']+)'\s*,\s*'[^']+'\s*,\s*'(\d+)'\)", webpage).groups()
|
r'<phoenix-iframe[^>]+src="[^"]+\btpid=(\d+)', webpage, 'tpid')
|
||||||
release_url = unescapeHTML(release_url)
|
result = self._download_json(
|
||||||
|
'https://feeds.video.aetnd.com/api/v2/history/videos',
|
||||||
return self.theplatform_url_result(
|
video_id, query={'filter[id]': video_id})['results'][0]
|
||||||
release_url, video_id, {
|
title = result['title']
|
||||||
'mbr': 'true',
|
info = self._extract_aen_smil(result['publicUrl'], video_id)
|
||||||
'switch': 'hls',
|
info.update({
|
||||||
'assetTypes': 'high_video_ak',
|
'title': title,
|
||||||
})
|
'description': result.get('description'),
|
||||||
else:
|
'duration': int_or_none(result.get('duration')),
|
||||||
webpage = self._download_webpage(url, topic_id)
|
'timestamp': int_or_none(result.get('added'), 1000),
|
||||||
entries = []
|
})
|
||||||
for episode_item in re.findall(r'<a.+?data-release-url="[^"]+"[^>]*>', webpage):
|
return info
|
||||||
video_attributes = extract_attributes(episode_item)
|
|
||||||
entries.append(self.theplatform_url_result(
|
|
||||||
video_attributes['data-release-url'], video_attributes['data-id'], {
|
|
||||||
'mbr': 'true',
|
|
||||||
'switch': 'hls',
|
|
||||||
'assetTypes': 'high_video_ak',
|
|
||||||
}))
|
|
||||||
return self.playlist_result(entries, topic_id, get_element_by_attribute('class', 'show-title', webpage))
|
|
||||||
|
@@ -4,6 +4,10 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..compat import (
|
||||||
|
compat_parse_qs,
|
||||||
|
compat_urllib_parse_urlparse,
|
||||||
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
@@ -12,12 +16,12 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class AolIE(InfoExtractor):
|
class AolIE(InfoExtractor):
|
||||||
IE_NAME = 'on.aol.com'
|
IE_NAME = 'aol.com'
|
||||||
_VALID_URL = r'(?:aol-video:|https?://(?:(?:www|on)\.)?aol\.com/(?:[^/]+/)*(?:[^/?#&]+-)?)(?P<id>[^/?#&]+)'
|
_VALID_URL = r'(?:aol-video:|https?://(?:www\.)?aol\.(?:com|ca|co\.uk|de|jp)/video/(?:[^/]+/)*)(?P<id>[0-9a-f]+)'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
# video with 5min ID
|
# video with 5min ID
|
||||||
'url': 'http://on.aol.com/video/u-s--official-warns-of-largest-ever-irs-phone-scam-518167793?icid=OnHomepageC2Wide_MustSee_Img',
|
'url': 'https://www.aol.com/video/view/u-s--official-warns-of-largest-ever-irs-phone-scam/518167793/',
|
||||||
'md5': '18ef68f48740e86ae94b98da815eec42',
|
'md5': '18ef68f48740e86ae94b98da815eec42',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '518167793',
|
'id': '518167793',
|
||||||
@@ -34,7 +38,7 @@ class AolIE(InfoExtractor):
|
|||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
# video with vidible ID
|
# video with vidible ID
|
||||||
'url': 'http://www.aol.com/video/view/netflix-is-raising-rates/5707d6b8e4b090497b04f706/',
|
'url': 'https://www.aol.com/video/view/netflix-is-raising-rates/5707d6b8e4b090497b04f706/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '5707d6b8e4b090497b04f706',
|
'id': '5707d6b8e4b090497b04f706',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
@@ -49,17 +53,29 @@ class AolIE(InfoExtractor):
|
|||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://on.aol.com/partners/abc-551438d309eab105804dbfe8/sneak-peek-was-haley-really-framed-570eaebee4b0448640a5c944',
|
'url': 'https://www.aol.com/video/view/park-bench-season-2-trailer/559a1b9be4b0c3bfad3357a7/',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://on.aol.com/shows/park-bench-shw518173474-559a1b9be4b0c3bfad3357a7?context=SH:SHW518173474:PL4327:1460619712763',
|
'url': 'https://www.aol.com/video/view/donald-trump-spokeswoman-tones-down-megyn-kelly-attacks/519442220/',
|
||||||
'only_matching': True,
|
|
||||||
}, {
|
|
||||||
'url': 'http://on.aol.com/video/519442220',
|
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}, {
|
}, {
|
||||||
'url': 'aol-video:5707d6b8e4b090497b04f706',
|
'url': 'aol-video:5707d6b8e4b090497b04f706',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.aol.com/video/playlist/PL8245/5ca79d19d21f1a04035db606/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.aol.ca/video/view/u-s-woman-s-family-arrested-for-murder-first-pinned-on-panhandler-police/5c7ccf45bc03931fa04b2fe1/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.aol.co.uk/video/view/-one-dead-and-22-hurt-in-bus-crash-/5cb3a6f3d21f1a072b457347/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.aol.de/video/view/eva-braun-privataufnahmen-von-hitlers-geliebter-werden-digitalisiert/5cb2d49de98ab54c113d3d5d/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.aol.jp/video/playlist/5a28e936a1334d000137da0c/5a28f3151e642219fde19831/',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
@@ -73,7 +89,7 @@ class AolIE(InfoExtractor):
|
|||||||
|
|
||||||
video_data = response['data']
|
video_data = response['data']
|
||||||
formats = []
|
formats = []
|
||||||
m3u8_url = video_data.get('videoMasterPlaylist')
|
m3u8_url = url_or_none(video_data.get('videoMasterPlaylist'))
|
||||||
if m3u8_url:
|
if m3u8_url:
|
||||||
formats.extend(self._extract_m3u8_formats(
|
formats.extend(self._extract_m3u8_formats(
|
||||||
m3u8_url, video_id, 'mp4', m3u8_id='hls', fatal=False))
|
m3u8_url, video_id, 'mp4', m3u8_id='hls', fatal=False))
|
||||||
@@ -96,6 +112,12 @@ class AolIE(InfoExtractor):
|
|||||||
'width': int(mobj.group(1)),
|
'width': int(mobj.group(1)),
|
||||||
'height': int(mobj.group(2)),
|
'height': int(mobj.group(2)),
|
||||||
})
|
})
|
||||||
|
else:
|
||||||
|
qs = compat_parse_qs(compat_urllib_parse_urlparse(video_url).query)
|
||||||
|
f.update({
|
||||||
|
'width': int_or_none(qs.get('w', [None])[0]),
|
||||||
|
'height': int_or_none(qs.get('h', [None])[0]),
|
||||||
|
})
|
||||||
formats.append(f)
|
formats.append(f)
|
||||||
self._sort_formats(formats, ('width', 'height', 'tbr', 'format_id'))
|
self._sort_formats(formats, ('width', 'height', 'tbr', 'format_id'))
|
||||||
|
|
||||||
|
37
youtube_dl/extractor/bfi.py
Normal file
37
youtube_dl/extractor/bfi.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..utils import extract_attributes
|
||||||
|
|
||||||
|
|
||||||
|
class BFIPlayerIE(InfoExtractor):
|
||||||
|
IE_NAME = 'bfi:player'
|
||||||
|
_VALID_URL = r'https?://player\.bfi\.org\.uk/[^/]+/film/watch-(?P<id>[\w-]+)-online'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'https://player.bfi.org.uk/free/film/watch-computer-doctor-1974-online',
|
||||||
|
'md5': 'e8783ebd8e061ec4bc6e9501ed547de8',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'htNnhlZjE60C9VySkQEIBtU-cNV1Xx63',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Computer Doctor',
|
||||||
|
'description': 'md5:fb6c240d40c4dbe40428bdd62f78203b',
|
||||||
|
},
|
||||||
|
'skip': 'BFI Player films cannot be played outside of the UK',
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
entries = []
|
||||||
|
for player_el in re.findall(r'(?s)<[^>]+class="player"[^>]*>', webpage):
|
||||||
|
player_attr = extract_attributes(player_el)
|
||||||
|
ooyala_id = player_attr.get('data-video-id')
|
||||||
|
if not ooyala_id:
|
||||||
|
continue
|
||||||
|
entries.append(self.url_result(
|
||||||
|
'ooyala:' + ooyala_id, 'Ooyala',
|
||||||
|
ooyala_id, player_attr.get('data-label')))
|
||||||
|
return self.playlist_result(entries)
|
@@ -28,7 +28,7 @@ class BIQLEIE(InfoExtractor):
|
|||||||
'url': 'http://biqle.org/watch/-44781847_168547604',
|
'url': 'http://biqle.org/watch/-44781847_168547604',
|
||||||
'md5': '7f24e72af1db0edf7c1aaba513174f97',
|
'md5': '7f24e72af1db0edf7c1aaba513174f97',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '168547604',
|
'id': '-44781847_168547604',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Ребенок в шоке от автоматической мойки',
|
'title': 'Ребенок в шоке от автоматической мойки',
|
||||||
'timestamp': 1396633454,
|
'timestamp': 1396633454,
|
||||||
|
@@ -55,6 +55,11 @@ class BitChuteIE(InfoExtractor):
|
|||||||
formats = [
|
formats = [
|
||||||
{'url': format_url}
|
{'url': format_url}
|
||||||
for format_url in orderedSet(format_urls)]
|
for format_url in orderedSet(format_urls)]
|
||||||
|
|
||||||
|
if not formats:
|
||||||
|
formats = self._parse_html5_media_entries(
|
||||||
|
url, webpage, video_id)[0]['formats']
|
||||||
|
|
||||||
self._check_formats(formats, video_id)
|
self._check_formats(formats, video_id)
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
@@ -65,8 +70,9 @@ class BitChuteIE(InfoExtractor):
|
|||||||
webpage, default=None) or self._html_search_meta(
|
webpage, default=None) or self._html_search_meta(
|
||||||
'twitter:image:src', webpage, 'thumbnail')
|
'twitter:image:src', webpage, 'thumbnail')
|
||||||
uploader = self._html_search_regex(
|
uploader = self._html_search_regex(
|
||||||
r'(?s)<p\b[^>]+\bclass=["\']video-author[^>]+>(.+?)</p>', webpage,
|
(r'(?s)<div class=["\']channel-banner.*?<p\b[^>]+\bclass=["\']name[^>]+>(.+?)</p>',
|
||||||
'uploader', fatal=False)
|
r'(?s)<p\b[^>]+\bclass=["\']video-author[^>]+>(.+?)</p>'),
|
||||||
|
webpage, 'uploader', fatal=False)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
|
@@ -32,8 +32,8 @@ class BlinkxIE(InfoExtractor):
|
|||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
display_id = video_id[:8]
|
display_id = video_id[:8]
|
||||||
|
|
||||||
api_url = ('https://apib4.blinkx.com/api.php?action=play_video&' +
|
api_url = ('https://apib4.blinkx.com/api.php?action=play_video&'
|
||||||
'video=%s' % video_id)
|
+ 'video=%s' % video_id)
|
||||||
data_json = self._download_webpage(api_url, display_id)
|
data_json = self._download_webpage(api_url, display_id)
|
||||||
data = json.loads(data_json)['api']['results'][0]
|
data = json.loads(data_json)['api']['results'][0]
|
||||||
duration = None
|
duration = None
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from .adobepass import AdobePassIE
|
from .adobepass import AdobePassIE
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
smuggle_url,
|
smuggle_url,
|
||||||
@@ -12,16 +14,16 @@ from ..utils import (
|
|||||||
class BravoTVIE(AdobePassIE):
|
class BravoTVIE(AdobePassIE):
|
||||||
_VALID_URL = r'https?://(?:www\.)?bravotv\.com/(?:[^/]+/)+(?P<id>[^/?#]+)'
|
_VALID_URL = r'https?://(?:www\.)?bravotv\.com/(?:[^/]+/)+(?P<id>[^/?#]+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.bravotv.com/last-chance-kitchen/season-5/videos/lck-ep-12-fishy-finale',
|
'url': 'https://www.bravotv.com/top-chef/season-16/episode-15/videos/the-top-chef-season-16-winner-is',
|
||||||
'md5': '9086d0b7ef0ea2aabc4781d75f4e5863',
|
'md5': 'e34684cfea2a96cd2ee1ef3a60909de9',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'zHyk1_HU_mPy',
|
'id': 'epL0pmK1kQlT',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'LCK Ep 12: Fishy Finale',
|
'title': 'The Top Chef Season 16 Winner Is...',
|
||||||
'description': 'S13/E12: Two eliminated chefs have just 12 minutes to cook up a delicious fish dish.',
|
'description': 'Find out who takes the title of Top Chef!',
|
||||||
'uploader': 'NBCU-BRAV',
|
'uploader': 'NBCU-BRAV',
|
||||||
'upload_date': '20160302',
|
'upload_date': '20190314',
|
||||||
'timestamp': 1456945320,
|
'timestamp': 1552591860,
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.bravotv.com/below-deck/season-3/ep-14-reunion-part-1',
|
'url': 'http://www.bravotv.com/below-deck/season-3/ep-14-reunion-part-1',
|
||||||
@@ -32,30 +34,38 @@ class BravoTVIE(AdobePassIE):
|
|||||||
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)
|
||||||
settings = self._parse_json(self._search_regex(
|
settings = self._parse_json(self._search_regex(
|
||||||
r'jQuery\.extend\(Drupal\.settings\s*,\s*({.+?})\);', webpage, 'drupal settings'),
|
r'<script[^>]+data-drupal-selector="drupal-settings-json"[^>]*>({.+?})</script>', webpage, 'drupal settings'),
|
||||||
display_id)
|
display_id)
|
||||||
info = {}
|
info = {}
|
||||||
query = {
|
query = {
|
||||||
'mbr': 'true',
|
'mbr': 'true',
|
||||||
}
|
}
|
||||||
account_pid, release_pid = [None] * 2
|
account_pid, release_pid = [None] * 2
|
||||||
tve = settings.get('sharedTVE')
|
tve = settings.get('ls_tve')
|
||||||
if tve:
|
if tve:
|
||||||
query['manifest'] = 'm3u'
|
query['manifest'] = 'm3u'
|
||||||
account_pid = 'HNK2IC'
|
mobj = re.search(r'<[^>]+id="pdk-player"[^>]+data-url=["\']?(?:https?:)?//player\.theplatform\.com/p/([^/]+)/(?:[^/]+/)*select/([^?#&"\']+)', webpage)
|
||||||
release_pid = tve['release_pid']
|
if mobj:
|
||||||
|
account_pid, tp_path = mobj.groups()
|
||||||
|
release_pid = tp_path.strip('/').split('/')[-1]
|
||||||
|
else:
|
||||||
|
account_pid = 'HNK2IC'
|
||||||
|
tp_path = release_pid = tve['release_pid']
|
||||||
if tve.get('entitlement') == 'auth':
|
if tve.get('entitlement') == 'auth':
|
||||||
adobe_pass = settings.get('adobePass', {})
|
adobe_pass = settings.get('tve_adobe_auth', {})
|
||||||
resource = self._get_mvpd_resource(
|
resource = self._get_mvpd_resource(
|
||||||
adobe_pass.get('adobePassResourceId', 'bravo'),
|
adobe_pass.get('adobePassResourceId', 'bravo'),
|
||||||
tve['title'], release_pid, tve.get('rating'))
|
tve['title'], release_pid, tve.get('rating'))
|
||||||
query['auth'] = self._extract_mvpd_auth(
|
query['auth'] = self._extract_mvpd_auth(
|
||||||
url, release_pid, adobe_pass.get('adobePassRequestorId', 'bravo'), resource)
|
url, release_pid, adobe_pass.get('adobePassRequestorId', 'bravo'), resource)
|
||||||
else:
|
else:
|
||||||
shared_playlist = settings['shared_playlist']
|
shared_playlist = settings['ls_playlist']
|
||||||
account_pid = shared_playlist['account_pid']
|
account_pid = shared_playlist['account_pid']
|
||||||
metadata = shared_playlist['video_metadata'][shared_playlist['default_clip']]
|
metadata = shared_playlist['video_metadata'][shared_playlist['default_clip']]
|
||||||
release_pid = metadata['release_pid']
|
tp_path = release_pid = metadata.get('release_pid')
|
||||||
|
if not release_pid:
|
||||||
|
release_pid = metadata['guid']
|
||||||
|
tp_path = 'media/guid/2140479951/' + release_pid
|
||||||
info.update({
|
info.update({
|
||||||
'title': metadata['title'],
|
'title': metadata['title'],
|
||||||
'description': metadata.get('description'),
|
'description': metadata.get('description'),
|
||||||
@@ -67,7 +77,7 @@ class BravoTVIE(AdobePassIE):
|
|||||||
'_type': 'url_transparent',
|
'_type': 'url_transparent',
|
||||||
'id': release_pid,
|
'id': release_pid,
|
||||||
'url': smuggle_url(update_url_query(
|
'url': smuggle_url(update_url_query(
|
||||||
'http://link.theplatform.com/s/%s/%s' % (account_pid, release_pid),
|
'http://link.theplatform.com/s/%s/%s' % (account_pid, tp_path),
|
||||||
query), {'force_smil_url': True}),
|
query), {'force_smil_url': True}),
|
||||||
'ie_key': 'ThePlatform',
|
'ie_key': 'ThePlatform',
|
||||||
})
|
})
|
||||||
|
@@ -3,11 +3,13 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..utils import parse_duration
|
||||||
|
|
||||||
|
|
||||||
class BYUtvIE(InfoExtractor):
|
class BYUtvIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?byutv\.org/(?:watch|player)/(?!event/)(?P<id>[0-9a-f-]+)(?:/(?P<display_id>[^/?#&]+))?'
|
_VALID_URL = r'https?://(?:www\.)?byutv\.org/(?:watch|player)/(?!event/)(?P<id>[0-9a-f-]+)(?:/(?P<display_id>[^/?#&]+))?'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
|
# ooyalaVOD
|
||||||
'url': 'http://www.byutv.org/watch/6587b9a3-89d2-42a6-a7f7-fd2f81840a7d/studio-c-season-5-episode-5',
|
'url': 'http://www.byutv.org/watch/6587b9a3-89d2-42a6-a7f7-fd2f81840a7d/studio-c-season-5-episode-5',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'ZvanRocTpW-G5_yZFeltTAMv6jxOU9KH',
|
'id': 'ZvanRocTpW-G5_yZFeltTAMv6jxOU9KH',
|
||||||
@@ -22,6 +24,20 @@ class BYUtvIE(InfoExtractor):
|
|||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
'add_ie': ['Ooyala'],
|
'add_ie': ['Ooyala'],
|
||||||
|
}, {
|
||||||
|
# dvr
|
||||||
|
'url': 'https://www.byutv.org/player/8f1dab9b-b243-47c8-b525-3e2d021a3451/byu-softball-pacific-vs-byu-41219---game-2',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '8f1dab9b-b243-47c8-b525-3e2d021a3451',
|
||||||
|
'display_id': 'byu-softball-pacific-vs-byu-41219---game-2',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Pacific vs. BYU (4/12/19)',
|
||||||
|
'description': 'md5:1ac7b57cb9a78015910a4834790ce1f3',
|
||||||
|
'duration': 11645,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.byutv.org/watch/6587b9a3-89d2-42a6-a7f7-fd2f81840a7d',
|
'url': 'http://www.byutv.org/watch/6587b9a3-89d2-42a6-a7f7-fd2f81840a7d',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
@@ -35,24 +51,42 @@ class BYUtvIE(InfoExtractor):
|
|||||||
video_id = mobj.group('id')
|
video_id = mobj.group('id')
|
||||||
display_id = mobj.group('display_id') or video_id
|
display_id = mobj.group('display_id') or video_id
|
||||||
|
|
||||||
ep = self._download_json(
|
info = self._download_json(
|
||||||
'https://api.byutv.org/api3/catalog/getvideosforcontent', video_id,
|
'https://api.byutv.org/api3/catalog/getvideosforcontent',
|
||||||
query={
|
display_id, query={
|
||||||
'contentid': video_id,
|
'contentid': video_id,
|
||||||
'channel': 'byutv',
|
'channel': 'byutv',
|
||||||
'x-byutv-context': 'web$US',
|
'x-byutv-context': 'web$US',
|
||||||
}, headers={
|
}, headers={
|
||||||
'x-byutv-context': 'web$US',
|
'x-byutv-context': 'web$US',
|
||||||
'x-byutv-platformkey': 'xsaaw9c7y5',
|
'x-byutv-platformkey': 'xsaaw9c7y5',
|
||||||
})['ooyalaVOD']
|
})
|
||||||
|
|
||||||
|
ep = info.get('ooyalaVOD')
|
||||||
|
if ep:
|
||||||
|
return {
|
||||||
|
'_type': 'url_transparent',
|
||||||
|
'ie_key': 'Ooyala',
|
||||||
|
'url': 'ooyala:%s' % ep['providerId'],
|
||||||
|
'id': video_id,
|
||||||
|
'display_id': display_id,
|
||||||
|
'title': ep.get('title'),
|
||||||
|
'description': ep.get('description'),
|
||||||
|
'thumbnail': ep.get('imageThumbnail'),
|
||||||
|
}
|
||||||
|
|
||||||
|
ep = info['dvr']
|
||||||
|
title = ep['title']
|
||||||
|
formats = self._extract_m3u8_formats(
|
||||||
|
ep['videoUrl'], video_id, 'mp4', entry_protocol='m3u8_native',
|
||||||
|
m3u8_id='hls')
|
||||||
|
self._sort_formats(formats)
|
||||||
return {
|
return {
|
||||||
'_type': 'url_transparent',
|
|
||||||
'ie_key': 'Ooyala',
|
|
||||||
'url': 'ooyala:%s' % ep['providerId'],
|
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'display_id': display_id,
|
'display_id': display_id,
|
||||||
'title': ep.get('title'),
|
'title': title,
|
||||||
'description': ep.get('description'),
|
'description': ep.get('description'),
|
||||||
'thumbnail': ep.get('imageThumbnail'),
|
'thumbnail': ep.get('imageThumbnail'),
|
||||||
|
'duration': parse_duration(ep.get('length')),
|
||||||
|
'formats': formats,
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class CanvasIE(InfoExtractor):
|
class CanvasIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://mediazone\.vrt\.be/api/v1/(?P<site_id>canvas|een|ketnet|vrtvideo)/assets/(?P<id>[^/?#&]+)'
|
_VALID_URL = r'https?://mediazone\.vrt\.be/api/v1/(?P<site_id>canvas|een|ketnet|vrt(?:video|nieuws)|sporza)/assets/(?P<id>[^/?#&]+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'https://mediazone.vrt.be/api/v1/ketnet/assets/md-ast-4ac54990-ce66-4d00-a8ca-9eac86f4c475',
|
'url': 'https://mediazone.vrt.be/api/v1/ketnet/assets/md-ast-4ac54990-ce66-4d00-a8ca-9eac86f4c475',
|
||||||
'md5': '90139b746a0a9bd7bb631283f6e2a64e',
|
'md5': '90139b746a0a9bd7bb631283f6e2a64e',
|
||||||
@@ -35,6 +35,10 @@ class CanvasIE(InfoExtractor):
|
|||||||
'url': 'https://mediazone.vrt.be/api/v1/canvas/assets/mz-ast-5e5f90b6-2d72-4c40-82c2-e134f884e93e',
|
'url': 'https://mediazone.vrt.be/api/v1/canvas/assets/mz-ast-5e5f90b6-2d72-4c40-82c2-e134f884e93e',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
_HLS_ENTRY_PROTOCOLS_MAP = {
|
||||||
|
'HLS': 'm3u8_native',
|
||||||
|
'HLS_AES': 'm3u8',
|
||||||
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
@@ -52,9 +56,9 @@ class CanvasIE(InfoExtractor):
|
|||||||
format_url, format_type = target.get('url'), target.get('type')
|
format_url, format_type = target.get('url'), target.get('type')
|
||||||
if not format_url or not format_type:
|
if not format_url or not format_type:
|
||||||
continue
|
continue
|
||||||
if format_type == 'HLS':
|
if format_type in self._HLS_ENTRY_PROTOCOLS_MAP:
|
||||||
formats.extend(self._extract_m3u8_formats(
|
formats.extend(self._extract_m3u8_formats(
|
||||||
format_url, video_id, 'mp4', entry_protocol='m3u8_native',
|
format_url, video_id, 'mp4', self._HLS_ENTRY_PROTOCOLS_MAP[format_type],
|
||||||
m3u8_id=format_type, fatal=False))
|
m3u8_id=format_type, fatal=False))
|
||||||
elif format_type == 'HDS':
|
elif format_type == 'HDS':
|
||||||
formats.extend(self._extract_f4m_formats(
|
formats.extend(self._extract_f4m_formats(
|
||||||
|
@@ -13,13 +13,17 @@ from ..utils import (
|
|||||||
|
|
||||||
class CBSBaseIE(ThePlatformFeedIE):
|
class CBSBaseIE(ThePlatformFeedIE):
|
||||||
def _parse_smil_subtitles(self, smil, namespace=None, subtitles_lang='en'):
|
def _parse_smil_subtitles(self, smil, namespace=None, subtitles_lang='en'):
|
||||||
closed_caption_e = find_xpath_attr(smil, self._xpath_ns('.//param', namespace), 'name', 'ClosedCaptionURL')
|
subtitles = {}
|
||||||
return {
|
for k, ext in [('sMPTE-TTCCURL', 'tt'), ('ClosedCaptionURL', 'ttml'), ('webVTTCaptionURL', 'vtt')]:
|
||||||
'en': [{
|
cc_e = find_xpath_attr(smil, self._xpath_ns('.//param', namespace), 'name', k)
|
||||||
'ext': 'ttml',
|
if cc_e is not None:
|
||||||
'url': closed_caption_e.attrib['value'],
|
cc_url = cc_e.get('value')
|
||||||
}]
|
if cc_url:
|
||||||
} if closed_caption_e is not None and closed_caption_e.attrib.get('value') else []
|
subtitles.setdefault(subtitles_lang, []).append({
|
||||||
|
'ext': ext,
|
||||||
|
'url': cc_url,
|
||||||
|
})
|
||||||
|
return subtitles
|
||||||
|
|
||||||
|
|
||||||
class CBSIE(CBSBaseIE):
|
class CBSIE(CBSBaseIE):
|
||||||
|
@@ -1,40 +1,62 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
import zlib
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from .cbs import CBSIE
|
from .cbs import CBSIE
|
||||||
|
from ..compat import (
|
||||||
|
compat_b64decode,
|
||||||
|
compat_urllib_parse_unquote,
|
||||||
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
parse_duration,
|
parse_duration,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CBSNewsEmbedIE(CBSIE):
|
||||||
|
IE_NAME = 'cbsnews:embed'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?cbsnews\.com/embed/video[^#]*#(?P<id>.+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://www.cbsnews.com/embed/video/?v=1.c9b5b61492913d6660db0b2f03579ef25e86307a#1Vb7b9s2EP5XBAHbT6Gt98PAMKTJ0se6LVjWYWtdGBR1stlIpEBSTtwi%2F%2FvuJNkNhmHdGxgM2NL57vjd6zt%2B8PngdN%2Fyg79qeGvhzN%2FLGrS%2F%2BuBLB531V28%2B%2BO7Qg7%2Fy97r2z3xZ42NW8yLhDbA0S0KWlHnIijwKWJBHZZnHBa8Cgbpdf%2F89NM9Hi9fXifhpr8sr%2FlP848tn%2BTdXycX25zh4cdX%2FvHl6PmmPqnWQv9w8Ed%2B9GjYRim07bFEqdG%2BZVHuwTm65A7bVRrYtR5lAyMox7pigF6W4k%2By91mjspGsJ%2BwVae4%2BsvdnaO1p73HkXs%2FVisUDTGm7R8IcdnOROeq%2B19qT1amhA1VJtPenoTUgrtfKc9m7Rq8dP7nnjwOB7wg7ADdNt7VX64DWAWlKhPtmDEq22g4GF99x6Dk9E8OSsankHXqPNKDxC%2FdK7MLKTircTDgsI3mmj4OBdSq64dy7fd1x577RU1rt4cvMtOaulFYOd%2FLewRWvDO9lIgXFpZSnkZmjbv5SxKTPoQXClFbpsf%2Fhbbpzs0IB3vb8KkyzJQ%2BywOAgCrMpgRrz%2BKk4fvb7kFbR4XJCu0gAdtNO7woCwZTu%2BBUs9bam%2Fds71drVerpeisgrubLjAB4nnOSkWQnfr5W6o1ku5Xpr1MgrCbL0M0vUyDtfLLK15WiYp47xKWSLyjFVpwVmVJSLIoCjSOFkv3W7oKsVliwZJcB9nwXpZ5GEQQwY8jNKqKCBrgjTLeFxgdCIpazojDgnRtn43J6kG7nZ6cAbxh0EeFFk4%2B1u867cY5u4344n%2FxXjCqAjucdTHgLKojNKmSfO8KRsOFY%2FzKEYCKEJBzv90QA9nfm9gL%2BHulaFqUkz9ULUYxl62B3U%2FRVNLA8IhggaPycOoBuwOCESciDQVSSUgiOMsROB%2FhKfwCKOzEk%2B4k6rWd4uuT%2FwTDz7K7t3d3WLO8ISD95jSPQbayBacthbz86XVgxHwhex5zawzgDOmtp%2F3GPcXn0VXHdSS029%2Fj99UC%2FwJUvyKQ%2FzKyixIEVlYJOn4RxxuaH43Ty9fbJ5OObykHH435XAzJTHeOF4hhEUXD8URe%2FQ%2FBT%2BMpf8d5GN02Ox%2FfiGsl7TA7POu1xZ5%2BbTzcAVKMe48mqcC21hkacVEVScM26liVVBnrKkC4CLKyzAvHu0lhEaTKMFwI3a4SN9MsrfYzdBLq2vkwRD1gVviLT8kY9h2CHH6Y%2Bix6609weFtey4ESp60WtyeWMy%2BsmBuhsoKIyuoT%2Bq2R%2FrW5qi3g%2FvzS2j40DoixDP8%2BKP0yUdpXJ4l6Vla%2Bg9vce%2BC4yM5YlUcbA%2F0jLKdpmTwvsdN5z88nAIe08%2F0HgxeG1iv%2B6Hlhjh7uiW0SDzYNI92L401uha3JKYk268UVRzdOzNQvAaJqoXzAc80dAV440NZ1WVVAAMRYQ2KrGJFmDUsq8saWSnjvIj8t78y%2FRa3JRnbHVfyFpfwoDiGpPgjzekyUiKNlU3OMlwuLMmzgvEojllYVE2Z1HhImvsnk%2BuhusTEoB21PAtSFodeFK3iYhXEH9WOG2%2FkOE833sfeG%2Ff5cfHtEFNXgYes0%2FXj7aGivUgJ9XpusCtoNcNYVVnJVrrDo0OmJAutHCpuZul4W9lLcfy7BnuLPT02%2ByXsCTk%2B9zhzswIN04YueNSK%2BPtM0jS88QdLqSLJDTLsuGZJNolm2yO0PXh3UPnz9Ix5bfIAqxPjvETQsDCEiPG4QbqNyhBZISxybLnZYCrW5H3Axp690%2F0BJdXtDZ5ITuM4xj3f4oUHGzc5JeJmZKpp%2FjwKh4wMV%2FV1yx3emLoR0MwbG4K%2F%2BZgVep3PnzXGDHZ6a3i%2Fk%2BJrONDN13%2Bnq6tBTYk4o7cLGhBtqCC4KwacGHpEVuoH5JNro%2FE6JfE6d5RydbiR76k%2BW5wioDHBIjw1euhHjUGRB0y5A97KoaPx6MlL%2BwgboUVtUFRI%2FLemgTpdtF59ii7pab08kuPcfWzs0l%2FRI5takWnFpka0zOgWRtYcuf9aIxZMxlwr6IiGpsb6j2DQUXPl%2FimXI599Ev7fWjoPD78A',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
item = self._parse_json(zlib.decompress(compat_b64decode(
|
||||||
|
compat_urllib_parse_unquote(self._match_id(url))),
|
||||||
|
-zlib.MAX_WBITS), None)['video']['items'][0]
|
||||||
|
return self._extract_video_info(item['mpxRefId'], 'cbsnews')
|
||||||
|
|
||||||
|
|
||||||
class CBSNewsIE(CBSIE):
|
class CBSNewsIE(CBSIE):
|
||||||
IE_NAME = 'cbsnews'
|
IE_NAME = 'cbsnews'
|
||||||
IE_DESC = 'CBS News'
|
IE_DESC = 'CBS News'
|
||||||
_VALID_URL = r'https?://(?:www\.)?cbsnews\.com/(?:news|videos)/(?P<id>[\da-z_-]+)'
|
_VALID_URL = r'https?://(?:www\.)?cbsnews\.com/(?:news|video)/(?P<id>[\da-z_-]+)'
|
||||||
|
|
||||||
_TESTS = [
|
_TESTS = [
|
||||||
{
|
{
|
||||||
# 60 minutes
|
# 60 minutes
|
||||||
'url': 'http://www.cbsnews.com/news/artificial-intelligence-positioned-to-be-a-game-changer/',
|
'url': 'http://www.cbsnews.com/news/artificial-intelligence-positioned-to-be-a-game-changer/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '_B6Ga3VJrI4iQNKsir_cdFo9Re_YJHE_',
|
'id': 'Y_nf_aEg6WwO9OLAq0MpKaPgfnBUxfW4',
|
||||||
'ext': 'mp4',
|
'ext': 'flv',
|
||||||
'title': 'Artificial Intelligence',
|
'title': 'Artificial Intelligence, real-life applications',
|
||||||
'description': 'md5:8818145f9974431e0fb58a1b8d69613c',
|
'description': 'md5:a7aaf27f1b4777244de8b0b442289304',
|
||||||
'thumbnail': r're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 1606,
|
'duration': 317,
|
||||||
'uploader': 'CBSI-NEW',
|
'uploader': 'CBSI-NEW',
|
||||||
'timestamp': 1498431900,
|
'timestamp': 1476046464,
|
||||||
'upload_date': '20170625',
|
'upload_date': '20161009',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
# m3u8 download
|
# rtmp download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': 'http://www.cbsnews.com/videos/fort-hood-shooting-army-downplays-mental-illness-as-cause-of-attack/',
|
'url': 'https://www.cbsnews.com/video/fort-hood-shooting-army-downplays-mental-illness-as-cause-of-attack/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'SNJBOYzXiWBOvaLsdzwH8fmtP1SCd91Y',
|
'id': 'SNJBOYzXiWBOvaLsdzwH8fmtP1SCd91Y',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
@@ -60,37 +82,29 @@ class CBSNewsIE(CBSIE):
|
|||||||
# 48 hours
|
# 48 hours
|
||||||
'url': 'http://www.cbsnews.com/news/maria-ridulph-murder-will-the-nations-oldest-cold-case-to-go-to-trial-ever-get-solved/',
|
'url': 'http://www.cbsnews.com/news/maria-ridulph-murder-will-the-nations-oldest-cold-case-to-go-to-trial-ever-get-solved/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'QpM5BJjBVEAUFi7ydR9LusS69DPLqPJ1',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Cold as Ice',
|
'title': 'Cold as Ice',
|
||||||
'description': 'Can a childhood memory of a friend\'s murder solve a 1957 cold case? "48 Hours" correspondent Erin Moriarty has the latest.',
|
'description': 'Can a childhood memory solve the 1957 murder of 7-year-old Maria Ridulph?',
|
||||||
'upload_date': '20170604',
|
|
||||||
'timestamp': 1496538000,
|
|
||||||
'uploader': 'CBSI-NEW',
|
|
||||||
},
|
|
||||||
'params': {
|
|
||||||
'skip_download': True,
|
|
||||||
},
|
},
|
||||||
|
'playlist_mincount': 7,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
display_id = self._match_id(url)
|
||||||
|
|
||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
|
||||||
video_info = self._parse_json(self._html_search_regex(
|
entries = []
|
||||||
r'(?:<ul class="media-list items" id="media-related-items"[^>]*><li data-video-info|<div id="cbsNewsVideoPlayer" data-video-player-options)=\'({.+?})\'',
|
for embed_url in re.findall(r'<iframe[^>]+data-src="(https?://(?:www\.)?cbsnews\.com/embed/video/[^#]*#[^"]+)"', webpage):
|
||||||
webpage, 'video JSON info', default='{}'), video_id, fatal=False)
|
entries.append(self.url_result(embed_url, CBSNewsEmbedIE.ie_key()))
|
||||||
|
if entries:
|
||||||
if video_info:
|
return self.playlist_result(
|
||||||
item = video_info['item'] if 'item' in video_info else video_info
|
entries, playlist_title=self._html_search_meta(['og:title', 'twitter:title'], webpage),
|
||||||
else:
|
playlist_description=self._html_search_meta(['og:description', 'twitter:description', 'description'], webpage))
|
||||||
state = self._parse_json(self._search_regex(
|
|
||||||
r'data-cbsvideoui-options=(["\'])(?P<json>{.+?})\1', webpage,
|
|
||||||
'playlist JSON info', group='json'), video_id)['state']
|
|
||||||
item = state['playlist'][state['pid']]
|
|
||||||
|
|
||||||
|
item = self._parse_json(self._html_search_regex(
|
||||||
|
r'CBSNEWS\.defaultPayload\s*=\s*({.+})',
|
||||||
|
webpage, 'video JSON info'), display_id)['items'][0]
|
||||||
return self._extract_video_info(item['mpxRefId'], 'cbsnews')
|
return self._extract_video_info(item['mpxRefId'], 'cbsnews')
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,9 +1,12 @@
|
|||||||
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
|
try_get,
|
||||||
|
url_or_none,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -18,11 +21,13 @@ class CCCIE(InfoExtractor):
|
|||||||
'id': '1839',
|
'id': '1839',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Introduction to Processor Design',
|
'title': 'Introduction to Processor Design',
|
||||||
|
'creator': 'byterazor',
|
||||||
'description': 'md5:df55f6d073d4ceae55aae6f2fd98a0ac',
|
'description': 'md5:df55f6d073d4ceae55aae6f2fd98a0ac',
|
||||||
'thumbnail': r're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'upload_date': '20131228',
|
'upload_date': '20131228',
|
||||||
'timestamp': 1388188800,
|
'timestamp': 1388188800,
|
||||||
'duration': 3710,
|
'duration': 3710,
|
||||||
|
'tags': list,
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://media.ccc.de/v/32c3-7368-shopshifting#download',
|
'url': 'https://media.ccc.de/v/32c3-7368-shopshifting#download',
|
||||||
@@ -68,6 +73,7 @@ class CCCIE(InfoExtractor):
|
|||||||
'id': event_id,
|
'id': event_id,
|
||||||
'display_id': display_id,
|
'display_id': display_id,
|
||||||
'title': event_data['title'],
|
'title': event_data['title'],
|
||||||
|
'creator': try_get(event_data, lambda x: ', '.join(x['persons'])),
|
||||||
'description': event_data.get('description'),
|
'description': event_data.get('description'),
|
||||||
'thumbnail': event_data.get('thumb_url'),
|
'thumbnail': event_data.get('thumb_url'),
|
||||||
'timestamp': parse_iso8601(event_data.get('date')),
|
'timestamp': parse_iso8601(event_data.get('date')),
|
||||||
@@ -75,3 +81,31 @@ class CCCIE(InfoExtractor):
|
|||||||
'tags': event_data.get('tags'),
|
'tags': event_data.get('tags'),
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CCCPlaylistIE(InfoExtractor):
|
||||||
|
IE_NAME = 'media.ccc.de:lists'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?media\.ccc\.de/c/(?P<id>[^/?#&]+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://media.ccc.de/c/30c3',
|
||||||
|
'info_dict': {
|
||||||
|
'title': '30C3',
|
||||||
|
'id': '30c3',
|
||||||
|
},
|
||||||
|
'playlist_count': 135,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
playlist_id = self._match_id(url).lower()
|
||||||
|
|
||||||
|
conf = self._download_json(
|
||||||
|
'https://media.ccc.de/public/conferences/' + playlist_id,
|
||||||
|
playlist_id)
|
||||||
|
|
||||||
|
entries = []
|
||||||
|
for e in conf['events']:
|
||||||
|
event_url = url_or_none(e.get('frontend_link'))
|
||||||
|
if event_url:
|
||||||
|
entries.append(self.url_result(event_url, ie=CCCIE.ie_key()))
|
||||||
|
|
||||||
|
return self.playlist_result(entries, playlist_id, conf.get('title'))
|
||||||
|
29
youtube_dl/extractor/cinemax.py
Normal file
29
youtube_dl/extractor/cinemax.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from .hbo import HBOBaseIE
|
||||||
|
|
||||||
|
|
||||||
|
class CinemaxIE(HBOBaseIE):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?cinemax\.com/(?P<path>[^/]+/video/[0-9a-z-]+-(?P<id>\d+))'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://www.cinemax.com/warrior/video/s1-ep-1-recap-20126903',
|
||||||
|
'md5': '82e0734bba8aa7ef526c9dd00cf35a05',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '20126903',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'S1 Ep 1: Recap',
|
||||||
|
},
|
||||||
|
'expected_warnings': ['Unknown MIME type application/mp4 in DASH manifest'],
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.cinemax.com/warrior/video/s1-ep-1-recap-20126903.embed',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
path, video_id = re.match(self._VALID_URL, url).groups()
|
||||||
|
info = self._extract_info('https://www.cinemax.com/%s.xml' % path, video_id)
|
||||||
|
info['id'] = video_id
|
||||||
|
return info
|
@@ -10,8 +10,8 @@ class CloudflareStreamIE(InfoExtractor):
|
|||||||
_VALID_URL = r'''(?x)
|
_VALID_URL = r'''(?x)
|
||||||
https?://
|
https?://
|
||||||
(?:
|
(?:
|
||||||
(?:watch\.)?cloudflarestream\.com/|
|
(?:watch\.)?(?:cloudflarestream\.com|videodelivery\.net)/|
|
||||||
embed\.cloudflarestream\.com/embed/[^/]+\.js\?.*?\bvideo=
|
embed\.(?:cloudflarestream\.com|videodelivery\.net)/embed/[^/]+\.js\?.*?\bvideo=
|
||||||
)
|
)
|
||||||
(?P<id>[\da-f]+)
|
(?P<id>[\da-f]+)
|
||||||
'''
|
'''
|
||||||
@@ -31,6 +31,9 @@ class CloudflareStreamIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'https://cloudflarestream.com/31c9291ab41fac05471db4e73aa11717/manifest/video.mpd',
|
'url': 'https://cloudflarestream.com/31c9291ab41fac05471db4e73aa11717/manifest/video.mpd',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://embed.videodelivery.net/embed/r4xu.fla9.latest.js?video=81d80727f3022488598f68d323c1ad5e',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -38,7 +41,7 @@ class CloudflareStreamIE(InfoExtractor):
|
|||||||
return [
|
return [
|
||||||
mobj.group('url')
|
mobj.group('url')
|
||||||
for mobj in re.finditer(
|
for mobj in re.finditer(
|
||||||
r'<script[^>]+\bsrc=(["\'])(?P<url>(?:https?:)?//embed\.cloudflarestream\.com/embed/[^/]+\.js\?.*?\bvideo=[\da-f]+?.*?)\1',
|
r'<script[^>]+\bsrc=(["\'])(?P<url>(?:https?:)?//embed\.(?:cloudflarestream\.com|videodelivery\.net)/embed/[^/]+\.js\?.*?\bvideo=[\da-f]+?.*?)\1',
|
||||||
webpage)]
|
webpage)]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
@@ -67,6 +67,7 @@ from ..utils import (
|
|||||||
sanitized_Request,
|
sanitized_Request,
|
||||||
sanitize_filename,
|
sanitize_filename,
|
||||||
str_or_none,
|
str_or_none,
|
||||||
|
strip_or_none,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
unified_strdate,
|
unified_strdate,
|
||||||
unified_timestamp,
|
unified_timestamp,
|
||||||
@@ -117,7 +118,7 @@ class InfoExtractor(object):
|
|||||||
unfragmented media)
|
unfragmented media)
|
||||||
- URL of the MPD manifest or base URL
|
- URL of the MPD manifest or base URL
|
||||||
representing the media if MPD manifest
|
representing the media if MPD manifest
|
||||||
is parsed froma string (in case of
|
is parsed from a string (in case of
|
||||||
fragmented media)
|
fragmented media)
|
||||||
for MSS - URL of the ISM manifest.
|
for MSS - URL of the ISM manifest.
|
||||||
* manifest_url
|
* manifest_url
|
||||||
@@ -542,11 +543,11 @@ class InfoExtractor(object):
|
|||||||
raise ExtractorError('An extractor error has occurred.', cause=e)
|
raise ExtractorError('An extractor error has occurred.', cause=e)
|
||||||
|
|
||||||
def __maybe_fake_ip_and_retry(self, countries):
|
def __maybe_fake_ip_and_retry(self, countries):
|
||||||
if (not self._downloader.params.get('geo_bypass_country', None) and
|
if (not self._downloader.params.get('geo_bypass_country', None)
|
||||||
self._GEO_BYPASS and
|
and self._GEO_BYPASS
|
||||||
self._downloader.params.get('geo_bypass', True) and
|
and self._downloader.params.get('geo_bypass', True)
|
||||||
not self._x_forwarded_for_ip and
|
and not self._x_forwarded_for_ip
|
||||||
countries):
|
and countries):
|
||||||
country_code = random.choice(countries)
|
country_code = random.choice(countries)
|
||||||
self._x_forwarded_for_ip = GeoUtils.random_ipv4(country_code)
|
self._x_forwarded_for_ip = GeoUtils.random_ipv4(country_code)
|
||||||
if self._x_forwarded_for_ip:
|
if self._x_forwarded_for_ip:
|
||||||
@@ -682,8 +683,8 @@ class InfoExtractor(object):
|
|||||||
|
|
||||||
def __check_blocked(self, content):
|
def __check_blocked(self, content):
|
||||||
first_block = content[:512]
|
first_block = content[:512]
|
||||||
if ('<title>Access to this site is blocked</title>' in content and
|
if ('<title>Access to this site is blocked</title>' in content
|
||||||
'Websense' in first_block):
|
and 'Websense' in first_block):
|
||||||
msg = 'Access to this webpage has been blocked by Websense filtering software in your network.'
|
msg = 'Access to this webpage has been blocked by Websense filtering software in your network.'
|
||||||
blocked_iframe = self._html_search_regex(
|
blocked_iframe = self._html_search_regex(
|
||||||
r'<iframe src="([^"]+)"', content,
|
r'<iframe src="([^"]+)"', content,
|
||||||
@@ -701,8 +702,8 @@ class InfoExtractor(object):
|
|||||||
if block_msg:
|
if block_msg:
|
||||||
msg += ' (Message: "%s")' % block_msg.replace('\n', ' ')
|
msg += ' (Message: "%s")' % block_msg.replace('\n', ' ')
|
||||||
raise ExtractorError(msg, expected=True)
|
raise ExtractorError(msg, expected=True)
|
||||||
if ('<title>TTK :: Доступ к ресурсу ограничен</title>' in content and
|
if ('<title>TTK :: Доступ к ресурсу ограничен</title>' in content
|
||||||
'blocklist.rkn.gov.ru' in content):
|
and 'blocklist.rkn.gov.ru' in content):
|
||||||
raise ExtractorError(
|
raise ExtractorError(
|
||||||
'Access to this webpage has been blocked by decision of the Russian government. '
|
'Access to this webpage has been blocked by decision of the Russian government. '
|
||||||
'Visit http://blocklist.rkn.gov.ru/ for a block reason.',
|
'Visit http://blocklist.rkn.gov.ru/ for a block reason.',
|
||||||
@@ -1709,8 +1710,8 @@ class InfoExtractor(object):
|
|||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
tbr = float_or_none(
|
tbr = float_or_none(
|
||||||
last_stream_inf.get('AVERAGE-BANDWIDTH') or
|
last_stream_inf.get('AVERAGE-BANDWIDTH')
|
||||||
last_stream_inf.get('BANDWIDTH'), scale=1000)
|
or last_stream_inf.get('BANDWIDTH'), scale=1000)
|
||||||
format_id = []
|
format_id = []
|
||||||
if m3u8_id:
|
if m3u8_id:
|
||||||
format_id.append(m3u8_id)
|
format_id.append(m3u8_id)
|
||||||
@@ -2019,6 +2020,8 @@ class InfoExtractor(object):
|
|||||||
if res is False:
|
if res is False:
|
||||||
return []
|
return []
|
||||||
mpd_doc, urlh = res
|
mpd_doc, urlh = res
|
||||||
|
if mpd_doc is None:
|
||||||
|
return []
|
||||||
mpd_base_url = base_url(urlh.geturl())
|
mpd_base_url = base_url(urlh.geturl())
|
||||||
|
|
||||||
return self._parse_mpd_formats(
|
return self._parse_mpd_formats(
|
||||||
@@ -2478,7 +2481,7 @@ class InfoExtractor(object):
|
|||||||
'subtitles': {},
|
'subtitles': {},
|
||||||
}
|
}
|
||||||
media_attributes = extract_attributes(media_tag)
|
media_attributes = extract_attributes(media_tag)
|
||||||
src = media_attributes.get('src')
|
src = strip_or_none(media_attributes.get('src'))
|
||||||
if src:
|
if src:
|
||||||
_, formats = _media_formats(src, media_type)
|
_, formats = _media_formats(src, media_type)
|
||||||
media_info['formats'].extend(formats)
|
media_info['formats'].extend(formats)
|
||||||
@@ -2488,7 +2491,7 @@ class InfoExtractor(object):
|
|||||||
s_attr = extract_attributes(source_tag)
|
s_attr = extract_attributes(source_tag)
|
||||||
# data-video-src and data-src are non standard but seen
|
# data-video-src and data-src are non standard but seen
|
||||||
# several times in the wild
|
# several times in the wild
|
||||||
src = dict_get(s_attr, ('src', 'data-video-src', 'data-src'))
|
src = strip_or_none(dict_get(s_attr, ('src', 'data-video-src', 'data-src')))
|
||||||
if not src:
|
if not src:
|
||||||
continue
|
continue
|
||||||
f = parse_content_type(s_attr.get('type'))
|
f = parse_content_type(s_attr.get('type'))
|
||||||
@@ -2502,8 +2505,8 @@ class InfoExtractor(object):
|
|||||||
if str_or_none(s_attr.get(lbl))
|
if str_or_none(s_attr.get(lbl))
|
||||||
]
|
]
|
||||||
width = int_or_none(s_attr.get('width'))
|
width = int_or_none(s_attr.get('width'))
|
||||||
height = (int_or_none(s_attr.get('height')) or
|
height = (int_or_none(s_attr.get('height'))
|
||||||
int_or_none(s_attr.get('res')))
|
or int_or_none(s_attr.get('res')))
|
||||||
if not width or not height:
|
if not width or not height:
|
||||||
for lbl in labels:
|
for lbl in labels:
|
||||||
resolution = parse_resolution(lbl)
|
resolution = parse_resolution(lbl)
|
||||||
@@ -2531,7 +2534,7 @@ class InfoExtractor(object):
|
|||||||
track_attributes = extract_attributes(track_tag)
|
track_attributes = extract_attributes(track_tag)
|
||||||
kind = track_attributes.get('kind')
|
kind = track_attributes.get('kind')
|
||||||
if not kind or kind in ('subtitles', 'captions'):
|
if not kind or kind in ('subtitles', 'captions'):
|
||||||
src = track_attributes.get('src')
|
src = strip_or_none(track_attributes.get('src'))
|
||||||
if not src:
|
if not src:
|
||||||
continue
|
continue
|
||||||
lang = track_attributes.get('srclang') or track_attributes.get('lang') or track_attributes.get('label')
|
lang = track_attributes.get('srclang') or track_attributes.get('lang') or track_attributes.get('label')
|
||||||
@@ -2815,6 +2818,33 @@ class InfoExtractor(object):
|
|||||||
self._downloader.cookiejar.add_cookie_header(req)
|
self._downloader.cookiejar.add_cookie_header(req)
|
||||||
return compat_cookies.SimpleCookie(req.get_header('Cookie'))
|
return compat_cookies.SimpleCookie(req.get_header('Cookie'))
|
||||||
|
|
||||||
|
def _apply_first_set_cookie_header(self, url_handle, cookie):
|
||||||
|
"""
|
||||||
|
Apply first Set-Cookie header instead of the last. Experimental.
|
||||||
|
|
||||||
|
Some sites (e.g. [1-3]) may serve two cookies under the same name
|
||||||
|
in Set-Cookie header and expect the first (old) one to be set rather
|
||||||
|
than second (new). However, as of RFC6265 the newer one cookie
|
||||||
|
should be set into cookie store what actually happens.
|
||||||
|
We will workaround this issue by resetting the cookie to
|
||||||
|
the first one manually.
|
||||||
|
1. https://new.vk.com/
|
||||||
|
2. https://github.com/ytdl-org/youtube-dl/issues/9841#issuecomment-227871201
|
||||||
|
3. https://learning.oreilly.com/
|
||||||
|
"""
|
||||||
|
for header, cookies in url_handle.headers.items():
|
||||||
|
if header.lower() != 'set-cookie':
|
||||||
|
continue
|
||||||
|
if sys.version_info[0] >= 3:
|
||||||
|
cookies = cookies.encode('iso-8859-1')
|
||||||
|
cookies = cookies.decode('utf-8')
|
||||||
|
cookie_value = re.search(
|
||||||
|
r'%s=(.+?);.*?\b[Dd]omain=(.+?)(?:[,;]|$)' % cookie, cookies)
|
||||||
|
if cookie_value:
|
||||||
|
value, domain = cookie_value.groups()
|
||||||
|
self._set_cookie(domain, cookie, value)
|
||||||
|
break
|
||||||
|
|
||||||
def get_testcases(self, include_onlymatching=False):
|
def get_testcases(self, include_onlymatching=False):
|
||||||
t = getattr(self, '_TEST', None)
|
t = getattr(self, '_TEST', None)
|
||||||
if t:
|
if t:
|
||||||
@@ -2845,8 +2875,8 @@ class InfoExtractor(object):
|
|||||||
return not any_restricted
|
return not any_restricted
|
||||||
|
|
||||||
def extract_subtitles(self, *args, **kwargs):
|
def extract_subtitles(self, *args, **kwargs):
|
||||||
if (self._downloader.params.get('writesubtitles', False) or
|
if (self._downloader.params.get('writesubtitles', False)
|
||||||
self._downloader.params.get('listsubtitles')):
|
or self._downloader.params.get('listsubtitles')):
|
||||||
return self._get_subtitles(*args, **kwargs)
|
return self._get_subtitles(*args, **kwargs)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@@ -2871,8 +2901,8 @@ class InfoExtractor(object):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def extract_automatic_captions(self, *args, **kwargs):
|
def extract_automatic_captions(self, *args, **kwargs):
|
||||||
if (self._downloader.params.get('writeautomaticsub', False) or
|
if (self._downloader.params.get('writeautomaticsub', False)
|
||||||
self._downloader.params.get('listsubtitles')):
|
or self._downloader.params.get('listsubtitles')):
|
||||||
return self._get_automatic_captions(*args, **kwargs)
|
return self._get_automatic_captions(*args, **kwargs)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@@ -2880,9 +2910,9 @@ class InfoExtractor(object):
|
|||||||
raise NotImplementedError('This method must be implemented by subclasses')
|
raise NotImplementedError('This method must be implemented by subclasses')
|
||||||
|
|
||||||
def mark_watched(self, *args, **kwargs):
|
def mark_watched(self, *args, **kwargs):
|
||||||
if (self._downloader.params.get('mark_watched', False) and
|
if (self._downloader.params.get('mark_watched', False)
|
||||||
(self._get_login_info()[0] is not None or
|
and (self._get_login_info()[0] is not None
|
||||||
self._downloader.params.get('cookiefile') is not None)):
|
or self._downloader.params.get('cookiefile') is not None)):
|
||||||
self._mark_watched(*args, **kwargs)
|
self._mark_watched(*args, **kwargs)
|
||||||
|
|
||||||
def _mark_watched(self, *args, **kwargs):
|
def _mark_watched(self, *args, **kwargs):
|
||||||
|
@@ -32,19 +32,19 @@ class CommonMistakesIE(InfoExtractor):
|
|||||||
|
|
||||||
|
|
||||||
class UnicodeBOMIE(InfoExtractor):
|
class UnicodeBOMIE(InfoExtractor):
|
||||||
IE_DESC = False
|
IE_DESC = False
|
||||||
_VALID_URL = r'(?P<bom>\ufeff)(?P<id>.*)$'
|
_VALID_URL = r'(?P<bom>\ufeff)(?P<id>.*)$'
|
||||||
|
|
||||||
# Disable test for python 3.2 since BOM is broken in re in this version
|
# Disable test for python 3.2 since BOM is broken in re in this version
|
||||||
# (see https://github.com/ytdl-org/youtube-dl/issues/9751)
|
# (see https://github.com/ytdl-org/youtube-dl/issues/9751)
|
||||||
_TESTS = [] if (3, 0) < sys.version_info <= (3, 3) else [{
|
_TESTS = [] if (3, 0) < sys.version_info <= (3, 3) else [{
|
||||||
'url': '\ufeffhttp://www.youtube.com/watch?v=BaW_jenozKc',
|
'url': '\ufeffhttp://www.youtube.com/watch?v=BaW_jenozKc',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
real_url = self._match_id(url)
|
real_url = self._match_id(url)
|
||||||
self.report_warning(
|
self.report_warning(
|
||||||
'Your URL starts with a Byte Order Mark (BOM). '
|
'Your URL starts with a Byte Order Mark (BOM). '
|
||||||
'Removing the BOM and looking for "%s" ...' % real_url)
|
'Removing the BOM and looking for "%s" ...' % real_url)
|
||||||
return self.url_result(real_url)
|
return self.url_result(real_url)
|
||||||
|
@@ -1,39 +0,0 @@
|
|||||||
# coding: utf-8
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from .common import InfoExtractor
|
|
||||||
|
|
||||||
|
|
||||||
class CriterionIE(InfoExtractor):
|
|
||||||
_VALID_URL = r'https?://(?:www\.)?criterion\.com/films/(?P<id>[0-9]+)-.+'
|
|
||||||
_TEST = {
|
|
||||||
'url': 'http://www.criterion.com/films/184-le-samourai',
|
|
||||||
'md5': 'bc51beba55685509883a9a7830919ec3',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '184',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Le Samouraï',
|
|
||||||
'description': 'md5:a2b4b116326558149bef81f76dcbb93f',
|
|
||||||
'thumbnail': r're:^https?://.*\.jpg$',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
|
||||||
video_id = self._match_id(url)
|
|
||||||
webpage = self._download_webpage(url, video_id)
|
|
||||||
|
|
||||||
final_url = self._search_regex(
|
|
||||||
r'so\.addVariable\("videoURL", "(.+?)"\)\;', webpage, 'video url')
|
|
||||||
title = self._og_search_title(webpage)
|
|
||||||
description = self._html_search_meta('description', webpage)
|
|
||||||
thumbnail = self._search_regex(
|
|
||||||
r'so\.addVariable\("thumbnailURL", "(.+?)"\)\;',
|
|
||||||
webpage, 'thumbnail url')
|
|
||||||
|
|
||||||
return {
|
|
||||||
'id': video_id,
|
|
||||||
'url': final_url,
|
|
||||||
'title': title,
|
|
||||||
'description': description,
|
|
||||||
'thumbnail': thumbnail,
|
|
||||||
}
|
|
@@ -79,7 +79,7 @@ class CWTVIE(InfoExtractor):
|
|||||||
season = str_or_none(video_data.get('season'))
|
season = str_or_none(video_data.get('season'))
|
||||||
episode = str_or_none(video_data.get('episode'))
|
episode = str_or_none(video_data.get('episode'))
|
||||||
if episode and season:
|
if episode and season:
|
||||||
episode = episode.lstrip(season)
|
episode = episode[len(season):]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'_type': 'url_transparent',
|
'_type': 'url_transparent',
|
||||||
|
@@ -45,8 +45,8 @@ class DailyMailIE(InfoExtractor):
|
|||||||
sources_url = (try_get(
|
sources_url = (try_get(
|
||||||
video_data,
|
video_data,
|
||||||
(lambda x: x['plugins']['sources']['url'],
|
(lambda x: x['plugins']['sources']['url'],
|
||||||
lambda x: x['sources']['url']), compat_str) or
|
lambda x: x['sources']['url']), compat_str)
|
||||||
'http://www.dailymail.co.uk/api/player/%s/video-sources.json' % video_id)
|
or 'http://www.dailymail.co.uk/api/player/%s/video-sources.json' % video_id)
|
||||||
|
|
||||||
video_sources = self._download_json(sources_url, video_id)
|
video_sources = self._download_json(sources_url, video_id)
|
||||||
body = video_sources.get('body')
|
body = video_sources.get('body')
|
||||||
|
@@ -70,8 +70,8 @@ class DctpTvIE(InfoExtractor):
|
|||||||
endpoint = next(
|
endpoint = next(
|
||||||
server['endpoint']
|
server['endpoint']
|
||||||
for server in servers
|
for server in servers
|
||||||
if url_or_none(server.get('endpoint')) and
|
if url_or_none(server.get('endpoint'))
|
||||||
'cloudfront' in server['endpoint'])
|
and 'cloudfront' in server['endpoint'])
|
||||||
else:
|
else:
|
||||||
endpoint = 'rtmpe://s2pqqn4u96e4j8.cloudfront.net/cfx/st/'
|
endpoint = 'rtmpe://s2pqqn4u96e4j8.cloudfront.net/cfx/st/'
|
||||||
|
|
||||||
|
@@ -58,10 +58,17 @@ class DigitallySpeakingIE(InfoExtractor):
|
|||||||
stream_name = xpath_text(a_format, 'streamName', fatal=True)
|
stream_name = xpath_text(a_format, 'streamName', fatal=True)
|
||||||
video_path = re.match(r'mp4\:(?P<path>.*)', stream_name).group('path')
|
video_path = re.match(r'mp4\:(?P<path>.*)', stream_name).group('path')
|
||||||
url = video_root + video_path
|
url = video_root + video_path
|
||||||
vbr = xpath_text(a_format, 'bitrate')
|
bitrate = xpath_text(a_format, 'bitrate')
|
||||||
|
tbr = int_or_none(bitrate)
|
||||||
|
vbr = int_or_none(self._search_regex(
|
||||||
|
r'-(\d+)\.mp4', video_path, 'vbr', default=None))
|
||||||
|
abr = tbr - vbr if tbr and vbr else None
|
||||||
video_formats.append({
|
video_formats.append({
|
||||||
|
'format_id': bitrate,
|
||||||
'url': url,
|
'url': url,
|
||||||
'vbr': int_or_none(vbr),
|
'tbr': tbr,
|
||||||
|
'vbr': vbr,
|
||||||
|
'abr': abr,
|
||||||
})
|
})
|
||||||
return video_formats
|
return video_formats
|
||||||
|
|
||||||
|
@@ -1,266 +0,0 @@
|
|||||||
# coding: utf-8
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import itertools
|
|
||||||
import json
|
|
||||||
|
|
||||||
from .common import InfoExtractor
|
|
||||||
from ..compat import (
|
|
||||||
compat_HTTPError,
|
|
||||||
compat_urlparse,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
|
||||||
clean_html,
|
|
||||||
ExtractorError,
|
|
||||||
int_or_none,
|
|
||||||
parse_age_limit,
|
|
||||||
parse_duration,
|
|
||||||
unified_timestamp,
|
|
||||||
url_or_none,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DramaFeverBaseIE(InfoExtractor):
|
|
||||||
_NETRC_MACHINE = 'dramafever'
|
|
||||||
|
|
||||||
_CONSUMER_SECRET = 'DA59dtVXYLxajktV'
|
|
||||||
|
|
||||||
_consumer_secret = None
|
|
||||||
|
|
||||||
def _get_consumer_secret(self):
|
|
||||||
mainjs = self._download_webpage(
|
|
||||||
'http://www.dramafever.com/static/51afe95/df2014/scripts/main.js',
|
|
||||||
None, 'Downloading main.js', fatal=False)
|
|
||||||
if not mainjs:
|
|
||||||
return self._CONSUMER_SECRET
|
|
||||||
return self._search_regex(
|
|
||||||
r"var\s+cs\s*=\s*'([^']+)'", mainjs,
|
|
||||||
'consumer secret', default=self._CONSUMER_SECRET)
|
|
||||||
|
|
||||||
def _real_initialize(self):
|
|
||||||
self._consumer_secret = self._get_consumer_secret()
|
|
||||||
self._login()
|
|
||||||
|
|
||||||
def _login(self):
|
|
||||||
username, password = self._get_login_info()
|
|
||||||
if username is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
login_form = {
|
|
||||||
'username': username,
|
|
||||||
'password': password,
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = self._download_json(
|
|
||||||
'https://www.dramafever.com/api/users/login', None, 'Logging in',
|
|
||||||
data=json.dumps(login_form).encode('utf-8'), headers={
|
|
||||||
'x-consumer-key': self._consumer_secret,
|
|
||||||
})
|
|
||||||
except ExtractorError as e:
|
|
||||||
if isinstance(e.cause, compat_HTTPError) and e.cause.code in (403, 404):
|
|
||||||
response = self._parse_json(
|
|
||||||
e.cause.read().decode('utf-8'), None)
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
# Successful login
|
|
||||||
if response.get('result') or response.get('guid') or response.get('user_guid'):
|
|
||||||
return
|
|
||||||
|
|
||||||
errors = response.get('errors')
|
|
||||||
if errors and isinstance(errors, list):
|
|
||||||
error = errors[0]
|
|
||||||
message = error.get('message') or error['reason']
|
|
||||||
raise ExtractorError('Unable to login: %s' % message, expected=True)
|
|
||||||
raise ExtractorError('Unable to log in')
|
|
||||||
|
|
||||||
|
|
||||||
class DramaFeverIE(DramaFeverBaseIE):
|
|
||||||
IE_NAME = 'dramafever'
|
|
||||||
_VALID_URL = r'https?://(?:www\.)?dramafever\.com/(?:[^/]+/)?drama/(?P<id>[0-9]+/[0-9]+)(?:/|$)'
|
|
||||||
_TESTS = [{
|
|
||||||
'url': 'https://www.dramafever.com/drama/4274/1/Heirs/',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '4274.1',
|
|
||||||
'ext': 'wvm',
|
|
||||||
'title': 'Heirs - Episode 1',
|
|
||||||
'description': 'md5:362a24ba18209f6276e032a651c50bc2',
|
|
||||||
'thumbnail': r're:^https?://.*\.jpg',
|
|
||||||
'duration': 3783,
|
|
||||||
'timestamp': 1381354993,
|
|
||||||
'upload_date': '20131009',
|
|
||||||
'series': 'Heirs',
|
|
||||||
'season_number': 1,
|
|
||||||
'episode': 'Episode 1',
|
|
||||||
'episode_number': 1,
|
|
||||||
},
|
|
||||||
'params': {
|
|
||||||
# m3u8 download
|
|
||||||
'skip_download': True,
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
'url': 'http://www.dramafever.com/drama/4826/4/Mnet_Asian_Music_Awards_2015/?ap=1',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '4826.4',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'Mnet Asian Music Awards 2015',
|
|
||||||
'description': 'md5:3ff2ee8fedaef86e076791c909cf2e91',
|
|
||||||
'episode': 'Mnet Asian Music Awards 2015 - Part 3',
|
|
||||||
'episode_number': 4,
|
|
||||||
'thumbnail': r're:^https?://.*\.jpg',
|
|
||||||
'timestamp': 1450213200,
|
|
||||||
'upload_date': '20151215',
|
|
||||||
'duration': 5359,
|
|
||||||
},
|
|
||||||
'params': {
|
|
||||||
# m3u8 download
|
|
||||||
'skip_download': True,
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
'url': 'https://www.dramafever.com/zh-cn/drama/4972/15/Doctor_Romantic/',
|
|
||||||
'only_matching': True,
|
|
||||||
}]
|
|
||||||
|
|
||||||
def _call_api(self, path, video_id, note, fatal=False):
|
|
||||||
return self._download_json(
|
|
||||||
'https://www.dramafever.com/api/5/' + path,
|
|
||||||
video_id, note=note, headers={
|
|
||||||
'x-consumer-key': self._consumer_secret,
|
|
||||||
}, fatal=fatal)
|
|
||||||
|
|
||||||
def _get_subtitles(self, video_id):
|
|
||||||
subtitles = {}
|
|
||||||
subs = self._call_api(
|
|
||||||
'video/%s/subtitles/webvtt/' % video_id, video_id,
|
|
||||||
'Downloading subtitles JSON', fatal=False)
|
|
||||||
if not subs or not isinstance(subs, list):
|
|
||||||
return subtitles
|
|
||||||
for sub in subs:
|
|
||||||
if not isinstance(sub, dict):
|
|
||||||
continue
|
|
||||||
sub_url = url_or_none(sub.get('url'))
|
|
||||||
if not sub_url:
|
|
||||||
continue
|
|
||||||
subtitles.setdefault(
|
|
||||||
sub.get('code') or sub.get('language') or 'en', []).append({
|
|
||||||
'url': sub_url
|
|
||||||
})
|
|
||||||
return subtitles
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
|
||||||
video_id = self._match_id(url).replace('/', '.')
|
|
||||||
|
|
||||||
series_id, episode_number = video_id.split('.')
|
|
||||||
|
|
||||||
video = self._call_api(
|
|
||||||
'series/%s/episodes/%s/' % (series_id, episode_number), video_id,
|
|
||||||
'Downloading video JSON')
|
|
||||||
|
|
||||||
formats = []
|
|
||||||
download_assets = video.get('download_assets')
|
|
||||||
if download_assets and isinstance(download_assets, dict):
|
|
||||||
for format_id, format_dict in download_assets.items():
|
|
||||||
if not isinstance(format_dict, dict):
|
|
||||||
continue
|
|
||||||
format_url = url_or_none(format_dict.get('url'))
|
|
||||||
if not format_url:
|
|
||||||
continue
|
|
||||||
formats.append({
|
|
||||||
'url': format_url,
|
|
||||||
'format_id': format_id,
|
|
||||||
'filesize': int_or_none(video.get('filesize')),
|
|
||||||
})
|
|
||||||
|
|
||||||
stream = self._call_api(
|
|
||||||
'video/%s/stream/' % video_id, video_id, 'Downloading stream JSON',
|
|
||||||
fatal=False)
|
|
||||||
if stream:
|
|
||||||
stream_url = stream.get('stream_url')
|
|
||||||
if stream_url:
|
|
||||||
formats.extend(self._extract_m3u8_formats(
|
|
||||||
stream_url, video_id, 'mp4', entry_protocol='m3u8_native',
|
|
||||||
m3u8_id='hls', fatal=False))
|
|
||||||
self._sort_formats(formats)
|
|
||||||
|
|
||||||
title = video.get('title') or 'Episode %s' % episode_number
|
|
||||||
description = video.get('description')
|
|
||||||
thumbnail = video.get('thumbnail')
|
|
||||||
timestamp = unified_timestamp(video.get('release_date'))
|
|
||||||
duration = parse_duration(video.get('duration'))
|
|
||||||
age_limit = parse_age_limit(video.get('tv_rating'))
|
|
||||||
series = video.get('series_title')
|
|
||||||
season_number = int_or_none(video.get('season'))
|
|
||||||
|
|
||||||
if series:
|
|
||||||
title = '%s - %s' % (series, title)
|
|
||||||
|
|
||||||
subtitles = self.extract_subtitles(video_id)
|
|
||||||
|
|
||||||
return {
|
|
||||||
'id': video_id,
|
|
||||||
'title': title,
|
|
||||||
'description': description,
|
|
||||||
'thumbnail': thumbnail,
|
|
||||||
'duration': duration,
|
|
||||||
'timestamp': timestamp,
|
|
||||||
'age_limit': age_limit,
|
|
||||||
'series': series,
|
|
||||||
'season_number': season_number,
|
|
||||||
'episode_number': int_or_none(episode_number),
|
|
||||||
'formats': formats,
|
|
||||||
'subtitles': subtitles,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class DramaFeverSeriesIE(DramaFeverBaseIE):
|
|
||||||
IE_NAME = 'dramafever:series'
|
|
||||||
_VALID_URL = r'https?://(?:www\.)?dramafever\.com/(?:[^/]+/)?drama/(?P<id>[0-9]+)(?:/(?:(?!\d+(?:/|$)).+)?)?$'
|
|
||||||
_TESTS = [{
|
|
||||||
'url': 'http://www.dramafever.com/drama/4512/Cooking_with_Shin/',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '4512',
|
|
||||||
'title': 'Cooking with Shin',
|
|
||||||
'description': 'md5:84a3f26e3cdc3fb7f500211b3593b5c1',
|
|
||||||
},
|
|
||||||
'playlist_count': 4,
|
|
||||||
}, {
|
|
||||||
'url': 'http://www.dramafever.com/drama/124/IRIS/',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '124',
|
|
||||||
'title': 'IRIS',
|
|
||||||
'description': 'md5:b3a30e587cf20c59bd1c01ec0ee1b862',
|
|
||||||
},
|
|
||||||
'playlist_count': 20,
|
|
||||||
}]
|
|
||||||
|
|
||||||
_PAGE_SIZE = 60 # max is 60 (see http://api.drama9.com/#get--api-4-episode-series-)
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
|
||||||
series_id = self._match_id(url)
|
|
||||||
|
|
||||||
series = self._download_json(
|
|
||||||
'http://www.dramafever.com/api/4/series/query/?cs=%s&series_id=%s'
|
|
||||||
% (self._consumer_secret, series_id),
|
|
||||||
series_id, 'Downloading series JSON')['series'][series_id]
|
|
||||||
|
|
||||||
title = clean_html(series['name'])
|
|
||||||
description = clean_html(series.get('description') or series.get('description_short'))
|
|
||||||
|
|
||||||
entries = []
|
|
||||||
for page_num in itertools.count(1):
|
|
||||||
episodes = self._download_json(
|
|
||||||
'http://www.dramafever.com/api/4/episode/series/?cs=%s&series_id=%s&page_size=%d&page_number=%d'
|
|
||||||
% (self._consumer_secret, series_id, self._PAGE_SIZE, page_num),
|
|
||||||
series_id, 'Downloading episodes JSON page #%d' % page_num)
|
|
||||||
for episode in episodes.get('value', []):
|
|
||||||
episode_url = episode.get('episode_url')
|
|
||||||
if not episode_url:
|
|
||||||
continue
|
|
||||||
entries.append(self.url_result(
|
|
||||||
compat_urlparse.urljoin(url, episode_url),
|
|
||||||
'DramaFever', episode.get('guid')))
|
|
||||||
if page_num == episodes['num_pages']:
|
|
||||||
break
|
|
||||||
|
|
||||||
return self.playlist_result(entries, series_id, title, description)
|
|
@@ -10,16 +10,16 @@ from ..utils import (
|
|||||||
int_or_none,
|
int_or_none,
|
||||||
js_to_json,
|
js_to_json,
|
||||||
mimetype2ext,
|
mimetype2ext,
|
||||||
|
try_get,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
|
parse_iso8601,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DVTVIE(InfoExtractor):
|
class DVTVIE(InfoExtractor):
|
||||||
IE_NAME = 'dvtv'
|
IE_NAME = 'dvtv'
|
||||||
IE_DESC = 'http://video.aktualne.cz/'
|
IE_DESC = 'http://video.aktualne.cz/'
|
||||||
|
|
||||||
_VALID_URL = r'https?://video\.aktualne\.cz/(?:[^/]+/)+r~(?P<id>[0-9a-f]{32})'
|
_VALID_URL = r'https?://video\.aktualne\.cz/(?:[^/]+/)+r~(?P<id>[0-9a-f]{32})'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://video.aktualne.cz/dvtv/vondra-o-ceskem-stoleti-pri-pohledu-na-havla-mi-bylo-trapne/r~e5efe9ca855511e4833a0025900fea04/',
|
'url': 'http://video.aktualne.cz/dvtv/vondra-o-ceskem-stoleti-pri-pohledu-na-havla-mi-bylo-trapne/r~e5efe9ca855511e4833a0025900fea04/',
|
||||||
'md5': '67cb83e4a955d36e1b5d31993134a0c2',
|
'md5': '67cb83e4a955d36e1b5d31993134a0c2',
|
||||||
@@ -28,11 +28,13 @@ class DVTVIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Vondra o Českém století: Při pohledu na Havla mi bylo trapně',
|
'title': 'Vondra o Českém století: Při pohledu na Havla mi bylo trapně',
|
||||||
'duration': 1484,
|
'duration': 1484,
|
||||||
|
'upload_date': '20141217',
|
||||||
|
'timestamp': 1418792400,
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://video.aktualne.cz/dvtv/dvtv-16-12-2014-utok-talibanu-boj-o-kliniku-uprchlici/r~973eb3bc854e11e498be002590604f2e/',
|
'url': 'http://video.aktualne.cz/dvtv/dvtv-16-12-2014-utok-talibanu-boj-o-kliniku-uprchlici/r~973eb3bc854e11e498be002590604f2e/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'title': r're:^DVTV 16\. 12\. 2014: útok Talibanu, boj o kliniku, uprchlíci',
|
'title': r'DVTV 16. 12. 2014: útok Talibanu, boj o kliniku, uprchlíci',
|
||||||
'id': '973eb3bc854e11e498be002590604f2e',
|
'id': '973eb3bc854e11e498be002590604f2e',
|
||||||
},
|
},
|
||||||
'playlist': [{
|
'playlist': [{
|
||||||
@@ -84,6 +86,8 @@ class DVTVIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Zeman si jen léčí mindráky, Sobotku nenávidí a Babiš se mu teď hodí, tvrdí Kmenta',
|
'title': 'Zeman si jen léčí mindráky, Sobotku nenávidí a Babiš se mu teď hodí, tvrdí Kmenta',
|
||||||
'duration': 1103,
|
'duration': 1103,
|
||||||
|
'upload_date': '20170511',
|
||||||
|
'timestamp': 1494514200,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
@@ -91,43 +95,59 @@ class DVTVIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://video.aktualne.cz/v-cechach-poprve-zazni-zelenkova-zrestaurovana-mse/r~45b4b00483ec11e4883b002590604f2e/',
|
'url': 'http://video.aktualne.cz/v-cechach-poprve-zazni-zelenkova-zrestaurovana-mse/r~45b4b00483ec11e4883b002590604f2e/',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# Test live stream video (liveStarter) parsing
|
||||||
|
'url': 'https://video.aktualne.cz/dvtv/zive-mistryne-sveta-eva-samkova-po-navratu-ze-sampionatu/r~182654c2288811e990fd0cc47ab5f122/',
|
||||||
|
'md5': '2e552e483f2414851ca50467054f9d5d',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '8d116360288011e98c840cc47ab5f122',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Živě: Mistryně světa Eva Samková po návratu ze šampionátu',
|
||||||
|
'upload_date': '20190204',
|
||||||
|
'timestamp': 1549289591,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# Video content is no longer available
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _parse_video_metadata(self, js, video_id, live_js=None):
|
def _parse_video_metadata(self, js, video_id, timestamp):
|
||||||
data = self._parse_json(js, video_id, transform_source=js_to_json)
|
data = self._parse_json(js, video_id, transform_source=js_to_json)
|
||||||
if live_js:
|
|
||||||
data.update(self._parse_json(
|
|
||||||
live_js, video_id, transform_source=js_to_json))
|
|
||||||
|
|
||||||
title = unescapeHTML(data['title'])
|
title = unescapeHTML(data['title'])
|
||||||
|
|
||||||
|
live_starter = try_get(data, lambda x: x['plugins']['liveStarter'], dict)
|
||||||
|
if live_starter:
|
||||||
|
data.update(live_starter)
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for video in data['sources']:
|
for tracks in data.get('tracks', {}).values():
|
||||||
video_url = video.get('file')
|
for video in tracks:
|
||||||
if not video_url:
|
video_url = video.get('src')
|
||||||
continue
|
if not video_url:
|
||||||
video_type = video.get('type')
|
continue
|
||||||
ext = determine_ext(video_url, mimetype2ext(video_type))
|
video_type = video.get('type')
|
||||||
if video_type == 'application/vnd.apple.mpegurl' or ext == 'm3u8':
|
ext = determine_ext(video_url, mimetype2ext(video_type))
|
||||||
formats.extend(self._extract_m3u8_formats(
|
if video_type == 'application/vnd.apple.mpegurl' or ext == 'm3u8':
|
||||||
video_url, video_id, 'mp4', entry_protocol='m3u8_native',
|
formats.extend(self._extract_m3u8_formats(
|
||||||
m3u8_id='hls', fatal=False))
|
video_url, video_id, 'mp4', entry_protocol='m3u8_native',
|
||||||
elif video_type == 'application/dash+xml' or ext == 'mpd':
|
m3u8_id='hls', fatal=False))
|
||||||
formats.extend(self._extract_mpd_formats(
|
elif video_type == 'application/dash+xml' or ext == 'mpd':
|
||||||
video_url, video_id, mpd_id='dash', fatal=False))
|
formats.extend(self._extract_mpd_formats(
|
||||||
else:
|
video_url, video_id, mpd_id='dash', fatal=False))
|
||||||
label = video.get('label')
|
else:
|
||||||
height = self._search_regex(
|
label = video.get('label')
|
||||||
r'^(\d+)[pP]', label or '', 'height', default=None)
|
height = self._search_regex(
|
||||||
format_id = ['http']
|
r'^(\d+)[pP]', label or '', 'height', default=None)
|
||||||
for f in (ext, label):
|
format_id = ['http']
|
||||||
if f:
|
for f in (ext, label):
|
||||||
format_id.append(f)
|
if f:
|
||||||
formats.append({
|
format_id.append(f)
|
||||||
'url': video_url,
|
formats.append({
|
||||||
'format_id': '-'.join(format_id),
|
'url': video_url,
|
||||||
'height': int_or_none(height),
|
'format_id': '-'.join(format_id),
|
||||||
})
|
'height': int_or_none(height),
|
||||||
|
})
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -136,41 +156,29 @@ class DVTVIE(InfoExtractor):
|
|||||||
'description': data.get('description'),
|
'description': data.get('description'),
|
||||||
'thumbnail': data.get('image'),
|
'thumbnail': data.get('image'),
|
||||||
'duration': int_or_none(data.get('duration')),
|
'duration': int_or_none(data.get('duration')),
|
||||||
'timestamp': int_or_none(data.get('pubtime')),
|
'timestamp': int_or_none(timestamp),
|
||||||
'formats': formats
|
'formats': formats
|
||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
timestamp = parse_iso8601(self._html_search_meta(
|
||||||
|
'article:published_time', webpage, 'published time', default=None))
|
||||||
|
|
||||||
# live content
|
items = re.findall(r'(?s)playlist\.push\(({.+?})\);', webpage)
|
||||||
live_item = self._search_regex(
|
|
||||||
r'(?s)embedData[0-9a-f]{32}\.asset\.liveStarter\s*=\s*(\{.+?\});',
|
|
||||||
webpage, 'video', default=None)
|
|
||||||
|
|
||||||
# single video
|
|
||||||
item = self._search_regex(
|
|
||||||
r'(?s)embedData[0-9a-f]{32}\[["\']asset["\']\]\s*=\s*(\{.+?\});',
|
|
||||||
webpage, 'video', default=None)
|
|
||||||
|
|
||||||
if item:
|
|
||||||
return self._parse_video_metadata(item, video_id, live_item)
|
|
||||||
|
|
||||||
# playlist
|
|
||||||
items = re.findall(
|
|
||||||
r"(?s)BBX\.context\.assets\['[0-9a-f]{32}'\]\.push\(({.+?})\);",
|
|
||||||
webpage)
|
|
||||||
if not items:
|
|
||||||
items = re.findall(r'(?s)var\s+asset\s*=\s*({.+?});\n', webpage)
|
|
||||||
|
|
||||||
if items:
|
if items:
|
||||||
return {
|
return self.playlist_result(
|
||||||
'_type': 'playlist',
|
[self._parse_video_metadata(i, video_id, timestamp) for i in items],
|
||||||
'id': video_id,
|
video_id, self._html_search_meta('twitter:title', webpage))
|
||||||
'title': self._og_search_title(webpage),
|
|
||||||
'entries': [self._parse_video_metadata(i, video_id) for i in items]
|
item = self._search_regex(
|
||||||
}
|
r'(?s)BBXPlayer\.setup\((.+?)\);',
|
||||||
|
webpage, 'video', default=None)
|
||||||
|
if item:
|
||||||
|
# remove function calls (ex. htmldeentitize)
|
||||||
|
# TODO this should be fixed in a general way in the js_to_json
|
||||||
|
item = re.sub(r'\w+?\((.+)\)', r'\1', item)
|
||||||
|
return self._parse_video_metadata(item, video_id, timestamp)
|
||||||
|
|
||||||
raise ExtractorError('Could not find neither video nor playlist')
|
raise ExtractorError('Could not find neither video nor playlist')
|
||||||
|
@@ -82,8 +82,8 @@ class ExpressenIE(InfoExtractor):
|
|||||||
title = info.get('titleRaw') or data['title']
|
title = info.get('titleRaw') or data['title']
|
||||||
description = info.get('descriptionRaw')
|
description = info.get('descriptionRaw')
|
||||||
thumbnail = info.get('socialMediaImage') or data.get('image')
|
thumbnail = info.get('socialMediaImage') or data.get('image')
|
||||||
duration = int_or_none(info.get('videoTotalSecondsDuration') or
|
duration = int_or_none(info.get('videoTotalSecondsDuration')
|
||||||
data.get('totalSecondsDuration'))
|
or data.get('totalSecondsDuration'))
|
||||||
timestamp = unified_timestamp(info.get('publishDate'))
|
timestamp = unified_timestamp(info.get('publishDate'))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -20,6 +20,7 @@ from .acast import (
|
|||||||
)
|
)
|
||||||
from .addanime import AddAnimeIE
|
from .addanime import AddAnimeIE
|
||||||
from .adn import ADNIE
|
from .adn import ADNIE
|
||||||
|
from .adobeconnect import AdobeConnectIE
|
||||||
from .adobetv import (
|
from .adobetv import (
|
||||||
AdobeTVIE,
|
AdobeTVIE,
|
||||||
AdobeTVShowIE,
|
AdobeTVShowIE,
|
||||||
@@ -106,6 +107,7 @@ from .behindkink import BehindKinkIE
|
|||||||
from .bellmedia import BellMediaIE
|
from .bellmedia import BellMediaIE
|
||||||
from .beatport import BeatportIE
|
from .beatport import BeatportIE
|
||||||
from .bet import BetIE
|
from .bet import BetIE
|
||||||
|
from .bfi import BFIPlayerIE
|
||||||
from .bigflix import BigflixIE
|
from .bigflix import BigflixIE
|
||||||
from .bild import BildIE
|
from .bild import BildIE
|
||||||
from .bilibili import (
|
from .bilibili import (
|
||||||
@@ -171,11 +173,15 @@ from .cbs import CBSIE
|
|||||||
from .cbslocal import CBSLocalIE
|
from .cbslocal import CBSLocalIE
|
||||||
from .cbsinteractive import CBSInteractiveIE
|
from .cbsinteractive import CBSInteractiveIE
|
||||||
from .cbsnews import (
|
from .cbsnews import (
|
||||||
|
CBSNewsEmbedIE,
|
||||||
CBSNewsIE,
|
CBSNewsIE,
|
||||||
CBSNewsLiveVideoIE,
|
CBSNewsLiveVideoIE,
|
||||||
)
|
)
|
||||||
from .cbssports import CBSSportsIE
|
from .cbssports import CBSSportsIE
|
||||||
from .ccc import CCCIE
|
from .ccc import (
|
||||||
|
CCCIE,
|
||||||
|
CCCPlaylistIE,
|
||||||
|
)
|
||||||
from .ccma import CCMAIE
|
from .ccma import CCMAIE
|
||||||
from .cctv import CCTVIE
|
from .cctv import CCTVIE
|
||||||
from .cda import CDAIE
|
from .cda import CDAIE
|
||||||
@@ -192,6 +198,7 @@ from .chirbit import (
|
|||||||
ChirbitProfileIE,
|
ChirbitProfileIE,
|
||||||
)
|
)
|
||||||
from .cinchcast import CinchcastIE
|
from .cinchcast import CinchcastIE
|
||||||
|
from .cinemax import CinemaxIE
|
||||||
from .ciscolive import (
|
from .ciscolive import (
|
||||||
CiscoLiveSessionIE,
|
CiscoLiveSessionIE,
|
||||||
CiscoLiveSearchIE,
|
CiscoLiveSearchIE,
|
||||||
@@ -234,7 +241,6 @@ from .condenast import CondeNastIE
|
|||||||
from .corus import CorusIE
|
from .corus import CorusIE
|
||||||
from .cracked import CrackedIE
|
from .cracked import CrackedIE
|
||||||
from .crackle import CrackleIE
|
from .crackle import CrackleIE
|
||||||
from .criterion import CriterionIE
|
|
||||||
from .crooksandliars import CrooksAndLiarsIE
|
from .crooksandliars import CrooksAndLiarsIE
|
||||||
from .crunchyroll import (
|
from .crunchyroll import (
|
||||||
CrunchyrollIE,
|
CrunchyrollIE,
|
||||||
@@ -281,10 +287,6 @@ from .dplay import (
|
|||||||
DPlayIE,
|
DPlayIE,
|
||||||
DPlayItIE,
|
DPlayItIE,
|
||||||
)
|
)
|
||||||
from .dramafever import (
|
|
||||||
DramaFeverIE,
|
|
||||||
DramaFeverSeriesIE,
|
|
||||||
)
|
|
||||||
from .dreisat import DreiSatIE
|
from .dreisat import DreiSatIE
|
||||||
from .drbonanza import DRBonanzaIE
|
from .drbonanza import DRBonanzaIE
|
||||||
from .drtuber import DrTuberIE
|
from .drtuber import DrTuberIE
|
||||||
@@ -440,10 +442,7 @@ from .goshgay import GoshgayIE
|
|||||||
from .gputechconf import GPUTechConfIE
|
from .gputechconf import GPUTechConfIE
|
||||||
from .groupon import GrouponIE
|
from .groupon import GrouponIE
|
||||||
from .hark import HarkIE
|
from .hark import HarkIE
|
||||||
from .hbo import (
|
from .hbo import HBOIE
|
||||||
HBOIE,
|
|
||||||
HBOEpisodeIE,
|
|
||||||
)
|
|
||||||
from .hearthisat import HearThisAtIE
|
from .hearthisat import HearThisAtIE
|
||||||
from .heise import HeiseIE
|
from .heise import HeiseIE
|
||||||
from .hellporno import HellPornoIE
|
from .hellporno import HellPornoIE
|
||||||
@@ -632,7 +631,11 @@ from .massengeschmacktv import MassengeschmackTVIE
|
|||||||
from .matchtv import MatchTVIE
|
from .matchtv import MatchTVIE
|
||||||
from .mdr import MDRIE
|
from .mdr import MDRIE
|
||||||
from .mediaset import MediasetIE
|
from .mediaset import MediasetIE
|
||||||
from .mediasite import MediasiteIE
|
from .mediasite import (
|
||||||
|
MediasiteIE,
|
||||||
|
MediasiteCatalogIE,
|
||||||
|
MediasiteNamedCatalogIE,
|
||||||
|
)
|
||||||
from .medici import MediciIE
|
from .medici import MediciIE
|
||||||
from .megaphone import MegaphoneIE
|
from .megaphone import MegaphoneIE
|
||||||
from .meipai import MeipaiIE
|
from .meipai import MeipaiIE
|
||||||
@@ -769,13 +772,6 @@ from .nova import (
|
|||||||
NovaEmbedIE,
|
NovaEmbedIE,
|
||||||
NovaIE,
|
NovaIE,
|
||||||
)
|
)
|
||||||
from .novamov import (
|
|
||||||
AuroraVidIE,
|
|
||||||
CloudTimeIE,
|
|
||||||
NowVideoIE,
|
|
||||||
VideoWeedIE,
|
|
||||||
WholeCloudIE,
|
|
||||||
)
|
|
||||||
from .nowness import (
|
from .nowness import (
|
||||||
NownessIE,
|
NownessIE,
|
||||||
NownessPlaylistIE,
|
NownessPlaylistIE,
|
||||||
@@ -805,6 +801,8 @@ from .nrk import (
|
|||||||
NRKTVSeasonIE,
|
NRKTVSeasonIE,
|
||||||
NRKTVSeriesIE,
|
NRKTVSeriesIE,
|
||||||
)
|
)
|
||||||
|
from .nrl import NRLTVIE
|
||||||
|
from .ntvcojp import NTVCoJpCUIE
|
||||||
from .ntvde import NTVDeIE
|
from .ntvde import NTVDeIE
|
||||||
from .ntvru import NTVRuIE
|
from .ntvru import NTVRuIE
|
||||||
from .nytimes import (
|
from .nytimes import (
|
||||||
@@ -828,7 +826,10 @@ from .ooyala import (
|
|||||||
OoyalaIE,
|
OoyalaIE,
|
||||||
OoyalaExternalIE,
|
OoyalaExternalIE,
|
||||||
)
|
)
|
||||||
from .openload import OpenloadIE
|
from .openload import (
|
||||||
|
OpenloadIE,
|
||||||
|
VerystreamIE,
|
||||||
|
)
|
||||||
from .ora import OraTVIE
|
from .ora import OraTVIE
|
||||||
from .orf import (
|
from .orf import (
|
||||||
ORFTVthekIE,
|
ORFTVthekIE,
|
||||||
@@ -865,6 +866,10 @@ from .picarto import (
|
|||||||
from .piksel import PikselIE
|
from .piksel import PikselIE
|
||||||
from .pinkbike import PinkbikeIE
|
from .pinkbike import PinkbikeIE
|
||||||
from .pladform import PladformIE
|
from .pladform import PladformIE
|
||||||
|
from .platzi import (
|
||||||
|
PlatziIE,
|
||||||
|
PlatziCourseIE,
|
||||||
|
)
|
||||||
from .playfm import PlayFMIE
|
from .playfm import PlayFMIE
|
||||||
from .playplustv import PlayPlusTVIE
|
from .playplustv import PlayPlusTVIE
|
||||||
from .plays import PlaysTVIE
|
from .plays import PlaysTVIE
|
||||||
@@ -884,7 +889,6 @@ from .polskieradio import (
|
|||||||
from .popcorntv import PopcornTVIE
|
from .popcorntv import PopcornTVIE
|
||||||
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,
|
||||||
@@ -934,7 +938,10 @@ from .raywenderlich import (
|
|||||||
)
|
)
|
||||||
from .rbmaradio import RBMARadioIE
|
from .rbmaradio import RBMARadioIE
|
||||||
from .rds import RDSIE
|
from .rds import RDSIE
|
||||||
from .redbulltv import RedBullTVIE
|
from .redbulltv import (
|
||||||
|
RedBullTVIE,
|
||||||
|
RedBullTVRrnContentIE,
|
||||||
|
)
|
||||||
from .reddit import (
|
from .reddit import (
|
||||||
RedditIE,
|
RedditIE,
|
||||||
RedditRIE,
|
RedditRIE,
|
||||||
@@ -1024,7 +1031,10 @@ from .skynewsarabia import (
|
|||||||
SkyNewsArabiaIE,
|
SkyNewsArabiaIE,
|
||||||
SkyNewsArabiaArticleIE,
|
SkyNewsArabiaArticleIE,
|
||||||
)
|
)
|
||||||
from .skysports import SkySportsIE
|
from .sky import (
|
||||||
|
SkyNewsIE,
|
||||||
|
SkySportsIE,
|
||||||
|
)
|
||||||
from .slideshare import SlideshareIE
|
from .slideshare import SlideshareIE
|
||||||
from .slideslive import SlidesLiveIE
|
from .slideslive import SlidesLiveIE
|
||||||
from .slutload import SlutloadIE
|
from .slutload import SlutloadIE
|
||||||
@@ -1086,7 +1096,12 @@ from .streamcloud import StreamcloudIE
|
|||||||
from .streamcz import StreamCZIE
|
from .streamcz import StreamCZIE
|
||||||
from .streetvoice import StreetVoiceIE
|
from .streetvoice import StreetVoiceIE
|
||||||
from .stretchinternet import StretchInternetIE
|
from .stretchinternet import StretchInternetIE
|
||||||
|
from .stv import STVPlayerIE
|
||||||
from .sunporno import SunPornoIE
|
from .sunporno import SunPornoIE
|
||||||
|
from .sverigesradio import (
|
||||||
|
SverigesRadioEpisodeIE,
|
||||||
|
SverigesRadioPublicationIE,
|
||||||
|
)
|
||||||
from .svt import (
|
from .svt import (
|
||||||
SVTIE,
|
SVTIE,
|
||||||
SVTPageIE,
|
SVTPageIE,
|
||||||
@@ -1114,6 +1129,7 @@ from .teachertube import (
|
|||||||
)
|
)
|
||||||
from .teachingchannel import TeachingChannelIE
|
from .teachingchannel import TeachingChannelIE
|
||||||
from .teamcoco import TeamcocoIE
|
from .teamcoco import TeamcocoIE
|
||||||
|
from .teamtreehouse import TeamTreeHouseIE
|
||||||
from .techtalks import TechTalksIE
|
from .techtalks import TechTalksIE
|
||||||
from .ted import TEDIE
|
from .ted import TEDIE
|
||||||
from .tele5 import Tele5IE
|
from .tele5 import Tele5IE
|
||||||
@@ -1407,10 +1423,6 @@ from .weiqitv import WeiqiTVIE
|
|||||||
from .wimp import WimpIE
|
from .wimp import WimpIE
|
||||||
from .wistia import WistiaIE
|
from .wistia import WistiaIE
|
||||||
from .worldstarhiphop import WorldStarHipHopIE
|
from .worldstarhiphop import WorldStarHipHopIE
|
||||||
from .wrzuta import (
|
|
||||||
WrzutaIE,
|
|
||||||
WrzutaPlaylistIE,
|
|
||||||
)
|
|
||||||
from .wsj import (
|
from .wsj import (
|
||||||
WSJIE,
|
WSJIE,
|
||||||
WSJArticleIE,
|
WSJArticleIE,
|
||||||
@@ -1443,6 +1455,8 @@ from .xxxymovies import XXXYMoviesIE
|
|||||||
from .yahoo import (
|
from .yahoo import (
|
||||||
YahooIE,
|
YahooIE,
|
||||||
YahooSearchIE,
|
YahooSearchIE,
|
||||||
|
YahooGyaOPlayerIE,
|
||||||
|
YahooGyaOIE,
|
||||||
)
|
)
|
||||||
from .yandexdisk import YandexDiskIE
|
from .yandexdisk import YandexDiskIE
|
||||||
from .yandexmusic import (
|
from .yandexmusic import (
|
||||||
|
@@ -22,8 +22,6 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class FourTubeBaseIE(InfoExtractor):
|
class FourTubeBaseIE(InfoExtractor):
|
||||||
_TKN_HOST = 'tkn.kodicdn.com'
|
|
||||||
|
|
||||||
def _extract_formats(self, url, video_id, media_id, sources):
|
def _extract_formats(self, url, video_id, media_id, sources):
|
||||||
token_url = 'https://%s/%s/desktop/%s' % (
|
token_url = 'https://%s/%s/desktop/%s' % (
|
||||||
self._TKN_HOST, media_id, '+'.join(sources))
|
self._TKN_HOST, media_id, '+'.join(sources))
|
||||||
@@ -120,6 +118,7 @@ class FourTubeIE(FourTubeBaseIE):
|
|||||||
IE_NAME = '4tube'
|
IE_NAME = '4tube'
|
||||||
_VALID_URL = r'https?://(?:(?P<kind>www|m)\.)?4tube\.com/(?:videos|embed)/(?P<id>\d+)(?:/(?P<display_id>[^/?#&]+))?'
|
_VALID_URL = r'https?://(?:(?P<kind>www|m)\.)?4tube\.com/(?:videos|embed)/(?P<id>\d+)(?:/(?P<display_id>[^/?#&]+))?'
|
||||||
_URL_TEMPLATE = 'https://www.4tube.com/videos/%s/video'
|
_URL_TEMPLATE = 'https://www.4tube.com/videos/%s/video'
|
||||||
|
_TKN_HOST = 'token.4tube.com'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.4tube.com/videos/209733/hot-babe-holly-michaels-gets-her-ass-stuffed-by-black',
|
'url': 'http://www.4tube.com/videos/209733/hot-babe-holly-michaels-gets-her-ass-stuffed-by-black',
|
||||||
'md5': '6516c8ac63b03de06bc8eac14362db4f',
|
'md5': '6516c8ac63b03de06bc8eac14362db4f',
|
||||||
@@ -149,6 +148,7 @@ class FourTubeIE(FourTubeBaseIE):
|
|||||||
class FuxIE(FourTubeBaseIE):
|
class FuxIE(FourTubeBaseIE):
|
||||||
_VALID_URL = r'https?://(?:(?P<kind>www|m)\.)?fux\.com/(?:video|embed)/(?P<id>\d+)(?:/(?P<display_id>[^/?#&]+))?'
|
_VALID_URL = r'https?://(?:(?P<kind>www|m)\.)?fux\.com/(?:video|embed)/(?P<id>\d+)(?:/(?P<display_id>[^/?#&]+))?'
|
||||||
_URL_TEMPLATE = 'https://www.fux.com/video/%s/video'
|
_URL_TEMPLATE = 'https://www.fux.com/video/%s/video'
|
||||||
|
_TKN_HOST = 'token.fux.com'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'https://www.fux.com/video/195359/awesome-fucking-kitchen-ends-cum-swallow',
|
'url': 'https://www.fux.com/video/195359/awesome-fucking-kitchen-ends-cum-swallow',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@@ -280,6 +280,7 @@ class PornTubeIE(FourTubeBaseIE):
|
|||||||
class PornerBrosIE(FourTubeBaseIE):
|
class PornerBrosIE(FourTubeBaseIE):
|
||||||
_VALID_URL = r'https?://(?:(?P<kind>www|m)\.)?pornerbros\.com/(?:videos/(?P<display_id>[^/]+)_|embed/)(?P<id>\d+)'
|
_VALID_URL = r'https?://(?:(?P<kind>www|m)\.)?pornerbros\.com/(?:videos/(?P<display_id>[^/]+)_|embed/)(?P<id>\d+)'
|
||||||
_URL_TEMPLATE = 'https://www.pornerbros.com/videos/video_%s'
|
_URL_TEMPLATE = 'https://www.pornerbros.com/videos/video_%s'
|
||||||
|
_TKN_HOST = 'token.pornerbros.com'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'https://www.pornerbros.com/videos/skinny-brunette-takes-big-cock-down-her-anal-hole_181369',
|
'url': 'https://www.pornerbros.com/videos/skinny-brunette-takes-big-cock-down-her-anal-hole_181369',
|
||||||
'md5': '6516c8ac63b03de06bc8eac14362db4f',
|
'md5': '6516c8ac63b03de06bc8eac14362db4f',
|
||||||
|
@@ -66,7 +66,7 @@ class FOXIE(AdobePassIE):
|
|||||||
'https://api2.fox.com/v2.0/' + path,
|
'https://api2.fox.com/v2.0/' + path,
|
||||||
video_id, data=data, headers=headers)
|
video_id, data=data, headers=headers)
|
||||||
except ExtractorError as e:
|
except ExtractorError as e:
|
||||||
if isinstance(e.cause, compat_HTTPError) and e.cause.status == 403:
|
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
|
||||||
entitlement_issues = self._parse_json(
|
entitlement_issues = self._parse_json(
|
||||||
e.cause.read().decode(), video_id)['entitlementIssues']
|
e.cause.read().decode(), video_id)['entitlementIssues']
|
||||||
for e in entitlement_issues:
|
for e in entitlement_issues:
|
||||||
@@ -100,7 +100,7 @@ class FOXIE(AdobePassIE):
|
|||||||
try:
|
try:
|
||||||
m3u8_url = self._download_json(release_url, video_id)['playURL']
|
m3u8_url = self._download_json(release_url, video_id)['playURL']
|
||||||
except ExtractorError as e:
|
except ExtractorError as e:
|
||||||
if isinstance(e.cause, compat_HTTPError) and e.cause.status == 403:
|
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
|
||||||
error = self._parse_json(e.cause.read().decode(), video_id)
|
error = self._parse_json(e.cause.read().decode(), video_id)
|
||||||
if error.get('exception') == 'GeoLocationBlocked':
|
if error.get('exception') == 'GeoLocationBlocked':
|
||||||
self.raise_geo_restricted(countries=['US'])
|
self.raise_geo_restricted(countries=['US'])
|
||||||
|
@@ -371,12 +371,13 @@ class FranceTVInfoIE(FranceTVBaseInfoExtractor):
|
|||||||
self.url_result(dailymotion_url, DailymotionIE.ie_key())
|
self.url_result(dailymotion_url, DailymotionIE.ie_key())
|
||||||
for dailymotion_url in dailymotion_urls])
|
for dailymotion_url in dailymotion_urls])
|
||||||
|
|
||||||
video_id, catalogue = self._search_regex(
|
video_id = self._search_regex(
|
||||||
(r'id-video=([^@]+@[^"]+)',
|
(r'player\.load[^;]+src:\s*["\']([^"\']+)',
|
||||||
|
r'id-video=([^@]+@[^"]+)',
|
||||||
r'<a[^>]+href="(?:https?:)?//videos\.francetv\.fr/video/([^@]+@[^"]+)"'),
|
r'<a[^>]+href="(?:https?:)?//videos\.francetv\.fr/video/([^@]+@[^"]+)"'),
|
||||||
webpage, 'video id').split('@')
|
webpage, 'video id')
|
||||||
|
|
||||||
return self._make_url_result(video_id, catalogue)
|
return self._make_url_result(video_id)
|
||||||
|
|
||||||
|
|
||||||
class FranceTVInfoSportIE(FranceTVBaseInfoExtractor):
|
class FranceTVInfoSportIE(FranceTVBaseInfoExtractor):
|
||||||
|
@@ -94,8 +94,8 @@ class FrontendMastersPageBaseIE(FrontendMastersBaseIE):
|
|||||||
chapter_number = None
|
chapter_number = None
|
||||||
index = lesson.get('index')
|
index = lesson.get('index')
|
||||||
element_index = lesson.get('elementIndex')
|
element_index = lesson.get('elementIndex')
|
||||||
if (isinstance(index, int) and isinstance(element_index, int) and
|
if (isinstance(index, int) and isinstance(element_index, int)
|
||||||
index < element_index):
|
and index < element_index):
|
||||||
chapter_number = element_index - index
|
chapter_number = element_index - index
|
||||||
chapter = (chapters[chapter_number - 1]
|
chapter = (chapters[chapter_number - 1]
|
||||||
if chapter_number - 1 < len(chapters) else None)
|
if chapter_number - 1 < len(chapters) else None)
|
||||||
|
@@ -4,12 +4,17 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_str
|
from ..compat import (
|
||||||
|
compat_str,
|
||||||
|
compat_urllib_parse_unquote,
|
||||||
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
ExtractorError,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
str_or_none,
|
str_or_none,
|
||||||
strip_or_none,
|
strip_or_none,
|
||||||
try_get,
|
try_get,
|
||||||
|
urlencode_postdata,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -46,6 +51,29 @@ class GaiaIE(InfoExtractor):
|
|||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
}]
|
}]
|
||||||
|
_NETRC_MACHINE = 'gaia'
|
||||||
|
_jwt = None
|
||||||
|
|
||||||
|
def _real_initialize(self):
|
||||||
|
auth = self._get_cookies('https://www.gaia.com/').get('auth')
|
||||||
|
if auth:
|
||||||
|
auth = self._parse_json(
|
||||||
|
compat_urllib_parse_unquote(auth.value),
|
||||||
|
None, fatal=False)
|
||||||
|
if not auth:
|
||||||
|
username, password = self._get_login_info()
|
||||||
|
if username is None:
|
||||||
|
return
|
||||||
|
auth = self._download_json(
|
||||||
|
'https://auth.gaia.com/v1/login',
|
||||||
|
None, data=urlencode_postdata({
|
||||||
|
'username': username,
|
||||||
|
'password': password
|
||||||
|
}))
|
||||||
|
if auth.get('success') is False:
|
||||||
|
raise ExtractorError(', '.join(auth['messages']), expected=True)
|
||||||
|
if auth:
|
||||||
|
self._jwt = auth.get('jwt')
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
display_id, vtype = re.search(self._VALID_URL, url).groups()
|
display_id, vtype = re.search(self._VALID_URL, url).groups()
|
||||||
@@ -59,8 +87,12 @@ class GaiaIE(InfoExtractor):
|
|||||||
media_id = compat_str(vdata['nid'])
|
media_id = compat_str(vdata['nid'])
|
||||||
title = node['title']
|
title = node['title']
|
||||||
|
|
||||||
|
headers = None
|
||||||
|
if self._jwt:
|
||||||
|
headers = {'Authorization': 'Bearer ' + self._jwt}
|
||||||
media = self._download_json(
|
media = self._download_json(
|
||||||
'https://brooklyn.gaia.com/media/' + media_id, media_id)
|
'https://brooklyn.gaia.com/media/' + media_id,
|
||||||
|
media_id, headers=headers)
|
||||||
formats = self._extract_m3u8_formats(
|
formats = self._extract_m3u8_formats(
|
||||||
media['mediaUrls']['bcHLS'], media_id, 'mp4')
|
media['mediaUrls']['bcHLS'], media_id, 'mp4')
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
@@ -3,22 +3,24 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from .kaltura import KalturaIE
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
HEADRequest,
|
HEADRequest,
|
||||||
sanitized_Request,
|
sanitized_Request,
|
||||||
|
smuggle_url,
|
||||||
urlencode_postdata,
|
urlencode_postdata,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class GDCVaultIE(InfoExtractor):
|
class GDCVaultIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?gdcvault\.com/play/(?P<id>\d+)/(?P<name>(\w|-)+)?'
|
_VALID_URL = r'https?://(?:www\.)?gdcvault\.com/play/(?P<id>\d+)(?:/(?P<name>[\w-]+))?'
|
||||||
_NETRC_MACHINE = 'gdcvault'
|
_NETRC_MACHINE = 'gdcvault'
|
||||||
_TESTS = [
|
_TESTS = [
|
||||||
{
|
{
|
||||||
'url': 'http://www.gdcvault.com/play/1019721/Doki-Doki-Universe-Sweet-Simple',
|
'url': 'http://www.gdcvault.com/play/1019721/Doki-Doki-Universe-Sweet-Simple',
|
||||||
'md5': '7ce8388f544c88b7ac11c7ab1b593704',
|
'md5': '7ce8388f544c88b7ac11c7ab1b593704',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '1019721',
|
'id': '201311826596_AWNY',
|
||||||
'display_id': 'Doki-Doki-Universe-Sweet-Simple',
|
'display_id': 'Doki-Doki-Universe-Sweet-Simple',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Doki-Doki Universe: Sweet, Simple and Genuine (GDC Next 10)'
|
'title': 'Doki-Doki Universe: Sweet, Simple and Genuine (GDC Next 10)'
|
||||||
@@ -27,7 +29,7 @@ class GDCVaultIE(InfoExtractor):
|
|||||||
{
|
{
|
||||||
'url': 'http://www.gdcvault.com/play/1015683/Embracing-the-Dark-Art-of',
|
'url': 'http://www.gdcvault.com/play/1015683/Embracing-the-Dark-Art-of',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '1015683',
|
'id': '201203272_1330951438328RSXR',
|
||||||
'display_id': 'Embracing-the-Dark-Art-of',
|
'display_id': 'Embracing-the-Dark-Art-of',
|
||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
'title': 'Embracing the Dark Art of Mathematical Modeling in AI'
|
'title': 'Embracing the Dark Art of Mathematical Modeling in AI'
|
||||||
@@ -56,7 +58,7 @@ class GDCVaultIE(InfoExtractor):
|
|||||||
'url': 'http://gdcvault.com/play/1023460/Tenacious-Design-and-The-Interface',
|
'url': 'http://gdcvault.com/play/1023460/Tenacious-Design-and-The-Interface',
|
||||||
'md5': 'a8efb6c31ed06ca8739294960b2dbabd',
|
'md5': 'a8efb6c31ed06ca8739294960b2dbabd',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '1023460',
|
'id': '840376_BQRC',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'display_id': 'Tenacious-Design-and-The-Interface',
|
'display_id': 'Tenacious-Design-and-The-Interface',
|
||||||
'title': 'Tenacious Design and The Interface of \'Destiny\'',
|
'title': 'Tenacious Design and The Interface of \'Destiny\'',
|
||||||
@@ -66,26 +68,38 @@ class GDCVaultIE(InfoExtractor):
|
|||||||
# Multiple audios
|
# Multiple audios
|
||||||
'url': 'http://www.gdcvault.com/play/1014631/Classic-Game-Postmortem-PAC',
|
'url': 'http://www.gdcvault.com/play/1014631/Classic-Game-Postmortem-PAC',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '1014631',
|
'id': '12396_1299111843500GMPX',
|
||||||
'ext': 'flv',
|
'ext': 'mp4',
|
||||||
'title': 'How to Create a Good Game - From My Experience of Designing Pac-Man',
|
'title': 'How to Create a Good Game - From My Experience of Designing Pac-Man',
|
||||||
},
|
},
|
||||||
'params': {
|
# 'params': {
|
||||||
'skip_download': True, # Requires rtmpdump
|
# 'skip_download': True, # Requires rtmpdump
|
||||||
'format': 'jp', # The japanese audio
|
# 'format': 'jp', # The japanese audio
|
||||||
}
|
# }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
# gdc-player.html
|
# gdc-player.html
|
||||||
'url': 'http://www.gdcvault.com/play/1435/An-American-engine-in-Tokyo',
|
'url': 'http://www.gdcvault.com/play/1435/An-American-engine-in-Tokyo',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '1435',
|
'id': '9350_1238021887562UHXB',
|
||||||
'display_id': 'An-American-engine-in-Tokyo',
|
'display_id': 'An-American-engine-in-Tokyo',
|
||||||
'ext': 'flv',
|
'ext': 'mp4',
|
||||||
'title': 'An American Engine in Tokyo:/nThe collaboration of Epic Games and Square Enix/nFor THE LAST REMINANT',
|
'title': 'An American Engine in Tokyo:/nThe collaboration of Epic Games and Square Enix/nFor THE LAST REMINANT',
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# Kaltura Embed
|
||||||
|
'url': 'https://www.gdcvault.com/play/1026180/Mastering-the-Apex-of-Scaling',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '0_h1fg8j3p',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Mastering the Apex of Scaling Game Servers (Presented by Multiplay)',
|
||||||
|
'timestamp': 1554401811,
|
||||||
|
'upload_date': '20190404',
|
||||||
|
'uploader_id': 'joe@blazestreaming.com',
|
||||||
|
},
|
||||||
'params': {
|
'params': {
|
||||||
'skip_download': True, # Requires rtmpdump
|
'format': 'mp4-408',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -114,10 +128,8 @@ class GDCVaultIE(InfoExtractor):
|
|||||||
return start_page
|
return start_page
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
video_id, name = re.match(self._VALID_URL, url).groups()
|
||||||
|
display_id = name or video_id
|
||||||
video_id = mobj.group('id')
|
|
||||||
display_id = mobj.group('name') or video_id
|
|
||||||
|
|
||||||
webpage_url = 'http://www.gdcvault.com/play/' + video_id
|
webpage_url = 'http://www.gdcvault.com/play/' + video_id
|
||||||
start_page = self._download_webpage(webpage_url, display_id)
|
start_page = self._download_webpage(webpage_url, display_id)
|
||||||
@@ -127,12 +139,12 @@ class GDCVaultIE(InfoExtractor):
|
|||||||
start_page, 'url', default=None)
|
start_page, 'url', default=None)
|
||||||
if direct_url:
|
if direct_url:
|
||||||
title = self._html_search_regex(
|
title = self._html_search_regex(
|
||||||
r'<td><strong>Session Name</strong></td>\s*<td>(.*?)</td>',
|
r'<td><strong>Session Name:?</strong></td>\s*<td>(.*?)</td>',
|
||||||
start_page, 'title')
|
start_page, 'title')
|
||||||
video_url = 'http://www.gdcvault.com' + direct_url
|
video_url = 'http://www.gdcvault.com' + direct_url
|
||||||
# resolve the url so that we can detect the correct extension
|
# resolve the url so that we can detect the correct extension
|
||||||
head = self._request_webpage(HEADRequest(video_url), video_id)
|
video_url = self._request_webpage(
|
||||||
video_url = head.geturl()
|
HEADRequest(video_url), video_id).geturl()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
@@ -141,34 +153,36 @@ class GDCVaultIE(InfoExtractor):
|
|||||||
'title': title,
|
'title': title,
|
||||||
}
|
}
|
||||||
|
|
||||||
PLAYER_REGEX = r'<iframe src="(?P<xml_root>.+?)/(?:gdc-)?player.*?\.html.*?".*?</iframe>'
|
embed_url = KalturaIE._extract_url(start_page)
|
||||||
|
if embed_url:
|
||||||
|
embed_url = smuggle_url(embed_url, {'source_url': url})
|
||||||
|
ie_key = 'Kaltura'
|
||||||
|
else:
|
||||||
|
PLAYER_REGEX = r'<iframe src="(?P<xml_root>.+?)/(?:gdc-)?player.*?\.html.*?".*?</iframe>'
|
||||||
|
|
||||||
xml_root = self._html_search_regex(
|
xml_root = self._html_search_regex(
|
||||||
PLAYER_REGEX, start_page, 'xml root', default=None)
|
PLAYER_REGEX, start_page, 'xml root', default=None)
|
||||||
if xml_root is None:
|
if xml_root is None:
|
||||||
# Probably need to authenticate
|
# Probably need to authenticate
|
||||||
login_res = self._login(webpage_url, display_id)
|
login_res = self._login(webpage_url, display_id)
|
||||||
if login_res is None:
|
if login_res is None:
|
||||||
self.report_warning('Could not login.')
|
self.report_warning('Could not login.')
|
||||||
else:
|
else:
|
||||||
start_page = login_res
|
start_page = login_res
|
||||||
# Grab the url from the authenticated page
|
# Grab the url from the authenticated page
|
||||||
xml_root = self._html_search_regex(
|
xml_root = self._html_search_regex(
|
||||||
PLAYER_REGEX, start_page, 'xml root')
|
PLAYER_REGEX, start_page, 'xml root')
|
||||||
|
|
||||||
xml_name = self._html_search_regex(
|
|
||||||
r'<iframe src=".*?\?xml=(.+?\.xml).*?".*?</iframe>',
|
|
||||||
start_page, 'xml filename', default=None)
|
|
||||||
if xml_name is None:
|
|
||||||
# Fallback to the older format
|
|
||||||
xml_name = self._html_search_regex(
|
xml_name = self._html_search_regex(
|
||||||
r'<iframe src=".*?\?xmlURL=xml/(?P<xml_file>.+?\.xml).*?".*?</iframe>',
|
r'<iframe src=".*?\?xml(?:=|URL=xml/)(.+?\.xml).*?".*?</iframe>',
|
||||||
start_page, 'xml filename')
|
start_page, 'xml filename')
|
||||||
|
embed_url = '%s/xml/%s' % (xml_root, xml_name)
|
||||||
|
ie_key = 'DigitallySpeaking'
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'_type': 'url_transparent',
|
'_type': 'url_transparent',
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'display_id': display_id,
|
'display_id': display_id,
|
||||||
'url': '%s/xml/%s' % (xml_root, xml_name),
|
'url': embed_url,
|
||||||
'ie_key': 'DigitallySpeaking',
|
'ie_key': ie_key,
|
||||||
}
|
}
|
||||||
|
@@ -89,7 +89,10 @@ from .piksel import PikselIE
|
|||||||
from .videa import VideaIE
|
from .videa import VideaIE
|
||||||
from .twentymin import TwentyMinutenIE
|
from .twentymin import TwentyMinutenIE
|
||||||
from .ustream import UstreamIE
|
from .ustream import UstreamIE
|
||||||
from .openload import OpenloadIE
|
from .openload import (
|
||||||
|
OpenloadIE,
|
||||||
|
VerystreamIE,
|
||||||
|
)
|
||||||
from .videopress import VideoPressIE
|
from .videopress import VideoPressIE
|
||||||
from .rutube import RutubeIE
|
from .rutube import RutubeIE
|
||||||
from .limelight import LimelightBaseIE
|
from .limelight import LimelightBaseIE
|
||||||
@@ -2546,11 +2549,11 @@ class GenericIE(InfoExtractor):
|
|||||||
return self.url_result(mobj.group('url'))
|
return self.url_result(mobj.group('url'))
|
||||||
|
|
||||||
# Look for Ooyala videos
|
# Look for Ooyala videos
|
||||||
mobj = (re.search(r'player\.ooyala\.com/[^"?]+[?#][^"]*?(?:embedCode|ec)=(?P<ec>[^"&]+)', webpage) or
|
mobj = (re.search(r'player\.ooyala\.com/[^"?]+[?#][^"]*?(?:embedCode|ec)=(?P<ec>[^"&]+)', webpage)
|
||||||
re.search(r'OO\.Player\.create\([\'"].*?[\'"],\s*[\'"](?P<ec>.{32})[\'"]', webpage) or
|
or re.search(r'OO\.Player\.create\([\'"].*?[\'"],\s*[\'"](?P<ec>.{32})[\'"]', webpage)
|
||||||
re.search(r'OO\.Player\.create\.apply\(\s*OO\.Player\s*,\s*op\(\s*\[\s*[\'"][^\'"]*[\'"]\s*,\s*[\'"](?P<ec>.{32})[\'"]', webpage) or
|
or re.search(r'OO\.Player\.create\.apply\(\s*OO\.Player\s*,\s*op\(\s*\[\s*[\'"][^\'"]*[\'"]\s*,\s*[\'"](?P<ec>.{32})[\'"]', webpage)
|
||||||
re.search(r'SBN\.VideoLinkset\.ooyala\([\'"](?P<ec>.{32})[\'"]\)', webpage) or
|
or re.search(r'SBN\.VideoLinkset\.ooyala\([\'"](?P<ec>.{32})[\'"]\)', webpage)
|
||||||
re.search(r'data-ooyala-video-id\s*=\s*[\'"](?P<ec>.{32})[\'"]', webpage))
|
or re.search(r'data-ooyala-video-id\s*=\s*[\'"](?P<ec>.{32})[\'"]', webpage))
|
||||||
if mobj is not None:
|
if mobj is not None:
|
||||||
embed_token = self._search_regex(
|
embed_token = self._search_regex(
|
||||||
r'embedToken[\'"]?\s*:\s*[\'"]([^\'"]+)',
|
r'embedToken[\'"]?\s*:\s*[\'"]([^\'"]+)',
|
||||||
@@ -2580,19 +2583,6 @@ class GenericIE(InfoExtractor):
|
|||||||
if mobj is not None:
|
if mobj is not None:
|
||||||
return self.url_result(mobj.group(1), 'Mpora')
|
return self.url_result(mobj.group(1), 'Mpora')
|
||||||
|
|
||||||
# Look for embedded NovaMov-based player
|
|
||||||
mobj = re.search(
|
|
||||||
r'''(?x)<(?:pagespeed_)?iframe[^>]+?src=(["\'])
|
|
||||||
(?P<url>http://(?:(?:embed|www)\.)?
|
|
||||||
(?:novamov\.com|
|
|
||||||
nowvideo\.(?:ch|sx|eu|at|ag|co)|
|
|
||||||
videoweed\.(?:es|com)|
|
|
||||||
movshare\.(?:net|sx|ag)|
|
|
||||||
divxstage\.(?:eu|net|ch|co|at|ag))
|
|
||||||
/embed\.php.+?)\1''', webpage)
|
|
||||||
if mobj is not None:
|
|
||||||
return self.url_result(mobj.group('url'))
|
|
||||||
|
|
||||||
# Look for embedded Facebook player
|
# Look for embedded Facebook player
|
||||||
facebook_urls = FacebookIE._extract_urls(webpage)
|
facebook_urls = FacebookIE._extract_urls(webpage)
|
||||||
if facebook_urls:
|
if facebook_urls:
|
||||||
@@ -3017,6 +3007,12 @@ class GenericIE(InfoExtractor):
|
|||||||
return self.playlist_from_matches(
|
return self.playlist_from_matches(
|
||||||
openload_urls, video_id, video_title, ie=OpenloadIE.ie_key())
|
openload_urls, video_id, video_title, ie=OpenloadIE.ie_key())
|
||||||
|
|
||||||
|
# Look for Verystream embeds
|
||||||
|
verystream_urls = VerystreamIE._extract_urls(webpage)
|
||||||
|
if verystream_urls:
|
||||||
|
return self.playlist_from_matches(
|
||||||
|
verystream_urls, video_id, video_title, ie=VerystreamIE.ie_key())
|
||||||
|
|
||||||
# Look for VideoPress embeds
|
# Look for VideoPress embeds
|
||||||
videopress_urls = VideoPressIE._extract_urls(webpage)
|
videopress_urls = VideoPressIE._extract_urls(webpage)
|
||||||
if videopress_urls:
|
if videopress_urls:
|
||||||
@@ -3212,8 +3208,8 @@ class GenericIE(InfoExtractor):
|
|||||||
else:
|
else:
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': src,
|
'url': src,
|
||||||
'ext': (mimetype2ext(src_type) or
|
'ext': (mimetype2ext(src_type)
|
||||||
ext if ext in KNOWN_EXTENSIONS else 'mp4'),
|
or ext if ext in KNOWN_EXTENSIONS else 'mp4'),
|
||||||
})
|
})
|
||||||
if formats:
|
if formats:
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
@@ -11,7 +11,7 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class GfycatIE(InfoExtractor):
|
class GfycatIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?gfycat\.com/(?:ifr/|gifs/detail/)?(?P<id>[^/?#]+)'
|
_VALID_URL = r'https?://(?:www\.)?gfycat\.com/(?:ifr/|gifs/detail/)?(?P<id>[^-/?#]+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://gfycat.com/DeadlyDecisiveGermanpinscher',
|
'url': 'http://gfycat.com/DeadlyDecisiveGermanpinscher',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@@ -47,6 +47,9 @@ class GfycatIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'https://gfycat.com/gifs/detail/UnconsciousLankyIvorygull',
|
'url': 'https://gfycat.com/gifs/detail/UnconsciousLankyIvorygull',
|
||||||
'only_matching': True
|
'only_matching': True
|
||||||
|
}, {
|
||||||
|
'url': 'https://gfycat.com/acceptablehappygoluckyharborporpoise-baseball',
|
||||||
|
'only_matching': True
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
@@ -4,12 +4,12 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_str
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
xpath_text,
|
xpath_text,
|
||||||
xpath_element,
|
xpath_element,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_duration,
|
parse_duration,
|
||||||
|
urljoin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -53,10 +53,13 @@ class HBOBaseIE(InfoExtractor):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def _extract_from_id(self, video_id):
|
def _extract_info(self, url, display_id):
|
||||||
video_data = self._download_xml(
|
video_data = self._download_xml(url, display_id)
|
||||||
'http://render.lv3.hbo.com/data/content/global/videos/data/%s.xml' % video_id, video_id)
|
video_id = xpath_text(video_data, 'id', fatal=True)
|
||||||
title = xpath_text(video_data, 'title', 'title', True)
|
episode_title = title = xpath_text(video_data, 'title', fatal=True)
|
||||||
|
series = xpath_text(video_data, 'program')
|
||||||
|
if series:
|
||||||
|
title = '%s - %s' % (series, title)
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for source in xpath_element(video_data, 'videos', 'sources', True):
|
for source in xpath_element(video_data, 'videos', 'sources', True):
|
||||||
@@ -128,68 +131,45 @@ class HBOBaseIE(InfoExtractor):
|
|||||||
'width': width,
|
'width': width,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
subtitles = None
|
||||||
|
caption_url = xpath_text(video_data, 'captionUrl')
|
||||||
|
if caption_url:
|
||||||
|
subtitles = {
|
||||||
|
'en': [{
|
||||||
|
'url': caption_url,
|
||||||
|
'ext': 'ttml'
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': title,
|
'title': title,
|
||||||
'duration': parse_duration(xpath_text(video_data, 'duration/tv14')),
|
'duration': parse_duration(xpath_text(video_data, 'duration/tv14')),
|
||||||
|
'series': series,
|
||||||
|
'episode': episode_title,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
'thumbnails': thumbnails,
|
'thumbnails': thumbnails,
|
||||||
|
'subtitles': subtitles,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class HBOIE(HBOBaseIE):
|
class HBOIE(HBOBaseIE):
|
||||||
IE_NAME = 'hbo'
|
IE_NAME = 'hbo'
|
||||||
_VALID_URL = r'https?://(?:www\.)?hbo\.com/video/video\.html\?.*vid=(?P<id>[0-9]+)'
|
_VALID_URL = r'https?://(?:www\.)?hbo\.com/(?:video|embed)(?:/[^/]+)*/(?P<id>[^/?#]+)'
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://www.hbo.com/video/video.html?autoplay=true&g=u&vid=1437839',
|
'url': 'https://www.hbo.com/video/game-of-thrones/seasons/season-8/videos/trailer',
|
||||||
'md5': '2c6a6bc1222c7e91cb3334dad1746e5a',
|
'md5': '8126210656f433c452a21367f9ad85b3',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '1437839',
|
'id': '22113301',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Ep. 64 Clip: Encryption',
|
'title': 'Game of Thrones - Trailer',
|
||||||
'thumbnail': r're:https?://.*\.jpg$',
|
},
|
||||||
'duration': 1072,
|
'expected_warnings': ['Unknown MIME type application/mp4 in DASH manifest'],
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
display_id = self._match_id(url)
|
||||||
return self._extract_from_id(video_id)
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
location_path = self._parse_json(self._html_search_regex(
|
||||||
|
r'data-state="({.+?})"', webpage, 'state'), display_id)['video']['locationUrl']
|
||||||
class HBOEpisodeIE(HBOBaseIE):
|
return self._extract_info(urljoin(url, location_path), display_id)
|
||||||
IE_NAME = 'hbo:episode'
|
|
||||||
_VALID_URL = r'https?://(?:www\.)?hbo\.com/(?P<path>(?!video)(?:(?:[^/]+/)+video|watch-free-episodes)/(?P<id>[0-9a-z-]+))(?:\.html)?'
|
|
||||||
|
|
||||||
_TESTS = [{
|
|
||||||
'url': 'http://www.hbo.com/girls/episodes/5/52-i-love-you-baby/video/ep-52-inside-the-episode.html?autoplay=true',
|
|
||||||
'md5': '61ead79b9c0dfa8d3d4b07ef4ac556fb',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '1439518',
|
|
||||||
'display_id': 'ep-52-inside-the-episode',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Ep. 52: Inside the Episode',
|
|
||||||
'thumbnail': r're:https?://.*\.jpg$',
|
|
||||||
'duration': 240,
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
'url': 'http://www.hbo.com/game-of-thrones/about/video/season-5-invitation-to-the-set.html?autoplay=true',
|
|
||||||
'only_matching': True,
|
|
||||||
}, {
|
|
||||||
'url': 'http://www.hbo.com/watch-free-episodes/last-week-tonight-with-john-oliver',
|
|
||||||
'only_matching': True,
|
|
||||||
}]
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
|
||||||
path, display_id = re.match(self._VALID_URL, url).groups()
|
|
||||||
|
|
||||||
content = self._download_json(
|
|
||||||
'http://www.hbo.com/api/content/' + path, display_id)['content']
|
|
||||||
|
|
||||||
video_id = compat_str((content.get('parsed', {}).get(
|
|
||||||
'common:FullBleedVideo', {}) or content['selectedEpisode'])['videoId'])
|
|
||||||
|
|
||||||
info_dict = self._extract_from_id(video_id)
|
|
||||||
info_dict['display_id'] = display_id
|
|
||||||
|
|
||||||
return info_dict
|
|
||||||
|
@@ -155,8 +155,8 @@ class HeiseIE(InfoExtractor):
|
|||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': title,
|
'title': title,
|
||||||
'description': description,
|
'description': description,
|
||||||
'thumbnail': (xpath_text(doc, './/{http://rss.jwpcdn.com/}image') or
|
'thumbnail': (xpath_text(doc, './/{http://rss.jwpcdn.com/}image')
|
||||||
self._og_search_thumbnail(webpage)),
|
or self._og_search_thumbnail(webpage)),
|
||||||
'timestamp': parse_iso8601(
|
'timestamp': parse_iso8601(
|
||||||
self._html_search_meta('date', webpage)),
|
self._html_search_meta('date', webpage)),
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
|
@@ -58,8 +58,8 @@ class HitboxIE(InfoExtractor):
|
|||||||
title = video_meta.get('media_status')
|
title = video_meta.get('media_status')
|
||||||
alt_title = video_meta.get('media_title')
|
alt_title = video_meta.get('media_title')
|
||||||
description = clean_html(
|
description = clean_html(
|
||||||
video_meta.get('media_description') or
|
video_meta.get('media_description')
|
||||||
video_meta.get('media_description_md'))
|
or video_meta.get('media_description_md'))
|
||||||
duration = float_or_none(video_meta.get('media_duration'))
|
duration = float_or_none(video_meta.get('media_duration'))
|
||||||
uploader = video_meta.get('media_user_name')
|
uploader = video_meta.get('media_user_name')
|
||||||
views = int_or_none(video_meta.get('media_views'))
|
views = int_or_none(video_meta.get('media_views'))
|
||||||
|
@@ -47,8 +47,8 @@ class HitRecordIE(InfoExtractor):
|
|||||||
tags = [
|
tags = [
|
||||||
t['text']
|
t['text']
|
||||||
for t in tags_list
|
for t in tags_list
|
||||||
if isinstance(t, dict) and t.get('text') and
|
if isinstance(t, dict) and t.get('text')
|
||||||
isinstance(t['text'], compat_str)]
|
and isinstance(t['text'], compat_str)]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
|
@@ -77,13 +77,13 @@ class HKETVIE(InfoExtractor):
|
|||||||
|
|
||||||
title = (
|
title = (
|
||||||
self._html_search_meta(
|
self._html_search_meta(
|
||||||
('ed_title', 'search.ed_title'), webpage, default=None) or
|
('ed_title', 'search.ed_title'), webpage, default=None)
|
||||||
self._search_regex(
|
or self._search_regex(
|
||||||
r'data-favorite_title_(?:eng|chi)=(["\'])(?P<id>(?:(?!\1).)+)\1',
|
r'data-favorite_title_(?:eng|chi)=(["\'])(?P<id>(?:(?!\1).)+)\1',
|
||||||
webpage, 'title', default=None, group='url') or
|
webpage, 'title', default=None, group='url')
|
||||||
self._html_search_regex(
|
or self._html_search_regex(
|
||||||
r'<h1>([^<]+)</h1>', webpage, 'title', default=None) or
|
r'<h1>([^<]+)</h1>', webpage, 'title', default=None)
|
||||||
self._og_search_title(webpage)
|
or self._og_search_title(webpage)
|
||||||
)
|
)
|
||||||
|
|
||||||
file_id = self._search_regex(
|
file_id = self._search_regex(
|
||||||
|
@@ -4,40 +4,59 @@ from __future__ import unicode_literals
|
|||||||
import hashlib
|
import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
import time
|
import time
|
||||||
|
import uuid
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_HTTPError
|
from ..compat import (
|
||||||
|
compat_HTTPError,
|
||||||
|
compat_str,
|
||||||
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
determine_ext,
|
determine_ext,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
str_or_none,
|
||||||
try_get,
|
try_get,
|
||||||
|
url_or_none,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class HotStarBaseIE(InfoExtractor):
|
class HotStarBaseIE(InfoExtractor):
|
||||||
_AKAMAI_ENCRYPTION_KEY = b'\x05\xfc\x1a\x01\xca\xc9\x4b\xc4\x12\xfc\x53\x12\x07\x75\xf9\xee'
|
_AKAMAI_ENCRYPTION_KEY = b'\x05\xfc\x1a\x01\xca\xc9\x4b\xc4\x12\xfc\x53\x12\x07\x75\xf9\xee'
|
||||||
|
|
||||||
def _call_api(self, path, video_id, query_name='contentId'):
|
def _call_api_impl(self, path, video_id, query):
|
||||||
st = int(time.time())
|
st = int(time.time())
|
||||||
exp = st + 6000
|
exp = st + 6000
|
||||||
auth = 'st=%d~exp=%d~acl=/*' % (st, exp)
|
auth = 'st=%d~exp=%d~acl=/*' % (st, exp)
|
||||||
auth += '~hmac=' + hmac.new(self._AKAMAI_ENCRYPTION_KEY, auth.encode(), hashlib.sha256).hexdigest()
|
auth += '~hmac=' + hmac.new(self._AKAMAI_ENCRYPTION_KEY, auth.encode(), hashlib.sha256).hexdigest()
|
||||||
response = self._download_json(
|
response = self._download_json(
|
||||||
'https://api.hotstar.com/' + path,
|
'https://api.hotstar.com/' + path, video_id, headers={
|
||||||
video_id, headers={
|
|
||||||
'hotstarauth': auth,
|
'hotstarauth': auth,
|
||||||
'x-country-code': 'IN',
|
'x-country-code': 'IN',
|
||||||
'x-platform-code': 'JIO',
|
'x-platform-code': 'JIO',
|
||||||
}, query={
|
}, query=query)
|
||||||
query_name: video_id,
|
|
||||||
'tas': 10000,
|
|
||||||
})
|
|
||||||
if response['statusCode'] != 'OK':
|
if response['statusCode'] != 'OK':
|
||||||
raise ExtractorError(
|
raise ExtractorError(
|
||||||
response['body']['message'], expected=True)
|
response['body']['message'], expected=True)
|
||||||
return response['body']['results']
|
return response['body']['results']
|
||||||
|
|
||||||
|
def _call_api(self, path, video_id, query_name='contentId'):
|
||||||
|
return self._call_api_impl(path, video_id, {
|
||||||
|
query_name: video_id,
|
||||||
|
'tas': 10000,
|
||||||
|
})
|
||||||
|
|
||||||
|
def _call_api_v2(self, path, video_id):
|
||||||
|
return self._call_api_impl(
|
||||||
|
'%s/in/contents/%s' % (path, video_id), video_id, {
|
||||||
|
'desiredConfig': 'encryption:plain;ladder:phone,tv;package:hls,dash',
|
||||||
|
'client': 'mweb',
|
||||||
|
'clientVersion': '6.18.0',
|
||||||
|
'deviceId': compat_str(uuid.uuid4()),
|
||||||
|
'osName': 'Windows',
|
||||||
|
'osVersion': '10',
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class HotStarIE(HotStarBaseIE):
|
class HotStarIE(HotStarBaseIE):
|
||||||
IE_NAME = 'hotstar'
|
IE_NAME = 'hotstar'
|
||||||
@@ -68,6 +87,10 @@ class HotStarIE(HotStarBaseIE):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://www.hotstar.com/1000000515',
|
'url': 'http://www.hotstar.com/1000000515',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# only available via api v2
|
||||||
|
'url': 'https://www.hotstar.com/tv/ek-bhram-sarvagun-sampanna/s-2116/janhvi-targets-suman/1000234847',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
_GEO_BYPASS = False
|
_GEO_BYPASS = False
|
||||||
|
|
||||||
@@ -95,26 +118,40 @@ class HotStarIE(HotStarBaseIE):
|
|||||||
raise ExtractorError('This video is DRM protected.', expected=True)
|
raise ExtractorError('This video is DRM protected.', expected=True)
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
format_data = self._call_api('h/v1/play', video_id)['item']
|
geo_restricted = False
|
||||||
format_url = format_data['playbackUrl']
|
playback_sets = self._call_api_v2('h/v2/play', video_id)['playBackSets']
|
||||||
ext = determine_ext(format_url)
|
for playback_set in playback_sets:
|
||||||
if ext == 'm3u8':
|
if not isinstance(playback_set, dict):
|
||||||
|
continue
|
||||||
|
format_url = url_or_none(playback_set.get('playbackUrl'))
|
||||||
|
if not format_url:
|
||||||
|
continue
|
||||||
|
tags = str_or_none(playback_set.get('tagsCombination')) or ''
|
||||||
|
if tags and 'encryption:plain' not in tags:
|
||||||
|
continue
|
||||||
|
ext = determine_ext(format_url)
|
||||||
try:
|
try:
|
||||||
formats.extend(self._extract_m3u8_formats(
|
if 'package:hls' in tags or ext == 'm3u8':
|
||||||
format_url, video_id, 'mp4', m3u8_id='hls'))
|
formats.extend(self._extract_m3u8_formats(
|
||||||
|
format_url, video_id, 'mp4', m3u8_id='hls'))
|
||||||
|
elif 'package:dash' in tags or ext == 'mpd':
|
||||||
|
formats.extend(self._extract_mpd_formats(
|
||||||
|
format_url, video_id, mpd_id='dash'))
|
||||||
|
elif ext == 'f4m':
|
||||||
|
# produce broken files
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
formats.append({
|
||||||
|
'url': format_url,
|
||||||
|
'width': int_or_none(playback_set.get('width')),
|
||||||
|
'height': int_or_none(playback_set.get('height')),
|
||||||
|
})
|
||||||
except ExtractorError as e:
|
except ExtractorError as e:
|
||||||
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
|
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
|
||||||
self.raise_geo_restricted(countries=['IN'])
|
geo_restricted = True
|
||||||
raise
|
continue
|
||||||
elif ext == 'f4m':
|
if not formats and geo_restricted:
|
||||||
# produce broken files
|
self.raise_geo_restricted(countries=['IN'])
|
||||||
pass
|
|
||||||
else:
|
|
||||||
formats.append({
|
|
||||||
'url': format_url,
|
|
||||||
'width': int_or_none(format_data.get('width')),
|
|
||||||
'height': int_or_none(format_data.get('height')),
|
|
||||||
})
|
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -60,8 +60,8 @@ class HRTiBaseIE(InfoExtractor):
|
|||||||
language=self._APP_LANGUAGE,
|
language=self._APP_LANGUAGE,
|
||||||
application_id=self._APP_PUBLICATION_ID)
|
application_id=self._APP_PUBLICATION_ID)
|
||||||
|
|
||||||
self._login_url = (modules['user']['resources']['login']['uri'] +
|
self._login_url = (modules['user']['resources']['login']['uri']
|
||||||
'/format/json').format(session_id=self._session_id)
|
+ '/format/json').format(session_id=self._session_id)
|
||||||
|
|
||||||
self._logout_url = modules['user']['resources']['logout']['uri']
|
self._logout_url = modules['user']['resources']['logout']['uri']
|
||||||
|
|
||||||
|
@@ -1,36 +1,83 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
determine_ext,
|
||||||
|
int_or_none,
|
||||||
|
strip_or_none,
|
||||||
|
xpath_attr,
|
||||||
|
xpath_text,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class InaIE(InfoExtractor):
|
class InaIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?ina\.fr/video/(?P<id>I?[A-Z0-9]+)'
|
_VALID_URL = r'https?://(?:www\.)?ina\.fr/(?:video|audio)/(?P<id>[A-Z0-9_]+)'
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
'url': 'http://www.ina.fr/video/I12055569/francois-hollande-je-crois-que-c-est-clair-video.html',
|
'url': 'http://www.ina.fr/video/I12055569/francois-hollande-je-crois-que-c-est-clair-video.html',
|
||||||
'md5': 'a667021bf2b41f8dc6049479d9bb38a3',
|
'md5': 'a667021bf2b41f8dc6049479d9bb38a3',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'I12055569',
|
'id': 'I12055569',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'François Hollande "Je crois que c\'est clair"',
|
'title': 'François Hollande "Je crois que c\'est clair"',
|
||||||
|
'description': 'md5:3f09eb072a06cb286b8f7e4f77109663',
|
||||||
}
|
}
|
||||||
}
|
}, {
|
||||||
|
'url': 'https://www.ina.fr/video/S806544_001/don-d-organes-des-avancees-mais-d-importants-besoins-video.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.ina.fr/audio/P16173408',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.ina.fr/video/P16173408-video.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
video_id = self._match_id(url)
|
||||||
|
info_doc = self._download_xml(
|
||||||
|
'http://player.ina.fr/notices/%s.mrss' % video_id, video_id)
|
||||||
|
item = info_doc.find('channel/item')
|
||||||
|
title = xpath_text(item, 'title', fatal=True)
|
||||||
|
media_ns_xpath = lambda x: self._xpath_ns(x, 'http://search.yahoo.com/mrss/')
|
||||||
|
content = item.find(media_ns_xpath('content'))
|
||||||
|
|
||||||
video_id = mobj.group('id')
|
get_furl = lambda x: xpath_attr(content, media_ns_xpath(x), 'url')
|
||||||
mrss_url = 'http://player.ina.fr/notices/%s.mrss' % video_id
|
formats = []
|
||||||
info_doc = self._download_xml(mrss_url, video_id)
|
for q, w, h in (('bq', 400, 300), ('mq', 512, 384), ('hq', 768, 576)):
|
||||||
|
q_url = get_furl(q)
|
||||||
|
if not q_url:
|
||||||
|
continue
|
||||||
|
formats.append({
|
||||||
|
'format_id': q,
|
||||||
|
'url': q_url,
|
||||||
|
'width': w,
|
||||||
|
'height': h,
|
||||||
|
})
|
||||||
|
if not formats:
|
||||||
|
furl = get_furl('player') or content.attrib['url']
|
||||||
|
ext = determine_ext(furl)
|
||||||
|
formats = [{
|
||||||
|
'url': furl,
|
||||||
|
'vcodec': 'none' if ext == 'mp3' else None,
|
||||||
|
'ext': ext,
|
||||||
|
}]
|
||||||
|
|
||||||
self.report_extraction(video_id)
|
thumbnails = []
|
||||||
|
for thumbnail in content.findall(media_ns_xpath('thumbnail')):
|
||||||
video_url = info_doc.find('.//{http://search.yahoo.com/mrss/}player').attrib['url']
|
thumbnail_url = thumbnail.get('url')
|
||||||
|
if not thumbnail_url:
|
||||||
|
continue
|
||||||
|
thumbnails.append({
|
||||||
|
'url': thumbnail_url,
|
||||||
|
'height': int_or_none(thumbnail.get('height')),
|
||||||
|
'width': int_or_none(thumbnail.get('width')),
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'url': video_url,
|
'formats': formats,
|
||||||
'title': info_doc.find('.//title').text,
|
'title': title,
|
||||||
|
'description': strip_or_none(xpath_text(item, 'description')),
|
||||||
|
'thumbnails': thumbnails,
|
||||||
}
|
}
|
||||||
|
@@ -122,9 +122,9 @@ class InfoQIE(BokeCCBaseIE):
|
|||||||
formats = self._extract_bokecc_formats(webpage, video_id)
|
formats = self._extract_bokecc_formats(webpage, video_id)
|
||||||
else:
|
else:
|
||||||
formats = (
|
formats = (
|
||||||
self._extract_rtmp_video(webpage) +
|
self._extract_rtmp_video(webpage)
|
||||||
self._extract_http_video(webpage) +
|
+ self._extract_http_video(webpage)
|
||||||
self._extract_http_audio(webpage, video_id))
|
+ self._extract_http_audio(webpage, video_id))
|
||||||
|
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
@@ -383,9 +383,9 @@ class IqiyiIE(InfoExtractor):
|
|||||||
self._sleep(5, video_id)
|
self._sleep(5, video_id)
|
||||||
|
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
title = (get_element_by_id('widget-videotitle', webpage) or
|
title = (get_element_by_id('widget-videotitle', webpage)
|
||||||
clean_html(get_element_by_attribute('class', 'mod-play-tit', webpage)) or
|
or clean_html(get_element_by_attribute('class', 'mod-play-tit', webpage))
|
||||||
self._html_search_regex(r'<span[^>]+data-videochanged-title="word"[^>]*>([^<]+)</span>', webpage, 'title'))
|
or self._html_search_regex(r'<span[^>]+data-videochanged-title="word"[^>]*>([^<]+)</span>', webpage, 'title'))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
|
@@ -77,10 +77,10 @@ class ITVIE(InfoExtractor):
|
|||||||
return etree.SubElement(element, _add_ns(name))
|
return etree.SubElement(element, _add_ns(name))
|
||||||
|
|
||||||
production_id = (
|
production_id = (
|
||||||
params.get('data-video-autoplay-id') or
|
params.get('data-video-autoplay-id')
|
||||||
'%s#001' % (
|
or '%s#001' % (
|
||||||
params.get('data-video-episode-id') or
|
params.get('data-video-episode-id')
|
||||||
video_id.replace('a', '/')))
|
or video_id.replace('a', '/')))
|
||||||
|
|
||||||
req_env = etree.Element(_add_ns('soapenv:Envelope'))
|
req_env = etree.Element(_add_ns('soapenv:Envelope'))
|
||||||
_add_sub_element(req_env, 'soapenv:Header')
|
_add_sub_element(req_env, 'soapenv:Header')
|
||||||
|
@@ -7,7 +7,7 @@ from .common import InfoExtractor
|
|||||||
|
|
||||||
|
|
||||||
class JWPlatformIE(InfoExtractor):
|
class JWPlatformIE(InfoExtractor):
|
||||||
_VALID_URL = r'(?:https?://(?:content\.jwplatform|cdn\.jwplayer)\.com/(?:(?:feed|player|thumb|preview|video|manifest)s|jw6|v2/media)/|jwplatform:)(?P<id>[a-zA-Z0-9]{8})'
|
_VALID_URL = r'(?:https?://(?:content\.jwplatform|cdn\.jwplayer)\.com/(?:(?:feed|player|thumb|preview|video)s|jw6|v2/media)/|jwplatform:)(?P<id>[a-zA-Z0-9]{8})'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://content.jwplatform.com/players/nPripu9l-ALJ3XQCI.js',
|
'url': 'http://content.jwplatform.com/players/nPripu9l-ALJ3XQCI.js',
|
||||||
'md5': 'fa8899fa601eb7c83a64e9d568bdf325',
|
'md5': 'fa8899fa601eb7c83a64e9d568bdf325',
|
||||||
|
@@ -118,8 +118,8 @@ class KalturaIE(InfoExtractor):
|
|||||||
(?P<q2>['"])_?(?P<partner_id>(?:(?!(?P=q2)).)+)(?P=q2),.*?
|
(?P<q2>['"])_?(?P<partner_id>(?:(?!(?P=q2)).)+)(?P=q2),.*?
|
||||||
(?P<q3>['"])entry_?[Ii]d(?P=q3)\s*:\s*
|
(?P<q3>['"])entry_?[Ii]d(?P=q3)\s*:\s*
|
||||||
(?P<q4>['"])(?P<id>(?:(?!(?P=q4)).)+)(?P=q4)(?:,|\s*\})
|
(?P<q4>['"])(?P<id>(?:(?!(?P=q4)).)+)(?P=q4)(?:,|\s*\})
|
||||||
""", webpage) or
|
""", webpage)
|
||||||
re.search(
|
or re.search(
|
||||||
r'''(?xs)
|
r'''(?xs)
|
||||||
(?P<q1>["'])
|
(?P<q1>["'])
|
||||||
(?:https?:)?//cdnapi(?:sec)?\.kaltura\.com(?::\d+)?/(?:(?!(?P=q1)).)*\b(?:p|partner_id)/(?P<partner_id>\d+)(?:(?!(?P=q1)).)*
|
(?:https?:)?//cdnapi(?:sec)?\.kaltura\.com(?::\d+)?/(?:(?!(?P=q1)).)*\b(?:p|partner_id)/(?P<partner_id>\d+)(?:(?!(?P=q1)).)*
|
||||||
@@ -132,8 +132,8 @@ class KalturaIE(InfoExtractor):
|
|||||||
\[\s*(?P<q2_1>["'])entry_?[Ii]d(?P=q2_1)\s*\]\s*=\s*
|
\[\s*(?P<q2_1>["'])entry_?[Ii]d(?P=q2_1)\s*\]\s*=\s*
|
||||||
)
|
)
|
||||||
(?P<q3>["'])(?P<id>(?:(?!(?P=q3)).)+)(?P=q3)
|
(?P<q3>["'])(?P<id>(?:(?!(?P=q3)).)+)(?P=q3)
|
||||||
''', webpage) or
|
''', webpage)
|
||||||
re.search(
|
or re.search(
|
||||||
r'''(?xs)
|
r'''(?xs)
|
||||||
<(?:iframe[^>]+src|meta[^>]+\bcontent)=(?P<q1>["'])
|
<(?:iframe[^>]+src|meta[^>]+\bcontent)=(?P<q1>["'])
|
||||||
(?:https?:)?//(?:(?:www|cdnapi(?:sec)?)\.)?kaltura\.com/(?:(?!(?P=q1)).)*\b(?:p|partner_id)/(?P<partner_id>\d+)
|
(?:https?:)?//(?:(?:www|cdnapi(?:sec)?)\.)?kaltura\.com/(?:(?!(?P=q1)).)*\b(?:p|partner_id)/(?P<partner_id>\d+)
|
||||||
@@ -145,6 +145,8 @@ class KalturaIE(InfoExtractor):
|
|||||||
)
|
)
|
||||||
if mobj:
|
if mobj:
|
||||||
embed_info = mobj.groupdict()
|
embed_info = mobj.groupdict()
|
||||||
|
for k, v in embed_info.items():
|
||||||
|
embed_info[k] = v.strip()
|
||||||
url = 'kaltura:%(partner_id)s:%(id)s' % embed_info
|
url = 'kaltura:%(partner_id)s:%(id)s' % embed_info
|
||||||
escaped_pid = re.escape(embed_info['partner_id'])
|
escaped_pid = re.escape(embed_info['partner_id'])
|
||||||
service_url = re.search(
|
service_url = re.search(
|
||||||
|
@@ -47,8 +47,8 @@ class KarriereVideosIE(InfoExtractor):
|
|||||||
|
|
||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
title = (self._html_search_meta('title', webpage, default=None) or
|
title = (self._html_search_meta('title', webpage, default=None)
|
||||||
self._search_regex(r'<h1 class="title">([^<]+)</h1>', webpage, 'video title'))
|
or self._search_regex(r'<h1 class="title">([^<]+)</h1>', webpage, 'video title'))
|
||||||
|
|
||||||
video_id = self._search_regex(
|
video_id = self._search_regex(
|
||||||
r'/config/video/(.+?)\.xml', webpage, 'video id')
|
r'/config/video/(.+?)\.xml', webpage, 'video id')
|
||||||
|
@@ -9,11 +9,13 @@ from ..utils import (
|
|||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
urlencode_postdata,
|
urlencode_postdata,
|
||||||
|
urljoin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class LinkedInLearningBaseIE(InfoExtractor):
|
class LinkedInLearningBaseIE(InfoExtractor):
|
||||||
_NETRC_MACHINE = 'linkedin'
|
_NETRC_MACHINE = 'linkedin'
|
||||||
|
_LOGIN_URL = 'https://www.linkedin.com/uas/login?trk=learning'
|
||||||
|
|
||||||
def _call_api(self, course_slug, fields, video_slug=None, resolution=None):
|
def _call_api(self, course_slug, fields, video_slug=None, resolution=None):
|
||||||
query = {
|
query = {
|
||||||
@@ -50,11 +52,10 @@ class LinkedInLearningBaseIE(InfoExtractor):
|
|||||||
return
|
return
|
||||||
|
|
||||||
login_page = self._download_webpage(
|
login_page = self._download_webpage(
|
||||||
'https://www.linkedin.com/uas/login?trk=learning',
|
self._LOGIN_URL, None, 'Downloading login page')
|
||||||
None, 'Downloading login page')
|
action_url = urljoin(self._LOGIN_URL, self._search_regex(
|
||||||
action_url = self._search_regex(
|
|
||||||
r'<form[^>]+action=(["\'])(?P<url>.+?)\1', login_page, 'post url',
|
r'<form[^>]+action=(["\'])(?P<url>.+?)\1', login_page, 'post url',
|
||||||
default='https://www.linkedin.com/uas/login-submit', group='url')
|
default='https://www.linkedin.com/uas/login-submit', group='url'))
|
||||||
data = self._hidden_inputs(login_page)
|
data = self._hidden_inputs(login_page)
|
||||||
data.update({
|
data.update({
|
||||||
'session_key': email,
|
'session_key': email,
|
||||||
|
@@ -82,6 +82,10 @@ class LiveLeakIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'https://www.liveleak.com/view?t=HvHi_1523016227',
|
'url': 'https://www.liveleak.com/view?t=HvHi_1523016227',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# No original video
|
||||||
|
'url': 'https://www.liveleak.com/view?t=C26ZZ_1558612804',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -134,11 +138,13 @@ class LiveLeakIE(InfoExtractor):
|
|||||||
orig_url = re.sub(r'\.mp4\.[^.]+', '', a_format['url'])
|
orig_url = re.sub(r'\.mp4\.[^.]+', '', a_format['url'])
|
||||||
if a_format['url'] != orig_url:
|
if a_format['url'] != orig_url:
|
||||||
format_id = a_format.get('format_id')
|
format_id = a_format.get('format_id')
|
||||||
formats.append({
|
format_id = 'original' + ('-' + format_id if format_id else '')
|
||||||
'format_id': 'original' + ('-' + format_id if format_id else ''),
|
if self._is_valid_url(orig_url, video_id, format_id):
|
||||||
'url': orig_url,
|
formats.append({
|
||||||
'preference': 1,
|
'format_id': format_id,
|
||||||
})
|
'url': orig_url,
|
||||||
|
'preference': 1,
|
||||||
|
})
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
info_dict['formats'] = formats
|
info_dict['formats'] = formats
|
||||||
|
|
||||||
|
@@ -13,6 +13,8 @@ from ..utils import (
|
|||||||
ExtractorError,
|
ExtractorError,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
mimetype2ext,
|
mimetype2ext,
|
||||||
|
str_or_none,
|
||||||
|
try_get,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
unsmuggle_url,
|
unsmuggle_url,
|
||||||
url_or_none,
|
url_or_none,
|
||||||
@@ -20,8 +22,11 @@ from ..utils import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
_ID_RE = r'(?:[0-9a-f]{32,34}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12,14})'
|
||||||
|
|
||||||
|
|
||||||
class MediasiteIE(InfoExtractor):
|
class MediasiteIE(InfoExtractor):
|
||||||
_VALID_URL = r'(?xi)https?://[^/]+/Mediasite/(?:Play|Showcase/(?:default|livebroadcast)/Presentation)/(?P<id>[0-9a-f]{32,34})(?P<query>\?[^#]+|)'
|
_VALID_URL = r'(?xi)https?://[^/]+/Mediasite/(?:Play|Showcase/(?:default|livebroadcast)/Presentation)/(?P<id>%s)(?P<query>\?[^#]+|)' % _ID_RE
|
||||||
_TESTS = [
|
_TESTS = [
|
||||||
{
|
{
|
||||||
'url': 'https://hitsmediaweb.h-its.org/mediasite/Play/2db6c271681e4f199af3c60d1f82869b1d',
|
'url': 'https://hitsmediaweb.h-its.org/mediasite/Play/2db6c271681e4f199af3c60d1f82869b1d',
|
||||||
@@ -93,6 +98,11 @@ class MediasiteIE(InfoExtractor):
|
|||||||
'url': 'https://mediasite.ntnu.no/Mediasite/Showcase/default/Presentation/7d8b913259334b688986e970fae6fcb31d',
|
'url': 'https://mediasite.ntnu.no/Mediasite/Showcase/default/Presentation/7d8b913259334b688986e970fae6fcb31d',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
# dashed id
|
||||||
|
'url': 'https://hitsmediaweb.h-its.org/mediasite/Play/2db6c271-681e-4f19-9af3-c60d1f82869b1d',
|
||||||
|
'only_matching': True,
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
# look in Mediasite.Core.js (Mediasite.ContentStreamType[*])
|
# look in Mediasite.Core.js (Mediasite.ContentStreamType[*])
|
||||||
@@ -109,7 +119,7 @@ class MediasiteIE(InfoExtractor):
|
|||||||
return [
|
return [
|
||||||
unescapeHTML(mobj.group('url'))
|
unescapeHTML(mobj.group('url'))
|
||||||
for mobj in re.finditer(
|
for mobj in re.finditer(
|
||||||
r'(?xi)<iframe\b[^>]+\bsrc=(["\'])(?P<url>(?:(?:https?:)?//[^/]+)?/Mediasite/Play/[0-9a-f]{32,34}(?:\?.*?)?)\1',
|
r'(?xi)<iframe\b[^>]+\bsrc=(["\'])(?P<url>(?:(?:https?:)?//[^/]+)?/Mediasite/Play/%s(?:\?.*?)?)\1' % _ID_RE,
|
||||||
webpage)]
|
webpage)]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
@@ -221,3 +231,136 @@ class MediasiteIE(InfoExtractor):
|
|||||||
'formats': formats,
|
'formats': formats,
|
||||||
'thumbnails': thumbnails,
|
'thumbnails': thumbnails,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MediasiteCatalogIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'''(?xi)
|
||||||
|
(?P<url>https?://[^/]+/Mediasite)
|
||||||
|
/Catalog/Full/
|
||||||
|
(?P<catalog_id>{0})
|
||||||
|
(?:
|
||||||
|
/(?P<current_folder_id>{0})
|
||||||
|
/(?P<root_dynamic_folder_id>{0})
|
||||||
|
)?
|
||||||
|
'''.format(_ID_RE)
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'http://events7.mediasite.com/Mediasite/Catalog/Full/631f9e48530d454381549f955d08c75e21',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '631f9e48530d454381549f955d08c75e21',
|
||||||
|
'title': 'WCET Summit: Adaptive Learning in Higher Ed: Improving Outcomes Dynamically',
|
||||||
|
},
|
||||||
|
'playlist_count': 6,
|
||||||
|
'expected_warnings': ['is not a supported codec'],
|
||||||
|
}, {
|
||||||
|
# with CurrentFolderId and RootDynamicFolderId
|
||||||
|
'url': 'https://medaudio.medicine.iu.edu/Mediasite/Catalog/Full/9518c4a6c5cf4993b21cbd53e828a92521/97a9db45f7ab47428c77cd2ed74bb98f14/9518c4a6c5cf4993b21cbd53e828a92521',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '9518c4a6c5cf4993b21cbd53e828a92521',
|
||||||
|
'title': 'IUSM Family and Friends Sessions',
|
||||||
|
},
|
||||||
|
'playlist_count': 2,
|
||||||
|
}, {
|
||||||
|
'url': 'http://uipsyc.mediasite.com/mediasite/Catalog/Full/d5d79287c75243c58c50fef50174ec1b21',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# no AntiForgeryToken
|
||||||
|
'url': 'https://live.libraries.psu.edu/Mediasite/Catalog/Full/8376d4b24dd1457ea3bfe4cf9163feda21',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://medaudio.medicine.iu.edu/Mediasite/Catalog/Full/9518c4a6c5cf4993b21cbd53e828a92521/97a9db45f7ab47428c77cd2ed74bb98f14/9518c4a6c5cf4993b21cbd53e828a92521',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# dashed id
|
||||||
|
'url': 'http://events7.mediasite.com/Mediasite/Catalog/Full/631f9e48-530d-4543-8154-9f955d08c75e',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
mobj = re.match(self._VALID_URL, url)
|
||||||
|
mediasite_url = mobj.group('url')
|
||||||
|
catalog_id = mobj.group('catalog_id')
|
||||||
|
current_folder_id = mobj.group('current_folder_id') or catalog_id
|
||||||
|
root_dynamic_folder_id = mobj.group('root_dynamic_folder_id')
|
||||||
|
|
||||||
|
webpage = self._download_webpage(url, catalog_id)
|
||||||
|
|
||||||
|
# AntiForgeryToken is optional (e.g. [1])
|
||||||
|
# 1. https://live.libraries.psu.edu/Mediasite/Catalog/Full/8376d4b24dd1457ea3bfe4cf9163feda21
|
||||||
|
anti_forgery_token = self._search_regex(
|
||||||
|
r'AntiForgeryToken\s*:\s*(["\'])(?P<value>(?:(?!\1).)+)\1',
|
||||||
|
webpage, 'anti forgery token', default=None, group='value')
|
||||||
|
if anti_forgery_token:
|
||||||
|
anti_forgery_header = self._search_regex(
|
||||||
|
r'AntiForgeryHeaderName\s*:\s*(["\'])(?P<value>(?:(?!\1).)+)\1',
|
||||||
|
webpage, 'anti forgery header name',
|
||||||
|
default='X-SOFO-AntiForgeryHeader', group='value')
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'IsViewPage': True,
|
||||||
|
'IsNewFolder': True,
|
||||||
|
'AuthTicket': None,
|
||||||
|
'CatalogId': catalog_id,
|
||||||
|
'CurrentFolderId': current_folder_id,
|
||||||
|
'RootDynamicFolderId': root_dynamic_folder_id,
|
||||||
|
'ItemsPerPage': 1000,
|
||||||
|
'PageIndex': 0,
|
||||||
|
'PermissionMask': 'Execute',
|
||||||
|
'CatalogSearchType': 'SearchInFolder',
|
||||||
|
'SortBy': 'Date',
|
||||||
|
'SortDirection': 'Descending',
|
||||||
|
'StartDate': None,
|
||||||
|
'EndDate': None,
|
||||||
|
'StatusFilterList': None,
|
||||||
|
'PreviewKey': None,
|
||||||
|
'Tags': [],
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
'Referer': url,
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
}
|
||||||
|
if anti_forgery_token:
|
||||||
|
headers[anti_forgery_header] = anti_forgery_token
|
||||||
|
|
||||||
|
catalog = self._download_json(
|
||||||
|
'%s/Catalog/Data/GetPresentationsForFolder' % mediasite_url,
|
||||||
|
catalog_id, data=json.dumps(data).encode(), headers=headers)
|
||||||
|
|
||||||
|
entries = []
|
||||||
|
for video in catalog['PresentationDetailsList']:
|
||||||
|
if not isinstance(video, dict):
|
||||||
|
continue
|
||||||
|
video_id = str_or_none(video.get('Id'))
|
||||||
|
if not video_id:
|
||||||
|
continue
|
||||||
|
entries.append(self.url_result(
|
||||||
|
'%s/Play/%s' % (mediasite_url, video_id),
|
||||||
|
ie=MediasiteIE.ie_key(), video_id=video_id))
|
||||||
|
|
||||||
|
title = try_get(
|
||||||
|
catalog, lambda x: x['CurrentFolder']['Name'], compat_str)
|
||||||
|
|
||||||
|
return self.playlist_result(entries, catalog_id, title,)
|
||||||
|
|
||||||
|
|
||||||
|
class MediasiteNamedCatalogIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'(?xi)(?P<url>https?://[^/]+/Mediasite)/Catalog/catalogs/(?P<catalog_name>[^/?#&]+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://msite.misis.ru/Mediasite/Catalog/catalogs/2016-industrial-management-skriabin-o-o',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
mobj = re.match(self._VALID_URL, url)
|
||||||
|
mediasite_url = mobj.group('url')
|
||||||
|
catalog_name = mobj.group('catalog_name')
|
||||||
|
|
||||||
|
webpage = self._download_webpage(url, catalog_name)
|
||||||
|
|
||||||
|
catalog_id = self._search_regex(
|
||||||
|
r'CatalogId\s*:\s*["\'](%s)' % _ID_RE, webpage, 'catalog id')
|
||||||
|
|
||||||
|
return self.url_result(
|
||||||
|
'%s/Catalog/Full/%s' % (mediasite_url, catalog_id),
|
||||||
|
ie=MediasiteCatalogIE.ie_key(), video_id=catalog_id)
|
||||||
|
@@ -1,22 +1,32 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import time
|
||||||
|
import uuid
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_str
|
from ..compat import (
|
||||||
from ..utils import int_or_none
|
compat_HTTPError,
|
||||||
|
compat_str,
|
||||||
|
)
|
||||||
|
from ..utils import (
|
||||||
|
ExtractorError,
|
||||||
|
int_or_none,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MGTVIE(InfoExtractor):
|
class MGTVIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?mgtv\.com/(v|b)/(?:[^/]+/)*(?P<id>\d+)\.html'
|
_VALID_URL = r'https?://(?:www\.)?mgtv\.com/(v|b)/(?:[^/]+/)*(?P<id>\d+)\.html'
|
||||||
IE_DESC = '芒果TV'
|
IE_DESC = '芒果TV'
|
||||||
|
_GEO_COUNTRIES = ['CN']
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.mgtv.com/v/1/290525/f/3116640.html',
|
'url': 'http://www.mgtv.com/v/1/290525/f/3116640.html',
|
||||||
'md5': 'b1ffc0fc163152acf6beaa81832c9ee7',
|
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '3116640',
|
'id': '3116640',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': '我是歌手第四季双年巅峰会:韩红李玟“双王”领军对抗',
|
'title': '我是歌手 第四季',
|
||||||
'description': '我是歌手第四季双年巅峰会',
|
'description': '我是歌手第四季双年巅峰会',
|
||||||
'duration': 7461,
|
'duration': 7461,
|
||||||
'thumbnail': r're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
@@ -28,16 +38,30 @@ class MGTVIE(InfoExtractor):
|
|||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
api_data = self._download_json(
|
try:
|
||||||
'http://pcweb.api.mgtv.com/player/video', video_id,
|
api_data = self._download_json(
|
||||||
query={'video_id': video_id},
|
'https://pcweb.api.mgtv.com/player/video', video_id, query={
|
||||||
headers=self.geo_verification_headers())['data']
|
'tk2': base64.urlsafe_b64encode(b'did=%s|pno=1030|ver=0.3.0301|clit=%d' % (compat_str(uuid.uuid4()).encode(), time.time()))[::-1],
|
||||||
|
'video_id': video_id,
|
||||||
|
}, headers=self.geo_verification_headers())['data']
|
||||||
|
except ExtractorError as e:
|
||||||
|
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401:
|
||||||
|
error = self._parse_json(e.cause.read().decode(), None)
|
||||||
|
if error.get('code') == 40005:
|
||||||
|
self.raise_geo_restricted(countries=self._GEO_COUNTRIES)
|
||||||
|
raise ExtractorError(error['msg'], expected=True)
|
||||||
|
raise
|
||||||
info = api_data['info']
|
info = api_data['info']
|
||||||
title = info['title'].strip()
|
title = info['title'].strip()
|
||||||
stream_domain = api_data['stream_domain'][0]
|
stream_data = self._download_json(
|
||||||
|
'https://pcweb.api.mgtv.com/player/getSource', video_id, query={
|
||||||
|
'pm2': api_data['atc']['pm2'],
|
||||||
|
'video_id': video_id,
|
||||||
|
}, headers=self.geo_verification_headers())['data']
|
||||||
|
stream_domain = stream_data['stream_domain'][0]
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for idx, stream in enumerate(api_data['stream']):
|
for idx, stream in enumerate(stream_data['stream']):
|
||||||
stream_path = stream.get('url')
|
stream_path = stream.get('url')
|
||||||
if not stream_path:
|
if not stream_path:
|
||||||
continue
|
continue
|
||||||
@@ -47,7 +71,7 @@ class MGTVIE(InfoExtractor):
|
|||||||
format_url = format_data.get('info')
|
format_url = format_data.get('info')
|
||||||
if not format_url:
|
if not format_url:
|
||||||
continue
|
continue
|
||||||
tbr = int_or_none(self._search_regex(
|
tbr = int_or_none(stream.get('filebitrate') or self._search_regex(
|
||||||
r'_(\d+)_mp4/', format_url, 'tbr', default=None))
|
r'_(\d+)_mp4/', format_url, 'tbr', default=None))
|
||||||
formats.append({
|
formats.append({
|
||||||
'format_id': compat_str(tbr or idx),
|
'format_id': compat_str(tbr or idx),
|
||||||
|
@@ -80,8 +80,8 @@ class MotherlessIE(InfoExtractor):
|
|||||||
video_url = (self._html_search_regex(
|
video_url = (self._html_search_regex(
|
||||||
(r'setup\(\{\s*["\']file["\']\s*:\s*(["\'])(?P<url>(?:(?!\1).)+)\1',
|
(r'setup\(\{\s*["\']file["\']\s*:\s*(["\'])(?P<url>(?:(?!\1).)+)\1',
|
||||||
r'fileurl\s*=\s*(["\'])(?P<url>(?:(?!\1).)+)\1'),
|
r'fileurl\s*=\s*(["\'])(?P<url>(?:(?!\1).)+)\1'),
|
||||||
webpage, 'video URL', default=None, group='url') or
|
webpage, 'video URL', default=None, group='url')
|
||||||
'http://cdn4.videos.motherlessmedia.com/videos/%s.mp4?fs=opencloud' % video_id)
|
or 'http://cdn4.videos.motherlessmedia.com/videos/%s.mp4?fs=opencloud' % video_id)
|
||||||
age_limit = self._rta_search(webpage)
|
age_limit = self._rta_search(webpage)
|
||||||
view_count = str_to_int(self._html_search_regex(
|
view_count = str_to_int(self._html_search_regex(
|
||||||
r'<strong>Views</strong>\s+([^<]+)<',
|
r'<strong>Views</strong>\s+([^<]+)<',
|
||||||
|
@@ -84,8 +84,8 @@ class NDTVIE(InfoExtractor):
|
|||||||
|
|
||||||
# '__title' does not contain extra words such as sub-site name, "Video" etc.
|
# '__title' does not contain extra words such as sub-site name, "Video" etc.
|
||||||
title = compat_urllib_parse_unquote_plus(
|
title = compat_urllib_parse_unquote_plus(
|
||||||
self._search_regex(r"__title\s*=\s*'([^']+)'", webpage, 'title', default=None) or
|
self._search_regex(r"__title\s*=\s*'([^']+)'", webpage, 'title', default=None)
|
||||||
self._og_search_title(webpage))
|
or self._og_search_title(webpage))
|
||||||
|
|
||||||
filename = self._search_regex(
|
filename = self._search_regex(
|
||||||
r"(?:__)?filename\s*[:=]\s*'([^']+)'", webpage, 'video filename')
|
r"(?:__)?filename\s*[:=]\s*'([^']+)'", webpage, 'video filename')
|
||||||
|
@@ -1,12 +1,17 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import re
|
import base64
|
||||||
|
import hashlib
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..aes import aes_cbc_decrypt
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
bytes_to_intlist,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
intlist_to_bytes,
|
||||||
|
parse_codecs,
|
||||||
|
parse_duration,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -14,7 +19,7 @@ class NewstubeIE(InfoExtractor):
|
|||||||
_VALID_URL = r'https?://(?:www\.)?newstube\.ru/media/(?P<id>.+)'
|
_VALID_URL = r'https?://(?:www\.)?newstube\.ru/media/(?P<id>.+)'
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://www.newstube.ru/media/telekanal-cnn-peremestil-gorod-slavyansk-v-krym',
|
'url': 'http://www.newstube.ru/media/telekanal-cnn-peremestil-gorod-slavyansk-v-krym',
|
||||||
'md5': '801eef0c2a9f4089fa04e4fe3533abdc',
|
'md5': '9d10320ad473444352f72f746ccb8b8c',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '728e0ef2-e187-4012-bac0-5a081fdcb1f6',
|
'id': '728e0ef2-e187-4012-bac0-5a081fdcb1f6',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
@@ -25,84 +30,45 @@ class NewstubeIE(InfoExtractor):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
video_id = self._match_id(url)
|
||||||
video_id = mobj.group('id')
|
|
||||||
|
|
||||||
page = self._download_webpage(url, video_id, 'Downloading page')
|
page = self._download_webpage(url, video_id)
|
||||||
|
title = self._html_search_meta(['og:title', 'twitter:title'], page, fatal=True)
|
||||||
|
|
||||||
video_guid = self._html_search_regex(
|
video_guid = self._html_search_regex(
|
||||||
r'<meta property="og:video:url" content="https?://(?:www\.)?newstube\.ru/freshplayer\.swf\?guid=(?P<guid>[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12})',
|
r'<meta\s+property="og:video(?::(?:(?:secure_)?url|iframe))?"\s+content="https?://(?:www\.)?newstube\.ru/embed/(?P<guid>[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12})',
|
||||||
page, 'video GUID')
|
page, 'video GUID')
|
||||||
|
|
||||||
player = self._download_xml(
|
enc_data = base64.b64decode(self._download_webpage(
|
||||||
'http://p.newstube.ru/v2/player.asmx/GetAutoPlayInfo6?state=&url=%s&sessionId=&id=%s&placement=profile&location=n2' % (url, video_guid),
|
'https://www.newstube.ru/embed/api/player/getsources2',
|
||||||
video_guid, 'Downloading player XML')
|
video_guid, query={
|
||||||
|
'guid': video_guid,
|
||||||
def ns(s):
|
'ff': 3,
|
||||||
return s.replace('/', '/%(ns)s') % {'ns': '{http://app1.newstube.ru/N2SiteWS/player.asmx}'}
|
}))
|
||||||
|
key = hashlib.pbkdf2_hmac(
|
||||||
error_message = player.find(ns('./ErrorMessage'))
|
'sha1', video_guid.replace('-', '').encode(), enc_data[:16], 1)[:16]
|
||||||
if error_message is not None:
|
dec_data = aes_cbc_decrypt(
|
||||||
raise ExtractorError('%s returned error: %s' % (self.IE_NAME, error_message.text), expected=True)
|
bytes_to_intlist(enc_data[32:]), bytes_to_intlist(key),
|
||||||
|
bytes_to_intlist(enc_data[16:32]))
|
||||||
session_id = player.find(ns('./SessionId')).text
|
sources = self._parse_json(intlist_to_bytes(dec_data[:-dec_data[-1]]), video_guid)
|
||||||
media_info = player.find(ns('./Medias/MediaInfo'))
|
|
||||||
title = media_info.find(ns('./Name')).text
|
|
||||||
description = self._og_search_description(page)
|
|
||||||
thumbnail = media_info.find(ns('./KeyFrame')).text
|
|
||||||
duration = int(media_info.find(ns('./Duration')).text) / 1000.0
|
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
|
for source in sources:
|
||||||
for stream_info in media_info.findall(ns('./Streams/StreamInfo')):
|
source_url = source.get('Src')
|
||||||
media_location = stream_info.find(ns('./MediaLocation'))
|
if not source_url:
|
||||||
if media_location is None:
|
|
||||||
continue
|
continue
|
||||||
|
height = int_or_none(source.get('Height'))
|
||||||
server = media_location.find(ns('./Server')).text
|
f = {
|
||||||
app = media_location.find(ns('./App')).text
|
'format_id': 'http' + ('-%dp' % height if height else ''),
|
||||||
media_id = stream_info.find(ns('./Id')).text
|
'url': source_url,
|
||||||
name = stream_info.find(ns('./Name')).text
|
'width': int_or_none(source.get('Width')),
|
||||||
width = int(stream_info.find(ns('./Width')).text)
|
|
||||||
height = int(stream_info.find(ns('./Height')).text)
|
|
||||||
|
|
||||||
formats.append({
|
|
||||||
'url': 'rtmp://%s/%s' % (server, app),
|
|
||||||
'app': app,
|
|
||||||
'play_path': '01/%s' % video_guid.upper(),
|
|
||||||
'rtmp_conn': ['S:%s' % session_id, 'S:%s' % media_id, 'S:n2'],
|
|
||||||
'page_url': url,
|
|
||||||
'ext': 'flv',
|
|
||||||
'format_id': 'rtmp' + ('-%s' % name if name else ''),
|
|
||||||
'width': width,
|
|
||||||
'height': height,
|
'height': height,
|
||||||
})
|
}
|
||||||
|
source_type = source.get('Type')
|
||||||
sources_data = self._download_json(
|
if source_type:
|
||||||
'http://www.newstube.ru/player2/getsources?guid=%s' % video_guid,
|
f.update(parse_codecs(self._search_regex(
|
||||||
video_guid, fatal=False)
|
r'codecs="([^"]+)"', source_type, 'codecs', fatal=False)))
|
||||||
if sources_data:
|
formats.append(f)
|
||||||
for source in sources_data.get('Sources', []):
|
|
||||||
source_url = source.get('Src')
|
|
||||||
if not source_url:
|
|
||||||
continue
|
|
||||||
height = int_or_none(source.get('Height'))
|
|
||||||
f = {
|
|
||||||
'format_id': 'http' + ('-%dp' % height if height else ''),
|
|
||||||
'url': source_url,
|
|
||||||
'width': int_or_none(source.get('Width')),
|
|
||||||
'height': height,
|
|
||||||
}
|
|
||||||
source_type = source.get('Type')
|
|
||||||
if source_type:
|
|
||||||
mobj = re.search(r'codecs="([^,]+),\s*([^"]+)"', source_type)
|
|
||||||
if mobj:
|
|
||||||
vcodec, acodec = mobj.groups()
|
|
||||||
f.update({
|
|
||||||
'vcodec': vcodec,
|
|
||||||
'acodec': acodec,
|
|
||||||
})
|
|
||||||
formats.append(f)
|
|
||||||
|
|
||||||
self._check_formats(formats, video_guid)
|
self._check_formats(formats, video_guid)
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
@@ -110,8 +76,8 @@ class NewstubeIE(InfoExtractor):
|
|||||||
return {
|
return {
|
||||||
'id': video_guid,
|
'id': video_guid,
|
||||||
'title': title,
|
'title': title,
|
||||||
'description': description,
|
'description': self._html_search_meta(['description', 'og:description'], page),
|
||||||
'thumbnail': thumbnail,
|
'thumbnail': self._html_search_meta(['og:image:secure_url', 'og:image', 'twitter:image'], page),
|
||||||
'duration': duration,
|
'duration': parse_duration(self._html_search_meta('duration', page)),
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
}
|
}
|
||||||
|
@@ -180,8 +180,8 @@ class AppleDailyIE(NextMediaIE):
|
|||||||
_URL_PATTERN = r'\{url: \'(.+)\'\}'
|
_URL_PATTERN = r'\{url: \'(.+)\'\}'
|
||||||
|
|
||||||
def _fetch_title(self, page):
|
def _fetch_title(self, page):
|
||||||
return (self._html_search_regex(r'<h1 id="h1">([^<>]+)</h1>', page, 'news title', default=None) or
|
return (self._html_search_regex(r'<h1 id="h1">([^<>]+)</h1>', page, 'news title', default=None)
|
||||||
self._html_search_meta('description', page, 'news title'))
|
or self._html_search_meta('description', page, 'news title'))
|
||||||
|
|
||||||
def _fetch_thumbnail(self, page):
|
def _fetch_thumbnail(self, page):
|
||||||
return self._html_search_regex(r"setInitialImage\(\'([^']+)'\)", page, 'video thumbnail', fatal=False)
|
return self._html_search_regex(r"setInitialImage\(\'([^']+)'\)", page, 'video thumbnail', fatal=False)
|
||||||
|
@@ -1,54 +1,81 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import ExtractorError
|
|
||||||
|
|
||||||
|
|
||||||
class NhkVodIE(InfoExtractor):
|
class NhkVodIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://www3\.nhk\.or\.jp/nhkworld/en/(?:vod|ondemand)/(?P<id>[^/]+/[^/?#&]+)'
|
_VALID_URL = r'https?://www3\.nhk\.or\.jp/nhkworld/(?P<lang>[a-z]{2})/ondemand/(?P<type>video|audio)/(?P<id>\d{7}|[a-z]+-\d{8}-\d+)'
|
||||||
|
# Content available only for a limited period of time. Visit
|
||||||
|
# https://www3.nhk.or.jp/nhkworld/en/ondemand/ for working samples.
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
# Videos available only for a limited period of time. Visit
|
|
||||||
# http://www3.nhk.or.jp/nhkworld/en/vod/ for working samples.
|
|
||||||
'url': 'http://www3.nhk.or.jp/nhkworld/en/vod/tokyofashion/20160815',
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'A1bnNiNTE6nY3jLllS-BIISfcC_PpvF5',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'TOKYO FASHION EXPRESS - The Kimono as Global Fashion',
|
|
||||||
'description': 'md5:db338ee6ce8204f415b754782f819824',
|
|
||||||
'series': 'TOKYO FASHION EXPRESS',
|
|
||||||
'episode': 'The Kimono as Global Fashion',
|
|
||||||
},
|
|
||||||
'skip': 'Videos available only for a limited period of time',
|
|
||||||
}, {
|
|
||||||
'url': 'https://www3.nhk.or.jp/nhkworld/en/ondemand/video/2015173/',
|
'url': 'https://www3.nhk.or.jp/nhkworld/en/ondemand/video/2015173/',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www3.nhk.or.jp/nhkworld/en/ondemand/audio/plugin-20190404-1/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www3.nhk.or.jp/nhkworld/fr/ondemand/audio/plugin-20190404-1/',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
_API_URL = 'http://api.nhk.or.jp/nhkworld/vodesdlist/v1/all/all/all.json?apikey=EJfK8jdS57GqlupFgAfAAwr573q01y6k'
|
_API_URL_TEMPLATE = 'https://api.nhk.or.jp/nhkworld/%sodesdlist/v7/episode/%s/%s/all%s.json'
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
lang, m_type, episode_id = re.match(self._VALID_URL, url).groups()
|
||||||
|
if episode_id.isdigit():
|
||||||
data = self._download_json(self._API_URL, video_id)
|
episode_id = episode_id[:4] + '-' + episode_id[4:]
|
||||||
|
|
||||||
try:
|
|
||||||
episode = next(
|
|
||||||
e for e in data['data']['episodes']
|
|
||||||
if e.get('url') and video_id in e['url'])
|
|
||||||
except StopIteration:
|
|
||||||
raise ExtractorError('Unable to find episode')
|
|
||||||
|
|
||||||
embed_code = episode['vod_id']
|
|
||||||
|
|
||||||
|
is_video = m_type == 'video'
|
||||||
|
episode = self._download_json(
|
||||||
|
self._API_URL_TEMPLATE % ('v' if is_video else 'r', episode_id, lang, '/all' if is_video else ''),
|
||||||
|
episode_id, query={'apikey': 'EJfK8jdS57GqlupFgAfAAwr573q01y6k'})['data']['episodes'][0]
|
||||||
title = episode.get('sub_title_clean') or episode['sub_title']
|
title = episode.get('sub_title_clean') or episode['sub_title']
|
||||||
description = episode.get('description_clean') or episode.get('description')
|
|
||||||
series = episode.get('title_clean') or episode.get('title')
|
|
||||||
|
|
||||||
return {
|
def get_clean_field(key):
|
||||||
'_type': 'url_transparent',
|
return episode.get(key + '_clean') or episode.get(key)
|
||||||
'ie_key': 'Ooyala',
|
|
||||||
'url': 'ooyala:%s' % embed_code,
|
series = get_clean_field('title')
|
||||||
|
|
||||||
|
thumbnails = []
|
||||||
|
for s, w, h in [('', 640, 360), ('_l', 1280, 720)]:
|
||||||
|
img_path = episode.get('image' + s)
|
||||||
|
if not img_path:
|
||||||
|
continue
|
||||||
|
thumbnails.append({
|
||||||
|
'id': '%dp' % h,
|
||||||
|
'height': h,
|
||||||
|
'width': w,
|
||||||
|
'url': 'https://www3.nhk.or.jp' + img_path,
|
||||||
|
})
|
||||||
|
|
||||||
|
info = {
|
||||||
|
'id': episode_id + '-' + lang,
|
||||||
'title': '%s - %s' % (series, title) if series and title else title,
|
'title': '%s - %s' % (series, title) if series and title else title,
|
||||||
'description': description,
|
'description': get_clean_field('description'),
|
||||||
|
'thumbnails': thumbnails,
|
||||||
'series': series,
|
'series': series,
|
||||||
'episode': title,
|
'episode': title,
|
||||||
}
|
}
|
||||||
|
if is_video:
|
||||||
|
info.update({
|
||||||
|
'_type': 'url_transparent',
|
||||||
|
'ie_key': 'Ooyala',
|
||||||
|
'url': 'ooyala:' + episode['vod_id'],
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
audio = episode['audio']
|
||||||
|
audio_path = audio['audio']
|
||||||
|
info['formats'] = self._extract_m3u8_formats(
|
||||||
|
'https://nhks-vh.akamaihd.net/i%s/master.m3u8' % audio_path,
|
||||||
|
episode_id, 'm4a', m3u8_id='hls', fatal=False)
|
||||||
|
for proto in ('rtmpt', 'rtmp'):
|
||||||
|
info['formats'].append({
|
||||||
|
'ext': 'flv',
|
||||||
|
'format_id': proto,
|
||||||
|
'url': '%s://flv.nhk.or.jp/ondemand/mp4:flv%s' % (proto, audio_path),
|
||||||
|
'vcodec': 'none',
|
||||||
|
})
|
||||||
|
for f in info['formats']:
|
||||||
|
f['language'] = lang
|
||||||
|
return info
|
||||||
|
@@ -369,14 +369,14 @@ class NiconicoIE(InfoExtractor):
|
|||||||
video_detail = watch_api_data.get('videoDetail', {})
|
video_detail = watch_api_data.get('videoDetail', {})
|
||||||
|
|
||||||
thumbnail = (
|
thumbnail = (
|
||||||
get_video_info(['thumbnail_url', 'thumbnailURL']) or
|
get_video_info(['thumbnail_url', 'thumbnailURL'])
|
||||||
self._html_search_meta('image', webpage, 'thumbnail', default=None) or
|
or self._html_search_meta('image', webpage, 'thumbnail', default=None)
|
||||||
video_detail.get('thumbnail'))
|
or video_detail.get('thumbnail'))
|
||||||
|
|
||||||
description = get_video_info('description')
|
description = get_video_info('description')
|
||||||
|
|
||||||
timestamp = (parse_iso8601(get_video_info('first_retrieve')) or
|
timestamp = (parse_iso8601(get_video_info('first_retrieve'))
|
||||||
unified_timestamp(get_video_info('postedDateTime')))
|
or unified_timestamp(get_video_info('postedDateTime')))
|
||||||
if not timestamp:
|
if not timestamp:
|
||||||
match = self._html_search_meta('datePublished', webpage, 'date published', default=None)
|
match = self._html_search_meta('datePublished', webpage, 'date published', default=None)
|
||||||
if match:
|
if match:
|
||||||
@@ -395,9 +395,9 @@ class NiconicoIE(InfoExtractor):
|
|||||||
view_count = int_or_none(match.replace(',', ''))
|
view_count = int_or_none(match.replace(',', ''))
|
||||||
view_count = view_count or video_detail.get('viewCount')
|
view_count = view_count or video_detail.get('viewCount')
|
||||||
|
|
||||||
comment_count = (int_or_none(get_video_info('comment_num')) or
|
comment_count = (int_or_none(get_video_info('comment_num'))
|
||||||
video_detail.get('commentCount') or
|
or video_detail.get('commentCount')
|
||||||
try_get(api_data, lambda x: x['thread']['commentCount']))
|
or try_get(api_data, lambda x: x['thread']['commentCount']))
|
||||||
if not comment_count:
|
if not comment_count:
|
||||||
match = self._html_search_regex(
|
match = self._html_search_regex(
|
||||||
r'>Comments: <strong[^>]*>([^<]+)</strong>',
|
r'>Comments: <strong[^>]*>([^<]+)</strong>',
|
||||||
@@ -406,11 +406,11 @@ class NiconicoIE(InfoExtractor):
|
|||||||
comment_count = int_or_none(match.replace(',', ''))
|
comment_count = int_or_none(match.replace(',', ''))
|
||||||
|
|
||||||
duration = (parse_duration(
|
duration = (parse_duration(
|
||||||
get_video_info('length') or
|
get_video_info('length')
|
||||||
self._html_search_meta(
|
or self._html_search_meta(
|
||||||
'video:duration', webpage, 'video duration', default=None)) or
|
'video:duration', webpage, 'video duration', default=None))
|
||||||
video_detail.get('length') or
|
or video_detail.get('length')
|
||||||
get_video_info('duration'))
|
or get_video_info('duration'))
|
||||||
|
|
||||||
webpage_url = get_video_info('watch_url') or url
|
webpage_url = get_video_info('watch_url') or url
|
||||||
|
|
||||||
|
@@ -1,212 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
from .common import InfoExtractor
|
|
||||||
from ..compat import compat_urlparse
|
|
||||||
from ..utils import (
|
|
||||||
ExtractorError,
|
|
||||||
NO_DEFAULT,
|
|
||||||
sanitized_Request,
|
|
||||||
urlencode_postdata,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NovaMovIE(InfoExtractor):
|
|
||||||
IE_NAME = 'novamov'
|
|
||||||
IE_DESC = 'NovaMov'
|
|
||||||
|
|
||||||
_VALID_URL_TEMPLATE = r'''(?x)
|
|
||||||
http://
|
|
||||||
(?:
|
|
||||||
(?:www\.)?%(host)s/(?:file|video|mobile/\#/videos)/|
|
|
||||||
(?:(?:embed|www)\.)%(host)s/embed(?:\.php|/)?\?(?:.*?&)?\bv=
|
|
||||||
)
|
|
||||||
(?P<id>[a-z\d]{13})
|
|
||||||
'''
|
|
||||||
_VALID_URL = _VALID_URL_TEMPLATE % {'host': r'novamov\.com'}
|
|
||||||
|
|
||||||
_HOST = 'www.novamov.com'
|
|
||||||
|
|
||||||
_FILE_DELETED_REGEX = r'This file no longer exists on our servers!</h2>'
|
|
||||||
_FILEKEY_REGEX = r'flashvars\.filekey=(?P<filekey>"?[^"]+"?);'
|
|
||||||
_TITLE_REGEX = r'(?s)<div class="v_tab blockborder rounded5" id="v_tab1">\s*<h3>([^<]+)</h3>'
|
|
||||||
_DESCRIPTION_REGEX = r'(?s)<div class="v_tab blockborder rounded5" id="v_tab1">\s*<h3>[^<]+</h3><p>([^<]+)</p>'
|
|
||||||
_URL_TEMPLATE = 'http://%s/video/%s'
|
|
||||||
|
|
||||||
_TEST = None
|
|
||||||
|
|
||||||
def _check_existence(self, webpage, video_id):
|
|
||||||
if re.search(self._FILE_DELETED_REGEX, webpage) is not None:
|
|
||||||
raise ExtractorError('Video %s does not exist' % video_id, expected=True)
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
|
||||||
video_id = self._match_id(url)
|
|
||||||
|
|
||||||
url = self._URL_TEMPLATE % (self._HOST, video_id)
|
|
||||||
|
|
||||||
webpage = self._download_webpage(
|
|
||||||
url, video_id, 'Downloading video page')
|
|
||||||
|
|
||||||
self._check_existence(webpage, video_id)
|
|
||||||
|
|
||||||
def extract_filekey(default=NO_DEFAULT):
|
|
||||||
filekey = self._search_regex(
|
|
||||||
self._FILEKEY_REGEX, webpage, 'filekey', default=default)
|
|
||||||
if filekey is not default and (filekey[0] != '"' or filekey[-1] != '"'):
|
|
||||||
return self._search_regex(
|
|
||||||
r'var\s+%s\s*=\s*"([^"]+)"' % re.escape(filekey), webpage, 'filekey', default=default)
|
|
||||||
else:
|
|
||||||
return filekey
|
|
||||||
|
|
||||||
filekey = extract_filekey(default=None)
|
|
||||||
|
|
||||||
if not filekey:
|
|
||||||
fields = self._hidden_inputs(webpage)
|
|
||||||
post_url = self._search_regex(
|
|
||||||
r'<form[^>]+action=(["\'])(?P<url>.+?)\1', webpage,
|
|
||||||
'post url', default=url, group='url')
|
|
||||||
if not post_url.startswith('http'):
|
|
||||||
post_url = compat_urlparse.urljoin(url, post_url)
|
|
||||||
request = sanitized_Request(
|
|
||||||
post_url, urlencode_postdata(fields))
|
|
||||||
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
|
||||||
request.add_header('Referer', post_url)
|
|
||||||
webpage = self._download_webpage(
|
|
||||||
request, video_id, 'Downloading continue to the video page')
|
|
||||||
self._check_existence(webpage, video_id)
|
|
||||||
|
|
||||||
filekey = extract_filekey()
|
|
||||||
|
|
||||||
title = self._html_search_regex(self._TITLE_REGEX, webpage, 'title')
|
|
||||||
description = self._html_search_regex(self._DESCRIPTION_REGEX, webpage, 'description', default='', fatal=False)
|
|
||||||
|
|
||||||
api_response = self._download_webpage(
|
|
||||||
'http://%s/api/player.api.php?key=%s&file=%s' % (self._HOST, filekey, video_id), video_id,
|
|
||||||
'Downloading video api response')
|
|
||||||
|
|
||||||
response = compat_urlparse.parse_qs(api_response)
|
|
||||||
|
|
||||||
if 'error_msg' in response:
|
|
||||||
raise ExtractorError('%s returned error: %s' % (self.IE_NAME, response['error_msg'][0]), expected=True)
|
|
||||||
|
|
||||||
video_url = response['url'][0]
|
|
||||||
|
|
||||||
return {
|
|
||||||
'id': video_id,
|
|
||||||
'url': video_url,
|
|
||||||
'title': title,
|
|
||||||
'description': description
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class WholeCloudIE(NovaMovIE):
|
|
||||||
IE_NAME = 'wholecloud'
|
|
||||||
IE_DESC = 'WholeCloud'
|
|
||||||
|
|
||||||
_VALID_URL = NovaMovIE._VALID_URL_TEMPLATE % {'host': r'(?:wholecloud\.net|movshare\.(?:net|sx|ag))'}
|
|
||||||
|
|
||||||
_HOST = 'www.wholecloud.net'
|
|
||||||
|
|
||||||
_FILE_DELETED_REGEX = r'>This file no longer exists on our servers.<'
|
|
||||||
_TITLE_REGEX = r'<strong>Title:</strong> ([^<]+)</p>'
|
|
||||||
_DESCRIPTION_REGEX = r'<strong>Description:</strong> ([^<]+)</p>'
|
|
||||||
|
|
||||||
_TEST = {
|
|
||||||
'url': 'http://www.wholecloud.net/video/559e28be54d96',
|
|
||||||
'md5': 'abd31a2132947262c50429e1d16c1bfd',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '559e28be54d96',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'dissapeared image',
|
|
||||||
'description': 'optical illusion dissapeared image magic illusion',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class NowVideoIE(NovaMovIE):
|
|
||||||
IE_NAME = 'nowvideo'
|
|
||||||
IE_DESC = 'NowVideo'
|
|
||||||
|
|
||||||
_VALID_URL = NovaMovIE._VALID_URL_TEMPLATE % {'host': r'nowvideo\.(?:to|ch|ec|sx|eu|at|ag|co|li)'}
|
|
||||||
|
|
||||||
_HOST = 'www.nowvideo.to'
|
|
||||||
|
|
||||||
_FILE_DELETED_REGEX = r'>This file no longer exists on our servers.<'
|
|
||||||
_TITLE_REGEX = r'<h4>([^<]+)</h4>'
|
|
||||||
_DESCRIPTION_REGEX = r'</h4>\s*<p>([^<]+)</p>'
|
|
||||||
|
|
||||||
_TEST = {
|
|
||||||
'url': 'http://www.nowvideo.sx/video/f1d6fce9a968b',
|
|
||||||
'md5': '12c82cad4f2084881d8bc60ee29df092',
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'f1d6fce9a968b',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'youtubedl test video BaWjenozKc',
|
|
||||||
'description': 'Description',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class VideoWeedIE(NovaMovIE):
|
|
||||||
IE_NAME = 'videoweed'
|
|
||||||
IE_DESC = 'VideoWeed'
|
|
||||||
|
|
||||||
_VALID_URL = NovaMovIE._VALID_URL_TEMPLATE % {'host': r'videoweed\.(?:es|com)'}
|
|
||||||
|
|
||||||
_HOST = 'www.videoweed.es'
|
|
||||||
|
|
||||||
_FILE_DELETED_REGEX = r'>This file no longer exists on our servers.<'
|
|
||||||
_TITLE_REGEX = r'<h1 class="text_shadow">([^<]+)</h1>'
|
|
||||||
_URL_TEMPLATE = 'http://%s/file/%s'
|
|
||||||
|
|
||||||
_TEST = {
|
|
||||||
'url': 'http://www.videoweed.es/file/b42178afbea14',
|
|
||||||
'md5': 'abd31a2132947262c50429e1d16c1bfd',
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'b42178afbea14',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'optical illusion dissapeared image magic illusion',
|
|
||||||
'description': ''
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class CloudTimeIE(NovaMovIE):
|
|
||||||
IE_NAME = 'cloudtime'
|
|
||||||
IE_DESC = 'CloudTime'
|
|
||||||
|
|
||||||
_VALID_URL = NovaMovIE._VALID_URL_TEMPLATE % {'host': r'cloudtime\.to'}
|
|
||||||
|
|
||||||
_HOST = 'www.cloudtime.to'
|
|
||||||
|
|
||||||
_FILE_DELETED_REGEX = r'>This file no longer exists on our servers.<'
|
|
||||||
_TITLE_REGEX = r'<div[^>]+class=["\']video_det["\'][^>]*>\s*<strong>([^<]+)</strong>'
|
|
||||||
|
|
||||||
_TEST = None
|
|
||||||
|
|
||||||
|
|
||||||
class AuroraVidIE(NovaMovIE):
|
|
||||||
IE_NAME = 'auroravid'
|
|
||||||
IE_DESC = 'AuroraVid'
|
|
||||||
|
|
||||||
_VALID_URL = NovaMovIE._VALID_URL_TEMPLATE % {'host': r'auroravid\.to'}
|
|
||||||
|
|
||||||
_HOST = 'www.auroravid.to'
|
|
||||||
|
|
||||||
_FILE_DELETED_REGEX = r'This file no longer exists on our servers!<'
|
|
||||||
|
|
||||||
_TESTS = [{
|
|
||||||
'url': 'http://www.auroravid.to/video/4rurhn9x446jj',
|
|
||||||
'md5': '7205f346a52bbeba427603ba10d4b935',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '4rurhn9x446jj',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'search engine optimization',
|
|
||||||
'description': 'search engine optimization is used to rank the web page in the google search engine'
|
|
||||||
},
|
|
||||||
'skip': '"Invalid token" errors abound (in web interface as well as youtube-dl, there is nothing we can do about it.)'
|
|
||||||
}, {
|
|
||||||
'url': 'http://www.auroravid.to/embed/?v=4rurhn9x446jj',
|
|
||||||
'only_matching': True,
|
|
||||||
}]
|
|
@@ -181,10 +181,7 @@ class NPOIE(NPOBaseIE):
|
|||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
try:
|
return self._get_info(url, video_id) or self._get_old_info(video_id)
|
||||||
return self._get_info(url, video_id)
|
|
||||||
except ExtractorError:
|
|
||||||
return self._get_old_info(video_id)
|
|
||||||
|
|
||||||
def _get_info(self, url, video_id):
|
def _get_info(self, url, video_id):
|
||||||
token = self._download_json(
|
token = self._download_json(
|
||||||
@@ -206,6 +203,7 @@ class NPOIE(NPOBaseIE):
|
|||||||
|
|
||||||
player_token = player['token']
|
player_token = player['token']
|
||||||
|
|
||||||
|
drm = False
|
||||||
format_urls = set()
|
format_urls = set()
|
||||||
formats = []
|
formats = []
|
||||||
for profile in ('hls', 'dash-widevine', 'dash-playready', 'smooth'):
|
for profile in ('hls', 'dash-widevine', 'dash-playready', 'smooth'):
|
||||||
@@ -227,7 +225,8 @@ class NPOIE(NPOBaseIE):
|
|||||||
if not stream_url or stream_url in format_urls:
|
if not stream_url or stream_url in format_urls:
|
||||||
continue
|
continue
|
||||||
format_urls.add(stream_url)
|
format_urls.add(stream_url)
|
||||||
if stream.get('protection') is not None:
|
if stream.get('protection') is not None or stream.get('keySystemOptions') is not None:
|
||||||
|
drm = True
|
||||||
continue
|
continue
|
||||||
stream_type = stream.get('type')
|
stream_type = stream.get('type')
|
||||||
stream_ext = determine_ext(stream_url)
|
stream_ext = determine_ext(stream_url)
|
||||||
@@ -246,6 +245,11 @@ class NPOIE(NPOBaseIE):
|
|||||||
'url': stream_url,
|
'url': stream_url,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if not formats:
|
||||||
|
if drm:
|
||||||
|
raise ExtractorError('This video is DRM protected.', expected=True)
|
||||||
|
return
|
||||||
|
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
info = {
|
info = {
|
||||||
|
@@ -45,8 +45,8 @@ class NRKBaseIE(InfoExtractor):
|
|||||||
entries = []
|
entries = []
|
||||||
|
|
||||||
conviva = data.get('convivaStatistics') or {}
|
conviva = data.get('convivaStatistics') or {}
|
||||||
live = (data.get('mediaElementType') == 'Live' or
|
live = (data.get('mediaElementType') == 'Live'
|
||||||
data.get('isLive') is True or conviva.get('isLive'))
|
or data.get('isLive') is True or conviva.get('isLive'))
|
||||||
|
|
||||||
def make_title(t):
|
def make_title(t):
|
||||||
return self._live_title(t) if live else t
|
return self._live_title(t) if live else t
|
||||||
|
30
youtube_dl/extractor/nrl.py
Normal file
30
youtube_dl/extractor/nrl.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
|
||||||
|
|
||||||
|
class NRLTVIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?nrl\.com/tv(/[^/]+)*/(?P<id>[^/?&#]+)'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'https://www.nrl.com/tv/news/match-highlights-titans-v-knights-862805/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'YyNnFuaDE6kPJqlDhG4CGQ_w89mKTau4',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Match Highlights: Titans v Knights',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
|
'format': 'bestvideo',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
q_data = self._parse_json(self._search_regex(
|
||||||
|
r"(?s)q-data='({.+?})'", webpage, 'player data'), display_id)
|
||||||
|
ooyala_id = q_data['videoId']
|
||||||
|
return self.url_result(
|
||||||
|
'ooyala:' + ooyala_id, 'Ooyala', ooyala_id, q_data.get('title'))
|
49
youtube_dl/extractor/ntvcojp.py
Normal file
49
youtube_dl/extractor/ntvcojp.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
js_to_json,
|
||||||
|
smuggle_url,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NTVCoJpCUIE(InfoExtractor):
|
||||||
|
IE_NAME = 'cu.ntv.co.jp'
|
||||||
|
IE_DESC = 'Nippon Television Network'
|
||||||
|
_VALID_URL = r'https?://cu\.ntv\.co\.jp/(?!program)(?P<id>[^/?&#]+)'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'https://cu.ntv.co.jp/televiva-chill-gohan_181031/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '5978891207001',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': '桜エビと炒り卵がポイント! 「中華風 エビチリおにぎり」──『美虎』五十嵐美幸',
|
||||||
|
'upload_date': '20181213',
|
||||||
|
'description': 'md5:211b52f4fd60f3e0e72b68b0c6ba52a9',
|
||||||
|
'uploader_id': '3855502814001',
|
||||||
|
'timestamp': 1544669941,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/%s/default_default/index.html?videoId=%s'
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
player_config = self._parse_json(self._search_regex(
|
||||||
|
r'(?s)PLAYER_CONFIG\s*=\s*({.+?})',
|
||||||
|
webpage, 'player config'), display_id, js_to_json)
|
||||||
|
video_id = player_config['videoId']
|
||||||
|
account_id = player_config.get('account') or '3855502814001'
|
||||||
|
return {
|
||||||
|
'_type': 'url_transparent',
|
||||||
|
'id': video_id,
|
||||||
|
'display_id': display_id,
|
||||||
|
'title': self._search_regex(r'<h1[^>]+class="title"[^>]*>([^<]+)', webpage, 'title').strip(),
|
||||||
|
'description': self._html_search_meta(['description', 'og:description'], webpage),
|
||||||
|
'url': smuggle_url(self.BRIGHTCOVE_URL_TEMPLATE % (account_id, video_id), {'geo_countries': ['JP']}),
|
||||||
|
'ie_key': 'BrightcoveNew',
|
||||||
|
}
|
@@ -31,12 +31,12 @@ class OoyalaBaseIE(InfoExtractor):
|
|||||||
title = metadata['title']
|
title = metadata['title']
|
||||||
|
|
||||||
auth_data = self._download_json(
|
auth_data = self._download_json(
|
||||||
self._AUTHORIZATION_URL_TEMPLATE % (pcode, embed_code) +
|
self._AUTHORIZATION_URL_TEMPLATE % (pcode, embed_code)
|
||||||
compat_urllib_parse_urlencode({
|
+ compat_urllib_parse_urlencode({
|
||||||
'domain': domain,
|
'domain': domain,
|
||||||
'supportedFormats': supportedformats or 'mp4,rtmp,m3u8,hds,dash,smooth',
|
'supportedFormats': supportedformats or 'mp4,rtmp,m3u8,hds,dash,smooth',
|
||||||
'embedToken': embed_token,
|
'embedToken': embed_token,
|
||||||
}), video_id)
|
}), video_id, headers=self.geo_verification_headers())
|
||||||
|
|
||||||
cur_auth_data = auth_data['authorization_data'][embed_code]
|
cur_auth_data = auth_data['authorization_data'][embed_code]
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user