mirror of
https://github.com/l1ving/youtube-dl
synced 2020-11-18 19:53:54 -08:00
Merge branch 'master' into Soundcloud-Thumbnail
This commit is contained in:
commit
0e88aab901
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.04.07*. 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.04.07**
|
|
||||||
|
|
||||||
### 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.04.07
|
|
||||||
[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.07.02. 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.07.02**
|
||||||
|
- [ ] 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.07.02
|
||||||
|
[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.07.02. 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.07.02**
|
||||||
|
- [ ] 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.07.02. 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.07.02**
|
||||||
|
- [ ] 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.07.02. 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.07.02**
|
||||||
|
- [ ] 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.07.02
|
||||||
|
[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.07.02. 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.07.02**
|
||||||
|
- [ ] 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
|
||||||
|
201
ChangeLog
201
ChangeLog
@ -1,3 +1,204 @@
|
|||||||
|
version 2019.07.02
|
||||||
|
|
||||||
|
Core
|
||||||
|
+ [utils] Introduce random_user_agent and use as default User-Agent (#21546)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [vevo] Add support for embed.vevo.com URLs (#21565)
|
||||||
|
+ [openload] Add support for oload.biz (#21574)
|
||||||
|
* [xiami] Update API base URL (#21575)
|
||||||
|
* [yourporn] Fix extraction (#21585)
|
||||||
|
+ [acast] Add support for URLs with episode id (#21444)
|
||||||
|
+ [dailymotion] Add support for DM.player embeds
|
||||||
|
* [soundcloud] Update client id
|
||||||
|
|
||||||
|
|
||||||
|
version 2019.06.27
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [go] Add support for disneynow.com (#21528)
|
||||||
|
* [mixer:vod] Relax URL regular expression (#21531, #21536)
|
||||||
|
* [drtv] Relax URL regular expression
|
||||||
|
* [fusion] Fix extraction (#17775, #21269)
|
||||||
|
- [nfb] Remove extractor (#21518)
|
||||||
|
+ [beeg] Add support for api/v6 v2 URLs (#21511)
|
||||||
|
+ [brightcove:new] Add support for playlists (#21331)
|
||||||
|
+ [openload] Add support for oload.life (#21495)
|
||||||
|
* [vimeo:channel,group] Make title extraction non fatal
|
||||||
|
* [vimeo:likes] Implement extrator in terms of channel extractor (#21493)
|
||||||
|
+ [pornhub] Add support for more paged video sources
|
||||||
|
+ [pornhub] Add support for downloading single pages and search pages (#15570)
|
||||||
|
* [pornhub] Rework extractors (#11922, #16078, #17454, #17936)
|
||||||
|
+ [youtube] Add another signature function pattern
|
||||||
|
* [tf1] Fix extraction (#21365, #21372)
|
||||||
|
* [crunchyroll] Move Accept-Language workaround to video extractor since
|
||||||
|
it causes playlists not to list any videos
|
||||||
|
* [crunchyroll:playlist] Fix and relax title extraction (#21291, #21443)
|
||||||
|
|
||||||
|
|
||||||
|
version 2019.06.21
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [utils] Restrict parse_codecs and add theora as known vcodec (#21381)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [youtube] Update signature function patterns (#21469, #21476)
|
||||||
|
* [youtube] Make --write-annotations non fatal (#21452)
|
||||||
|
+ [sixplay] Add support for rtlmost.hu (#21405)
|
||||||
|
* [youtube] Hardcode codec metadata for av01 video only formats (#21381)
|
||||||
|
* [toutv] Update client key (#21370)
|
||||||
|
+ [biqle] Add support for new embed domain
|
||||||
|
* [cbs] Improve DRM protected videos detection (#21339)
|
||||||
|
|
||||||
|
|
||||||
|
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
|
version 2019.04.07
|
||||||
|
|
||||||
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
|
||||||
|
@ -700,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..."
|
||||||
|
@ -46,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**
|
||||||
@ -77,7 +78,6 @@
|
|||||||
- **AudioBoom**
|
- **AudioBoom**
|
||||||
- **audiomack**
|
- **audiomack**
|
||||||
- **audiomack:album**
|
- **audiomack:album**
|
||||||
- **auroravid**: AuroraVid
|
|
||||||
- **AWAAN**
|
- **AWAAN**
|
||||||
- **awaan:live**
|
- **awaan:live**
|
||||||
- **awaan:season**
|
- **awaan:season**
|
||||||
@ -149,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**
|
||||||
@ -163,6 +164,7 @@
|
|||||||
- **chirbit**
|
- **chirbit**
|
||||||
- **chirbit:profile**
|
- **chirbit:profile**
|
||||||
- **Cinchcast**
|
- **Cinchcast**
|
||||||
|
- **Cinemax**
|
||||||
- **CiscoLiveSearch**
|
- **CiscoLiveSearch**
|
||||||
- **CiscoLiveSession**
|
- **CiscoLiveSession**
|
||||||
- **CJSW**
|
- **CJSW**
|
||||||
@ -172,7 +174,6 @@
|
|||||||
- **Clipsyndicate**
|
- **Clipsyndicate**
|
||||||
- **CloserToTruth**
|
- **CloserToTruth**
|
||||||
- **CloudflareStream**
|
- **CloudflareStream**
|
||||||
- **cloudtime**: CloudTime
|
|
||||||
- **Cloudy**
|
- **Cloudy**
|
||||||
- **Clubic**
|
- **Clubic**
|
||||||
- **Clyp**
|
- **Clyp**
|
||||||
@ -192,7 +193,6 @@
|
|||||||
- **Coub**
|
- **Coub**
|
||||||
- **Cracked**
|
- **Cracked**
|
||||||
- **Crackle**
|
- **Crackle**
|
||||||
- **Criterion**
|
|
||||||
- **CrooksAndLiars**
|
- **CrooksAndLiars**
|
||||||
- **crunchyroll**
|
- **crunchyroll**
|
||||||
- **crunchyroll:playlist**
|
- **crunchyroll:playlist**
|
||||||
@ -200,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**
|
||||||
@ -235,8 +236,6 @@
|
|||||||
- **DouyuTV**: 斗鱼
|
- **DouyuTV**: 斗鱼
|
||||||
- **DPlay**
|
- **DPlay**
|
||||||
- **DPlayIt**
|
- **DPlayIt**
|
||||||
- **dramafever**
|
|
||||||
- **dramafever:series**
|
|
||||||
- **DRBonanza**
|
- **DRBonanza**
|
||||||
- **Dropbox**
|
- **Dropbox**
|
||||||
- **DrTuber**
|
- **DrTuber**
|
||||||
@ -486,6 +485,7 @@
|
|||||||
- **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**
|
||||||
@ -581,7 +581,6 @@
|
|||||||
- **NextTV**: 壹電視
|
- **NextTV**: 壹電視
|
||||||
- **Nexx**
|
- **Nexx**
|
||||||
- **NexxEmbed**
|
- **NexxEmbed**
|
||||||
- **nfb**: National Film Board of Canada
|
|
||||||
- **nfl.com**
|
- **nfl.com**
|
||||||
- **NhkVod**
|
- **NhkVod**
|
||||||
- **nhl.com**
|
- **nhl.com**
|
||||||
@ -607,7 +606,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**
|
||||||
@ -623,6 +621,7 @@
|
|||||||
- **NRKTVEpisodes**
|
- **NRKTVEpisodes**
|
||||||
- **NRKTVSeason**
|
- **NRKTVSeason**
|
||||||
- **NRKTVSeries**
|
- **NRKTVSeries**
|
||||||
|
- **NRLTV**
|
||||||
- **ntv.ru**
|
- **ntv.ru**
|
||||||
- **Nuvid**
|
- **Nuvid**
|
||||||
- **NYTimes**
|
- **NYTimes**
|
||||||
@ -632,7 +631,6 @@
|
|||||||
- **OdaTV**
|
- **OdaTV**
|
||||||
- **Odnoklassniki**
|
- **Odnoklassniki**
|
||||||
- **OktoberfestTV**
|
- **OktoberfestTV**
|
||||||
- **on.aol.com**
|
|
||||||
- **OnDemandKorea**
|
- **OnDemandKorea**
|
||||||
- **onet.pl**
|
- **onet.pl**
|
||||||
- **onet.tv**
|
- **onet.tv**
|
||||||
@ -691,11 +689,11 @@
|
|||||||
- **PopcornTV**
|
- **PopcornTV**
|
||||||
- **PornCom**
|
- **PornCom**
|
||||||
- **PornerBros**
|
- **PornerBros**
|
||||||
- **PornFlip**
|
|
||||||
- **PornHd**
|
- **PornHd**
|
||||||
- **PornHub**: PornHub and Thumbzilla
|
- **PornHub**: PornHub and Thumbzilla
|
||||||
- **PornHubPlaylist**
|
- **PornHubPagedVideoList**
|
||||||
- **PornHubUserVideos**
|
- **PornHubUser**
|
||||||
|
- **PornHubUserVideosUpload**
|
||||||
- **Pornotube**
|
- **Pornotube**
|
||||||
- **PornoVoisines**
|
- **PornoVoisines**
|
||||||
- **PornoXO**
|
- **PornoXO**
|
||||||
@ -732,6 +730,7 @@
|
|||||||
- **RBMARadio**
|
- **RBMARadio**
|
||||||
- **RDS**: RDS.ca
|
- **RDS**: RDS.ca
|
||||||
- **RedBullTV**
|
- **RedBullTV**
|
||||||
|
- **RedBullTVRrnContent**
|
||||||
- **Reddit**
|
- **Reddit**
|
||||||
- **RedditR**
|
- **RedditR**
|
||||||
- **RedTube**
|
- **RedTube**
|
||||||
@ -803,6 +802,7 @@
|
|||||||
- **ShowRoomLive**
|
- **ShowRoomLive**
|
||||||
- **Sina**
|
- **Sina**
|
||||||
- **SkylineWebcams**
|
- **SkylineWebcams**
|
||||||
|
- **SkyNews**
|
||||||
- **skynewsarabia:article**
|
- **skynewsarabia:article**
|
||||||
- **skynewsarabia:video**
|
- **skynewsarabia:video**
|
||||||
- **SkySports**
|
- **SkySports**
|
||||||
@ -853,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
|
||||||
@ -994,6 +997,7 @@
|
|||||||
- **Vbox7**
|
- **Vbox7**
|
||||||
- **VeeHD**
|
- **VeeHD**
|
||||||
- **Veoh**
|
- **Veoh**
|
||||||
|
- **verystream**
|
||||||
- **Vessel**
|
- **Vessel**
|
||||||
- **Vesti**: Вести.Ru
|
- **Vesti**: Вести.Ru
|
||||||
- **Vevo**
|
- **Vevo**
|
||||||
@ -1017,7 +1021,6 @@
|
|||||||
- **videomore:video**
|
- **videomore:video**
|
||||||
- **VideoPremium**
|
- **VideoPremium**
|
||||||
- **VideoPress**
|
- **VideoPress**
|
||||||
- **videoweed**: VideoWeed
|
|
||||||
- **Vidio**
|
- **Vidio**
|
||||||
- **VidLii**
|
- **VidLii**
|
||||||
- **vidme**
|
- **vidme**
|
||||||
@ -1064,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**
|
||||||
@ -1094,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**
|
||||||
@ -1124,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([
|
||||||
|
@ -73,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,
|
||||||
@ -183,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')
|
||||||
@ -752,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'), '𝕐')
|
||||||
@ -809,6 +822,15 @@ class TestUtil(unittest.TestCase):
|
|||||||
'vcodec': 'av01.0.05M.08',
|
'vcodec': 'av01.0.05M.08',
|
||||||
'acodec': 'none',
|
'acodec': 'none',
|
||||||
})
|
})
|
||||||
|
self.assertEqual(parse_codecs('theora, vorbis'), {
|
||||||
|
'vcodec': 'theora',
|
||||||
|
'acodec': 'vorbis',
|
||||||
|
})
|
||||||
|
self.assertEqual(parse_codecs('unknownvcodec, unknownacodec'), {
|
||||||
|
'vcodec': 'unknownvcodec',
|
||||||
|
'acodec': 'unknownacodec',
|
||||||
|
})
|
||||||
|
self.assertEqual(parse_codecs('unknown'), {})
|
||||||
|
|
||||||
def test_escape_rfc3986(self):
|
def test_escape_rfc3986(self):
|
||||||
reserved = "!*'();:@&=+$,/?#[]"
|
reserved = "!*'();:@&=+$,/?#[]"
|
||||||
|
@ -400,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 '
|
||||||
@ -440,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. '
|
||||||
@ -850,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
|
||||||
@ -1619,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,
|
||||||
@ -1947,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. '
|
||||||
@ -1967,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']))
|
||||||
@ -1995,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:
|
||||||
@ -2143,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']
|
||||||
|
@ -230,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
|
||||||
|
@ -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,
|
||||||
|
@ -7,6 +7,7 @@ import functools
|
|||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_str
|
from ..compat import compat_str
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
clean_html,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
try_get,
|
try_get,
|
||||||
@ -27,7 +28,7 @@ class ACastIE(InfoExtractor):
|
|||||||
'''
|
'''
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'https://www.acast.com/sparpodcast/2.raggarmordet-rosterurdetforflutna',
|
'url': 'https://www.acast.com/sparpodcast/2.raggarmordet-rosterurdetforflutna',
|
||||||
'md5': 'a02393c74f3bdb1801c3ec2695577ce0',
|
'md5': '16d936099ec5ca2d5869e3a813ee8dc4',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '2a92b283-1a75-4ad8-8396-499c641de0d9',
|
'id': '2a92b283-1a75-4ad8-8396-499c641de0d9',
|
||||||
'ext': 'mp3',
|
'ext': 'mp3',
|
||||||
@ -46,28 +47,37 @@ class ACastIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'https://play.acast.com/s/rattegangspodden/s04e09-styckmordet-i-helenelund-del-22',
|
'url': 'https://play.acast.com/s/rattegangspodden/s04e09-styckmordet-i-helenelund-del-22',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://play.acast.com/s/sparpodcast/2a92b283-1a75-4ad8-8396-499c641de0d9',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
channel, display_id = re.match(self._VALID_URL, url).groups()
|
channel, display_id = re.match(self._VALID_URL, url).groups()
|
||||||
s = self._download_json(
|
s = self._download_json(
|
||||||
'https://play-api.acast.com/stitch/%s/%s' % (channel, display_id),
|
'https://feeder.acast.com/api/v1/shows/%s/episodes/%s' % (channel, display_id),
|
||||||
display_id)['result']
|
display_id)
|
||||||
media_url = s['url']
|
media_url = s['url']
|
||||||
|
if re.search(r'[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}', display_id):
|
||||||
|
episode_url = s.get('episodeUrl')
|
||||||
|
if episode_url:
|
||||||
|
display_id = episode_url
|
||||||
|
else:
|
||||||
|
channel, display_id = re.match(self._VALID_URL, s['link']).groups()
|
||||||
cast_data = self._download_json(
|
cast_data = self._download_json(
|
||||||
'https://play-api.acast.com/splash/%s/%s' % (channel, display_id),
|
'https://play-api.acast.com/splash/%s/%s' % (channel, display_id),
|
||||||
display_id)['result']
|
display_id)['result']
|
||||||
e = cast_data['episode']
|
e = cast_data['episode']
|
||||||
title = e['name']
|
title = e.get('name') or s['title']
|
||||||
return {
|
return {
|
||||||
'id': compat_str(e['id']),
|
'id': compat_str(e['id']),
|
||||||
'display_id': display_id,
|
'display_id': display_id,
|
||||||
'url': media_url,
|
'url': media_url,
|
||||||
'title': title,
|
'title': title,
|
||||||
'description': e.get('description') or e.get('summary'),
|
'description': e.get('summary') or clean_html(e.get('description') or s.get('description')),
|
||||||
'thumbnail': e.get('image'),
|
'thumbnail': e.get('image'),
|
||||||
'timestamp': unified_timestamp(e.get('publishingDate')),
|
'timestamp': unified_timestamp(e.get('publishingDate') or s.get('publishDate')),
|
||||||
'duration': float_or_none(s.get('duration') or e.get('duration')),
|
'duration': float_or_none(e.get('duration') or s.get('duration')),
|
||||||
'filesize': int_or_none(e.get('contentLength')),
|
'filesize': int_or_none(e.get('contentLength')),
|
||||||
'creator': try_get(cast_data, lambda x: x['show']['author'], compat_str),
|
'creator': try_get(cast_data, lambda x: x['show']['author'], compat_str),
|
||||||
'series': try_get(cast_data, lambda x: x['show']['name'], compat_str),
|
'series': try_get(cast_data, lambda x: x['show']['name'], compat_str),
|
||||||
|
@ -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,
|
||||||
|
@ -60,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, 'Downloading subtitles data', 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 + '083db5aebd9353b4')),
|
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(
|
||||||
|
@ -25,6 +25,11 @@ MSO_INFO = {
|
|||||||
'username_field': 'username',
|
'username_field': 'username',
|
||||||
'password_field': 'password',
|
'password_field': 'password',
|
||||||
},
|
},
|
||||||
|
'ATT': {
|
||||||
|
'name': 'AT&T U-verse',
|
||||||
|
'username_field': 'userid',
|
||||||
|
'password_field': 'password',
|
||||||
|
},
|
||||||
'ATTOTT': {
|
'ATTOTT': {
|
||||||
'name': 'DIRECTV NOW',
|
'name': 'DIRECTV NOW',
|
||||||
'username_field': 'email',
|
'username_field': 'email',
|
||||||
|
@ -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))
|
|
||||||
|
@ -17,7 +17,7 @@ from ..utils import (
|
|||||||
|
|
||||||
class AolIE(InfoExtractor):
|
class AolIE(InfoExtractor):
|
||||||
IE_NAME = 'aol.com'
|
IE_NAME = 'aol.com'
|
||||||
_VALID_URL = r'(?:aol-video:|https?://(?:www\.)?aol\.com/video/(?:[^/]+/)*)(?P<id>[0-9a-f]+)'
|
_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
|
||||||
@ -64,6 +64,18 @@ class AolIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'https://www.aol.com/video/playlist/PL8245/5ca79d19d21f1a04035db606/',
|
'url': 'https://www.aol.com/video/playlist/PL8245/5ca79d19d21f1a04035db606/',
|
||||||
'only_matching': True,
|
'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):
|
||||||
|
@ -4,17 +4,10 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_str
|
||||||
compat_parse_qs,
|
|
||||||
compat_str,
|
|
||||||
compat_urllib_parse_urlparse,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
find_xpath_attr,
|
|
||||||
get_element_by_attribute,
|
|
||||||
int_or_none,
|
int_or_none,
|
||||||
NO_DEFAULT,
|
|
||||||
qualities,
|
qualities,
|
||||||
try_get,
|
try_get,
|
||||||
unified_strdate,
|
unified_strdate,
|
||||||
@ -25,59 +18,7 @@ from ..utils import (
|
|||||||
# add tests.
|
# add tests.
|
||||||
|
|
||||||
|
|
||||||
class ArteTvIE(InfoExtractor):
|
|
||||||
_VALID_URL = r'https?://videos\.arte\.tv/(?P<lang>fr|de|en|es)/.*-(?P<id>.*?)\.html'
|
|
||||||
IE_NAME = 'arte.tv'
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
|
||||||
mobj = re.match(self._VALID_URL, url)
|
|
||||||
lang = mobj.group('lang')
|
|
||||||
video_id = mobj.group('id')
|
|
||||||
|
|
||||||
ref_xml_url = url.replace('/videos/', '/do_delegate/videos/')
|
|
||||||
ref_xml_url = ref_xml_url.replace('.html', ',view,asPlayerXml.xml')
|
|
||||||
ref_xml_doc = self._download_xml(
|
|
||||||
ref_xml_url, video_id, note='Downloading metadata')
|
|
||||||
config_node = find_xpath_attr(ref_xml_doc, './/video', 'lang', lang)
|
|
||||||
config_xml_url = config_node.attrib['ref']
|
|
||||||
config = self._download_xml(
|
|
||||||
config_xml_url, video_id, note='Downloading configuration')
|
|
||||||
|
|
||||||
formats = [{
|
|
||||||
'format_id': q.attrib['quality'],
|
|
||||||
# The playpath starts at 'mp4:', if we don't manually
|
|
||||||
# split the url, rtmpdump will incorrectly parse them
|
|
||||||
'url': q.text.split('mp4:', 1)[0],
|
|
||||||
'play_path': 'mp4:' + q.text.split('mp4:', 1)[1],
|
|
||||||
'ext': 'flv',
|
|
||||||
'quality': 2 if q.attrib['quality'] == 'hd' else 1,
|
|
||||||
} for q in config.findall('./urls/url')]
|
|
||||||
self._sort_formats(formats)
|
|
||||||
|
|
||||||
title = config.find('.//name').text
|
|
||||||
thumbnail = config.find('.//firstThumbnailUrl').text
|
|
||||||
return {
|
|
||||||
'id': video_id,
|
|
||||||
'title': title,
|
|
||||||
'thumbnail': thumbnail,
|
|
||||||
'formats': formats,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ArteTVBaseIE(InfoExtractor):
|
class ArteTVBaseIE(InfoExtractor):
|
||||||
@classmethod
|
|
||||||
def _extract_url_info(cls, url):
|
|
||||||
mobj = re.match(cls._VALID_URL, url)
|
|
||||||
lang = mobj.group('lang')
|
|
||||||
query = compat_parse_qs(compat_urllib_parse_urlparse(url).query)
|
|
||||||
if 'vid' in query:
|
|
||||||
video_id = query['vid'][0]
|
|
||||||
else:
|
|
||||||
# This is not a real id, it can be for example AJT for the news
|
|
||||||
# http://www.arte.tv/guide/fr/emissions/AJT/arte-journal
|
|
||||||
video_id = mobj.group('id')
|
|
||||||
return video_id, lang
|
|
||||||
|
|
||||||
def _extract_from_json_url(self, json_url, video_id, lang, title=None):
|
def _extract_from_json_url(self, json_url, video_id, lang, title=None):
|
||||||
info = self._download_json(json_url, video_id)
|
info = self._download_json(json_url, video_id)
|
||||||
player_info = info['videoJsonPlayer']
|
player_info = info['videoJsonPlayer']
|
||||||
@ -108,13 +49,15 @@ class ArteTVBaseIE(InfoExtractor):
|
|||||||
'upload_date': unified_strdate(upload_date_str),
|
'upload_date': unified_strdate(upload_date_str),
|
||||||
'thumbnail': player_info.get('programImage') or player_info.get('VTU', {}).get('IUR'),
|
'thumbnail': player_info.get('programImage') or player_info.get('VTU', {}).get('IUR'),
|
||||||
}
|
}
|
||||||
qfunc = qualities(['HQ', 'MQ', 'EQ', 'SQ'])
|
qfunc = qualities(['MQ', 'HQ', 'EQ', 'SQ'])
|
||||||
|
|
||||||
LANGS = {
|
LANGS = {
|
||||||
'fr': 'F',
|
'fr': 'F',
|
||||||
'de': 'A',
|
'de': 'A',
|
||||||
'en': 'E[ANG]',
|
'en': 'E[ANG]',
|
||||||
'es': 'E[ESP]',
|
'es': 'E[ESP]',
|
||||||
|
'it': 'E[ITA]',
|
||||||
|
'pl': 'E[POL]',
|
||||||
}
|
}
|
||||||
|
|
||||||
langcode = LANGS.get(lang, lang)
|
langcode = LANGS.get(lang, lang)
|
||||||
@ -126,8 +69,8 @@ class ArteTVBaseIE(InfoExtractor):
|
|||||||
l = re.escape(langcode)
|
l = re.escape(langcode)
|
||||||
|
|
||||||
# Language preference from most to least priority
|
# Language preference from most to least priority
|
||||||
# Reference: section 5.6.3 of
|
# Reference: section 6.8 of
|
||||||
# http://www.arte.tv/sites/en/corporate/files/complete-technical-guidelines-arte-geie-v1-05.pdf
|
# https://www.arte.tv/sites/en/corporate/files/complete-technical-guidelines-arte-geie-v1-07-1.pdf
|
||||||
PREFERENCES = (
|
PREFERENCES = (
|
||||||
# original version in requested language, without subtitles
|
# original version in requested language, without subtitles
|
||||||
r'VO{0}$'.format(l),
|
r'VO{0}$'.format(l),
|
||||||
@ -193,274 +136,59 @@ class ArteTVBaseIE(InfoExtractor):
|
|||||||
|
|
||||||
class ArteTVPlus7IE(ArteTVBaseIE):
|
class ArteTVPlus7IE(ArteTVBaseIE):
|
||||||
IE_NAME = 'arte.tv:+7'
|
IE_NAME = 'arte.tv:+7'
|
||||||
_VALID_URL = r'https?://(?:(?:www|sites)\.)?arte\.tv/(?:[^/]+/)?(?P<lang>fr|de|en|es)/(?:videos/)?(?:[^/]+/)*(?P<id>[^/?#&]+)'
|
_VALID_URL = r'https?://(?:www\.)?arte\.tv/(?P<lang>fr|de|en|es|it|pl)/videos/(?P<id>\d{6}-\d{3}-[AF])'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.arte.tv/guide/de/sendungen/XEN/xenius/?vid=055918-015_PLUS7-D',
|
'url': 'https://www.arte.tv/en/videos/088501-000-A/mexico-stealing-petrol-to-survive/',
|
||||||
'only_matching': True,
|
'info_dict': {
|
||||||
}, {
|
'id': '088501-000-A',
|
||||||
'url': 'http://sites.arte.tv/karambolage/de/video/karambolage-22',
|
'ext': 'mp4',
|
||||||
'only_matching': True,
|
'title': 'Mexico: Stealing Petrol to Survive',
|
||||||
}, {
|
'upload_date': '20190628',
|
||||||
'url': 'http://www.arte.tv/de/videos/048696-000-A/der-kluge-bauch-unser-zweites-gehirn',
|
},
|
||||||
'only_matching': True,
|
|
||||||
}]
|
}]
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def suitable(cls, url):
|
|
||||||
return False if ArteTVPlaylistIE.suitable(url) else super(ArteTVPlus7IE, cls).suitable(url)
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id, lang = self._extract_url_info(url)
|
lang, video_id = re.match(self._VALID_URL, url).groups()
|
||||||
webpage = self._download_webpage(url, video_id)
|
return self._extract_from_json_url(
|
||||||
return self._extract_from_webpage(webpage, video_id, lang)
|
'https://api.arte.tv/api/player/v1/config/%s/%s' % (lang, video_id),
|
||||||
|
video_id, lang)
|
||||||
def _extract_from_webpage(self, webpage, video_id, lang):
|
|
||||||
patterns_templates = (r'arte_vp_url=["\'](.*?%s.*?)["\']', r'data-url=["\']([^"]+%s[^"]+)["\']')
|
|
||||||
ids = (video_id, '')
|
|
||||||
# some pages contain multiple videos (like
|
|
||||||
# http://www.arte.tv/guide/de/sendungen/XEN/xenius/?vid=055918-015_PLUS7-D),
|
|
||||||
# so we first try to look for json URLs that contain the video id from
|
|
||||||
# the 'vid' parameter.
|
|
||||||
patterns = [t % re.escape(_id) for _id in ids for t in patterns_templates]
|
|
||||||
json_url = self._html_search_regex(
|
|
||||||
patterns, webpage, 'json vp url', default=None)
|
|
||||||
if not json_url:
|
|
||||||
def find_iframe_url(webpage, default=NO_DEFAULT):
|
|
||||||
return self._html_search_regex(
|
|
||||||
r'<iframe[^>]+src=(["\'])(?P<url>.+\bjson_url=.+?)\1',
|
|
||||||
webpage, 'iframe url', group='url', default=default)
|
|
||||||
|
|
||||||
iframe_url = find_iframe_url(webpage, None)
|
|
||||||
if not iframe_url:
|
|
||||||
embed_url = self._html_search_regex(
|
|
||||||
r'arte_vp_url_oembed=\'([^\']+?)\'', webpage, 'embed url', default=None)
|
|
||||||
if embed_url:
|
|
||||||
player = self._download_json(
|
|
||||||
embed_url, video_id, 'Downloading player page')
|
|
||||||
iframe_url = find_iframe_url(player['html'])
|
|
||||||
# en and es URLs produce react-based pages with different layout (e.g.
|
|
||||||
# http://www.arte.tv/guide/en/053330-002-A/carnival-italy?zone=world)
|
|
||||||
if not iframe_url:
|
|
||||||
program = self._search_regex(
|
|
||||||
r'program\s*:\s*({.+?["\']embed_html["\'].+?}),?\s*\n',
|
|
||||||
webpage, 'program', default=None)
|
|
||||||
if program:
|
|
||||||
embed_html = self._parse_json(program, video_id)
|
|
||||||
if embed_html:
|
|
||||||
iframe_url = find_iframe_url(embed_html['embed_html'])
|
|
||||||
if iframe_url:
|
|
||||||
json_url = compat_parse_qs(
|
|
||||||
compat_urllib_parse_urlparse(iframe_url).query)['json_url'][0]
|
|
||||||
if json_url:
|
|
||||||
title = self._search_regex(
|
|
||||||
r'<h3[^>]+title=(["\'])(?P<title>.+?)\1',
|
|
||||||
webpage, 'title', default=None, group='title')
|
|
||||||
return self._extract_from_json_url(json_url, video_id, lang, title=title)
|
|
||||||
# Different kind of embed URL (e.g.
|
|
||||||
# http://www.arte.tv/magazine/trepalium/fr/episode-0406-replay-trepalium)
|
|
||||||
entries = [
|
|
||||||
self.url_result(url)
|
|
||||||
for _, url in re.findall(r'<iframe[^>]+src=(["\'])(?P<url>.+?)\1', webpage)]
|
|
||||||
return self.playlist_result(entries)
|
|
||||||
|
|
||||||
|
|
||||||
# It also uses the arte_vp_url url from the webpage to extract the information
|
|
||||||
class ArteTVCreativeIE(ArteTVPlus7IE):
|
|
||||||
IE_NAME = 'arte.tv:creative'
|
|
||||||
_VALID_URL = r'https?://creative\.arte\.tv/(?P<lang>fr|de|en|es)/(?:[^/]+/)*(?P<id>[^/?#&]+)'
|
|
||||||
|
|
||||||
_TESTS = [{
|
|
||||||
'url': 'http://creative.arte.tv/fr/episode/osmosis-episode-1',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '057405-001-A',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'OSMOSIS - N\'AYEZ PLUS PEUR D\'AIMER (1)',
|
|
||||||
'upload_date': '20150716',
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
'url': 'http://creative.arte.tv/fr/Monty-Python-Reunion',
|
|
||||||
'playlist_count': 11,
|
|
||||||
'add_ie': ['Youtube'],
|
|
||||||
}, {
|
|
||||||
'url': 'http://creative.arte.tv/de/episode/agentur-amateur-4-der-erste-kunde',
|
|
||||||
'only_matching': True,
|
|
||||||
}]
|
|
||||||
|
|
||||||
|
|
||||||
class ArteTVInfoIE(ArteTVPlus7IE):
|
|
||||||
IE_NAME = 'arte.tv:info'
|
|
||||||
_VALID_URL = r'https?://info\.arte\.tv/(?P<lang>fr|de|en|es)/(?:[^/]+/)*(?P<id>[^/?#&]+)'
|
|
||||||
|
|
||||||
_TESTS = [{
|
|
||||||
'url': 'http://info.arte.tv/fr/service-civique-un-cache-misere',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '067528-000-A',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Service civique, un cache misère ?',
|
|
||||||
'upload_date': '20160403',
|
|
||||||
},
|
|
||||||
}]
|
|
||||||
|
|
||||||
|
|
||||||
class ArteTVFutureIE(ArteTVPlus7IE):
|
|
||||||
IE_NAME = 'arte.tv:future'
|
|
||||||
_VALID_URL = r'https?://future\.arte\.tv/(?P<lang>fr|de|en|es)/(?P<id>[^/?#&]+)'
|
|
||||||
|
|
||||||
_TESTS = [{
|
|
||||||
'url': 'http://future.arte.tv/fr/info-sciences/les-ecrevisses-aussi-sont-anxieuses',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '050940-028-A',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Les écrevisses aussi peuvent être anxieuses',
|
|
||||||
'upload_date': '20140902',
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
'url': 'http://future.arte.tv/fr/la-science-est-elle-responsable',
|
|
||||||
'only_matching': True,
|
|
||||||
}]
|
|
||||||
|
|
||||||
|
|
||||||
class ArteTVDDCIE(ArteTVPlus7IE):
|
|
||||||
IE_NAME = 'arte.tv:ddc'
|
|
||||||
_VALID_URL = r'https?://ddc\.arte\.tv/(?P<lang>emission|folge)/(?P<id>[^/?#&]+)'
|
|
||||||
|
|
||||||
_TESTS = []
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
|
||||||
video_id, lang = self._extract_url_info(url)
|
|
||||||
if lang == 'folge':
|
|
||||||
lang = 'de'
|
|
||||||
elif lang == 'emission':
|
|
||||||
lang = 'fr'
|
|
||||||
webpage = self._download_webpage(url, video_id)
|
|
||||||
scriptElement = get_element_by_attribute('class', 'visu_video_block', webpage)
|
|
||||||
script_url = self._html_search_regex(r'src="(.*?)"', scriptElement, 'script url')
|
|
||||||
javascriptPlayerGenerator = self._download_webpage(script_url, video_id, 'Download javascript player generator')
|
|
||||||
json_url = self._search_regex(r"json_url=(.*)&rendering_place.*", javascriptPlayerGenerator, 'json url')
|
|
||||||
return self._extract_from_json_url(json_url, video_id, lang)
|
|
||||||
|
|
||||||
|
|
||||||
class ArteTVConcertIE(ArteTVPlus7IE):
|
|
||||||
IE_NAME = 'arte.tv:concert'
|
|
||||||
_VALID_URL = r'https?://concert\.arte\.tv/(?P<lang>fr|de|en|es)/(?P<id>[^/?#&]+)'
|
|
||||||
|
|
||||||
_TESTS = [{
|
|
||||||
'url': 'http://concert.arte.tv/de/notwist-im-pariser-konzertclub-divan-du-monde',
|
|
||||||
'md5': '9ea035b7bd69696b67aa2ccaaa218161',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '186',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'The Notwist im Pariser Konzertclub "Divan du Monde"',
|
|
||||||
'upload_date': '20140128',
|
|
||||||
'description': 'md5:486eb08f991552ade77439fe6d82c305',
|
|
||||||
},
|
|
||||||
}]
|
|
||||||
|
|
||||||
|
|
||||||
class ArteTVCinemaIE(ArteTVPlus7IE):
|
|
||||||
IE_NAME = 'arte.tv:cinema'
|
|
||||||
_VALID_URL = r'https?://cinema\.arte\.tv/(?P<lang>fr|de|en|es)/(?P<id>.+)'
|
|
||||||
|
|
||||||
_TESTS = [{
|
|
||||||
'url': 'http://cinema.arte.tv/fr/article/les-ailes-du-desir-de-julia-reck',
|
|
||||||
'md5': 'a5b9dd5575a11d93daf0e3f404f45438',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '062494-000-A',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Film lauréat du concours web - "Les ailes du désir" de Julia Reck',
|
|
||||||
'upload_date': '20150807',
|
|
||||||
},
|
|
||||||
}]
|
|
||||||
|
|
||||||
|
|
||||||
class ArteTVMagazineIE(ArteTVPlus7IE):
|
|
||||||
IE_NAME = 'arte.tv:magazine'
|
|
||||||
_VALID_URL = r'https?://(?:www\.)?arte\.tv/magazine/[^/]+/(?P<lang>fr|de|en|es)/(?P<id>[^/?#&]+)'
|
|
||||||
|
|
||||||
_TESTS = [{
|
|
||||||
# Embedded via <iframe src="http://www.arte.tv/arte_vp/index.php?json_url=..."
|
|
||||||
'url': 'http://www.arte.tv/magazine/trepalium/fr/entretien-avec-le-realisateur-vincent-lannoo-trepalium',
|
|
||||||
'md5': '2a9369bcccf847d1c741e51416299f25',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '065965-000-A',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Trepalium - Extrait Ep.01',
|
|
||||||
'upload_date': '20160121',
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
# Embedded via <iframe src="http://www.arte.tv/guide/fr/embed/054813-004-A/medium"
|
|
||||||
'url': 'http://www.arte.tv/magazine/trepalium/fr/episode-0406-replay-trepalium',
|
|
||||||
'md5': 'fedc64fc7a946110fe311634e79782ca',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '054813-004_PLUS7-F',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Trepalium (4/6)',
|
|
||||||
'description': 'md5:10057003c34d54e95350be4f9b05cb40',
|
|
||||||
'upload_date': '20160218',
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
'url': 'http://www.arte.tv/magazine/metropolis/de/frank-woeste-german-paris-metropolis',
|
|
||||||
'only_matching': True,
|
|
||||||
}]
|
|
||||||
|
|
||||||
|
|
||||||
class ArteTVEmbedIE(ArteTVPlus7IE):
|
class ArteTVEmbedIE(ArteTVPlus7IE):
|
||||||
IE_NAME = 'arte.tv:embed'
|
IE_NAME = 'arte.tv:embed'
|
||||||
_VALID_URL = r'''(?x)
|
_VALID_URL = r'''(?x)
|
||||||
http://www\.arte\.tv
|
https://www\.arte\.tv
|
||||||
/(?:playerv2/embed|arte_vp/index)\.php\?json_url=
|
/player/v3/index\.php\?json_url=
|
||||||
(?P<json_url>
|
(?P<json_url>
|
||||||
http://arte\.tv/papi/tvguide/videos/stream/player/
|
https?://api\.arte\.tv/api/player/v1/config/
|
||||||
(?P<lang>[^/]+)/(?P<id>[^/]+)[^&]*
|
(?P<lang>[^/]+)/(?P<id>\d{6}-\d{3}-[AF])
|
||||||
)
|
)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
_TESTS = []
|
_TESTS = []
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
json_url, lang, video_id = re.match(self._VALID_URL, url).groups()
|
||||||
video_id = mobj.group('id')
|
|
||||||
lang = mobj.group('lang')
|
|
||||||
json_url = mobj.group('json_url')
|
|
||||||
return self._extract_from_json_url(json_url, video_id, lang)
|
return self._extract_from_json_url(json_url, video_id, lang)
|
||||||
|
|
||||||
|
|
||||||
class TheOperaPlatformIE(ArteTVPlus7IE):
|
|
||||||
IE_NAME = 'theoperaplatform'
|
|
||||||
_VALID_URL = r'https?://(?:www\.)?theoperaplatform\.eu/(?P<lang>fr|de|en|es)/(?P<id>[^/?#&]+)'
|
|
||||||
|
|
||||||
_TESTS = [{
|
|
||||||
'url': 'http://www.theoperaplatform.eu/de/opera/verdi-otello',
|
|
||||||
'md5': '970655901fa2e82e04c00b955e9afe7b',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '060338-009-A',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Verdi - OTELLO',
|
|
||||||
'upload_date': '20160927',
|
|
||||||
},
|
|
||||||
}]
|
|
||||||
|
|
||||||
|
|
||||||
class ArteTVPlaylistIE(ArteTVBaseIE):
|
class ArteTVPlaylistIE(ArteTVBaseIE):
|
||||||
IE_NAME = 'arte.tv:playlist'
|
IE_NAME = 'arte.tv:playlist'
|
||||||
_VALID_URL = r'https?://(?:www\.)?arte\.tv/guide/(?P<lang>fr|de|en|es)/[^#]*#collection/(?P<id>PL-\d+)'
|
_VALID_URL = r'https?://(?:www\.)?arte\.tv/(?P<lang>fr|de|en|es|it|pl)/videos/(?P<id>RC-\d{6})'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.arte.tv/guide/de/plus7/?country=DE#collection/PL-013263/ARTETV',
|
'url': 'https://www.arte.tv/en/videos/RC-016954/earn-a-living/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'PL-013263',
|
'id': 'RC-016954',
|
||||||
'title': 'Areva & Uramin',
|
'title': 'Earn a Living',
|
||||||
'description': 'md5:a1dc0312ce357c262259139cfd48c9bf',
|
'description': 'md5:d322c55011514b3a7241f7fb80d494c2',
|
||||||
},
|
},
|
||||||
'playlist_mincount': 6,
|
'playlist_mincount': 6,
|
||||||
}, {
|
|
||||||
'url': 'http://www.arte.tv/guide/de/playlists?country=DE#collection/PL-013190/ARTETV',
|
|
||||||
'only_matching': True,
|
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
playlist_id, lang = self._extract_url_info(url)
|
lang, playlist_id = re.match(self._VALID_URL, url).groups()
|
||||||
collection = self._download_json(
|
collection = self._download_json(
|
||||||
'https://api.arte.tv/api/player/v1/collectionData/%s/%s?source=videos'
|
'https://api.arte.tv/api/player/v1/collectionData/%s/%s?source=videos'
|
||||||
% (lang, playlist_id), playlist_id)
|
% (lang, playlist_id), playlist_id)
|
||||||
|
@ -99,8 +99,8 @@ class BeamProLiveIE(BeamProBaseIE):
|
|||||||
|
|
||||||
class BeamProVodIE(BeamProBaseIE):
|
class BeamProVodIE(BeamProBaseIE):
|
||||||
IE_NAME = 'Mixer:vod'
|
IE_NAME = 'Mixer:vod'
|
||||||
_VALID_URL = r'https?://(?:\w+\.)?(?:beam\.pro|mixer\.com)/[^/?#&]+\?.*?\bvod=(?P<id>\d+)'
|
_VALID_URL = r'https?://(?:\w+\.)?(?:beam\.pro|mixer\.com)/[^/?#&]+\?.*?\bvod=(?P<id>[^?#&]+)'
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
'url': 'https://mixer.com/willow8714?vod=2259830',
|
'url': 'https://mixer.com/willow8714?vod=2259830',
|
||||||
'md5': 'b2431e6e8347dc92ebafb565d368b76b',
|
'md5': 'b2431e6e8347dc92ebafb565d368b76b',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@ -119,7 +119,13 @@ class BeamProVodIE(BeamProBaseIE):
|
|||||||
'params': {
|
'params': {
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
}
|
}, {
|
||||||
|
'url': 'https://mixer.com/streamer?vod=IxFno1rqC0S_XJ1a2yGgNw',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://mixer.com/streamer?vod=Rh3LY0VAqkGpEQUe2pN-ig',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _extract_format(vod, vod_type):
|
def _extract_format(vod, vod_type):
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_str
|
from ..compat import (
|
||||||
|
compat_str,
|
||||||
|
compat_urlparse,
|
||||||
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
int_or_none,
|
int_or_none,
|
||||||
unified_timestamp,
|
unified_timestamp,
|
||||||
@ -11,6 +14,7 @@ from ..utils import (
|
|||||||
class BeegIE(InfoExtractor):
|
class BeegIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?beeg\.(?:com|porn(?:/video)?)/(?P<id>\d+)'
|
_VALID_URL = r'https?://(?:www\.)?beeg\.(?:com|porn(?:/video)?)/(?P<id>\d+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
|
# api/v6 v1
|
||||||
'url': 'http://beeg.com/5416503',
|
'url': 'http://beeg.com/5416503',
|
||||||
'md5': 'a1a1b1a8bc70a89e49ccfd113aed0820',
|
'md5': 'a1a1b1a8bc70a89e49ccfd113aed0820',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@ -24,6 +28,10 @@ class BeegIE(InfoExtractor):
|
|||||||
'tags': list,
|
'tags': list,
|
||||||
'age_limit': 18,
|
'age_limit': 18,
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
# api/v6 v2
|
||||||
|
'url': 'https://beeg.com/1941093077?t=911-1391',
|
||||||
|
'only_matching': True,
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://beeg.porn/video/5416503',
|
'url': 'https://beeg.porn/video/5416503',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
@ -41,11 +49,22 @@ class BeegIE(InfoExtractor):
|
|||||||
r'beeg_version\s*=\s*([\da-zA-Z_-]+)', webpage, 'beeg version',
|
r'beeg_version\s*=\s*([\da-zA-Z_-]+)', webpage, 'beeg version',
|
||||||
default='1546225636701')
|
default='1546225636701')
|
||||||
|
|
||||||
|
qs = compat_urlparse.parse_qs(compat_urlparse.urlparse(url).query)
|
||||||
|
t = qs.get('t', [''])[0].split('-')
|
||||||
|
if len(t) > 1:
|
||||||
|
query = {
|
||||||
|
'v': 2,
|
||||||
|
's': t[0],
|
||||||
|
'e': t[1],
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
query = {'v': 1}
|
||||||
|
|
||||||
for api_path in ('', 'api.'):
|
for api_path in ('', 'api.'):
|
||||||
video = self._download_json(
|
video = self._download_json(
|
||||||
'https://%sbeeg.com/api/v6/%s/video/%s'
|
'https://%sbeeg.com/api/v6/%s/video/%s'
|
||||||
% (api_path, beeg_version, video_id), video_id,
|
% (api_path, beeg_version, video_id), video_id,
|
||||||
fatal=api_path == 'api.')
|
fatal=api_path == 'api.', query=query)
|
||||||
if video:
|
if video:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ class BIQLEIE(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)
|
||||||
embed_url = self._proto_relative_url(self._search_regex(
|
embed_url = self._proto_relative_url(self._search_regex(
|
||||||
r'<iframe.+?src="((?:https?:)?//daxab\.com/[^"]+)".*?></iframe>',
|
r'<iframe.+?src="((?:https?:)?//(?:daxab\.com|dxb\.to|[^/]+/player)/[^"]+)".*?></iframe>',
|
||||||
webpage, 'embed url'))
|
webpage, 'embed url'))
|
||||||
if VKIE.suitable(embed_url):
|
if VKIE.suitable(embed_url):
|
||||||
return self.url_result(embed_url, VKIE.ie_key(), video_id)
|
return self.url_result(embed_url, VKIE.ie_key(), video_id)
|
||||||
|
@ -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',
|
||||||
})
|
})
|
||||||
|
@ -483,7 +483,7 @@ class BrightcoveLegacyIE(InfoExtractor):
|
|||||||
|
|
||||||
class BrightcoveNewIE(AdobePassIE):
|
class BrightcoveNewIE(AdobePassIE):
|
||||||
IE_NAME = 'brightcove:new'
|
IE_NAME = 'brightcove:new'
|
||||||
_VALID_URL = r'https?://players\.brightcove\.net/(?P<account_id>\d+)/(?P<player_id>[^/]+)_(?P<embed>[^/]+)/index\.html\?.*videoId=(?P<video_id>\d+|ref:[^&]+)'
|
_VALID_URL = r'https?://players\.brightcove\.net/(?P<account_id>\d+)/(?P<player_id>[^/]+)_(?P<embed>[^/]+)/index\.html\?.*(?P<content_type>video|playlist)Id=(?P<video_id>\d+|ref:[^&]+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://players.brightcove.net/929656772001/e41d32dc-ec74-459e-a845-6c69f7b724ea_default/index.html?videoId=4463358922001',
|
'url': 'http://players.brightcove.net/929656772001/e41d32dc-ec74-459e-a845-6c69f7b724ea_default/index.html?videoId=4463358922001',
|
||||||
'md5': 'c8100925723840d4b0d243f7025703be',
|
'md5': 'c8100925723840d4b0d243f7025703be',
|
||||||
@ -516,6 +516,21 @@ class BrightcoveNewIE(AdobePassIE):
|
|||||||
# m3u8 download
|
# m3u8 download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
# playlist stream
|
||||||
|
'url': 'https://players.brightcove.net/1752604059001/S13cJdUBz_default/index.html?playlistId=5718313430001',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '5718313430001',
|
||||||
|
'title': 'No Audio Playlist',
|
||||||
|
},
|
||||||
|
'playlist_count': 7,
|
||||||
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'url': 'http://players.brightcove.net/5690807595001/HyZNerRl7_default/index.html?playlistId=5743160747001',
|
||||||
|
'only_matching': True,
|
||||||
}, {
|
}, {
|
||||||
# ref: prefixed video id
|
# ref: prefixed video id
|
||||||
'url': 'http://players.brightcove.net/3910869709001/21519b5c-4b3b-4363-accb-bdc8f358f823_default/index.html?videoId=ref:7069442',
|
'url': 'http://players.brightcove.net/3910869709001/21519b5c-4b3b-4363-accb-bdc8f358f823_default/index.html?videoId=ref:7069442',
|
||||||
@ -715,7 +730,7 @@ class BrightcoveNewIE(AdobePassIE):
|
|||||||
'ip_blocks': smuggled_data.get('geo_ip_blocks'),
|
'ip_blocks': smuggled_data.get('geo_ip_blocks'),
|
||||||
})
|
})
|
||||||
|
|
||||||
account_id, player_id, embed, video_id = re.match(self._VALID_URL, url).groups()
|
account_id, player_id, embed, content_type, video_id = re.match(self._VALID_URL, url).groups()
|
||||||
|
|
||||||
webpage = self._download_webpage(
|
webpage = self._download_webpage(
|
||||||
'http://players.brightcove.net/%s/%s_%s/index.min.js'
|
'http://players.brightcove.net/%s/%s_%s/index.min.js'
|
||||||
@ -736,7 +751,7 @@ class BrightcoveNewIE(AdobePassIE):
|
|||||||
r'policyKey\s*:\s*(["\'])(?P<pk>.+?)\1',
|
r'policyKey\s*:\s*(["\'])(?P<pk>.+?)\1',
|
||||||
webpage, 'policy key', group='pk')
|
webpage, 'policy key', group='pk')
|
||||||
|
|
||||||
api_url = 'https://edge.api.brightcove.com/playback/v1/accounts/%s/videos/%s' % (account_id, video_id)
|
api_url = 'https://edge.api.brightcove.com/playback/v1/accounts/%s/%ss/%s' % (account_id, content_type, video_id)
|
||||||
headers = {
|
headers = {
|
||||||
'Accept': 'application/json;pk=%s' % policy_key,
|
'Accept': 'application/json;pk=%s' % policy_key,
|
||||||
}
|
}
|
||||||
@ -771,5 +786,12 @@ class BrightcoveNewIE(AdobePassIE):
|
|||||||
'tveToken': tve_token,
|
'tveToken': tve_token,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if content_type == 'playlist':
|
||||||
|
return self.playlist_result(
|
||||||
|
[self._parse_brightcove_metadata(vid, vid.get('id'), headers)
|
||||||
|
for vid in json_data.get('videos', []) if vid.get('id')],
|
||||||
|
json_data.get('id'), json_data.get('name'),
|
||||||
|
json_data.get('description'))
|
||||||
|
|
||||||
return self._parse_brightcove_metadata(
|
return self._parse_brightcove_metadata(
|
||||||
json_data, video_id, headers=headers)
|
json_data, video_id, headers=headers)
|
||||||
|
@ -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):
|
||||||
@ -65,7 +69,7 @@ class CBSIE(CBSBaseIE):
|
|||||||
last_e = None
|
last_e = None
|
||||||
for item in items_data.findall('.//item'):
|
for item in items_data.findall('.//item'):
|
||||||
asset_type = xpath_text(item, 'assetType')
|
asset_type = xpath_text(item, 'assetType')
|
||||||
if not asset_type or asset_type in asset_types or asset_type in ('HLS_FPS', 'DASH_CENC'):
|
if not asset_type or asset_type in asset_types or 'HLS_FPS' in asset_type or 'DASH_CENC' in asset_type:
|
||||||
continue
|
continue
|
||||||
asset_types.append(asset_type)
|
asset_types.append(asset_type)
|
||||||
query = {
|
query = {
|
||||||
|
@ -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,
|
|
||||||
}
|
|
@ -103,19 +103,6 @@ class CrunchyrollBaseIE(InfoExtractor):
|
|||||||
def _real_initialize(self):
|
def _real_initialize(self):
|
||||||
self._login()
|
self._login()
|
||||||
|
|
||||||
def _download_webpage(self, url_or_request, *args, **kwargs):
|
|
||||||
request = (url_or_request if isinstance(url_or_request, compat_urllib_request.Request)
|
|
||||||
else sanitized_Request(url_or_request))
|
|
||||||
# Accept-Language must be set explicitly to accept any language to avoid issues
|
|
||||||
# similar to https://github.com/ytdl-org/youtube-dl/issues/6797.
|
|
||||||
# Along with IP address Crunchyroll uses Accept-Language to guess whether georestriction
|
|
||||||
# should be imposed or not (from what I can see it just takes the first language
|
|
||||||
# ignoring the priority and requires it to correspond the IP). By the way this causes
|
|
||||||
# Crunchyroll to not work in georestriction cases in some browsers that don't place
|
|
||||||
# the locale lang first in header. However allowing any language seems to workaround the issue.
|
|
||||||
request.add_header('Accept-Language', '*')
|
|
||||||
return super(CrunchyrollBaseIE, self)._download_webpage(request, *args, **kwargs)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _add_skip_wall(url):
|
def _add_skip_wall(url):
|
||||||
parsed_url = compat_urlparse.urlparse(url)
|
parsed_url = compat_urlparse.urlparse(url)
|
||||||
@ -269,6 +256,19 @@ class CrunchyrollIE(CrunchyrollBaseIE, VRVIE):
|
|||||||
'1080': ('80', '108'),
|
'1080': ('80', '108'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _download_webpage(self, url_or_request, *args, **kwargs):
|
||||||
|
request = (url_or_request if isinstance(url_or_request, compat_urllib_request.Request)
|
||||||
|
else sanitized_Request(url_or_request))
|
||||||
|
# Accept-Language must be set explicitly to accept any language to avoid issues
|
||||||
|
# similar to https://github.com/ytdl-org/youtube-dl/issues/6797.
|
||||||
|
# Along with IP address Crunchyroll uses Accept-Language to guess whether georestriction
|
||||||
|
# should be imposed or not (from what I can see it just takes the first language
|
||||||
|
# ignoring the priority and requires it to correspond the IP). By the way this causes
|
||||||
|
# Crunchyroll to not work in georestriction cases in some browsers that don't place
|
||||||
|
# the locale lang first in header. However allowing any language seems to workaround the issue.
|
||||||
|
request.add_header('Accept-Language', '*')
|
||||||
|
return super(CrunchyrollBaseIE, self)._download_webpage(request, *args, **kwargs)
|
||||||
|
|
||||||
def _decrypt_subtitles(self, data, iv, id):
|
def _decrypt_subtitles(self, data, iv, id):
|
||||||
data = bytes_to_intlist(compat_b64decode(data))
|
data = bytes_to_intlist(compat_b64decode(data))
|
||||||
iv = bytes_to_intlist(compat_b64decode(iv))
|
iv = bytes_to_intlist(compat_b64decode(iv))
|
||||||
@ -661,9 +661,8 @@ class CrunchyrollShowPlaylistIE(CrunchyrollBaseIE):
|
|||||||
webpage = self._download_webpage(
|
webpage = self._download_webpage(
|
||||||
self._add_skip_wall(url), show_id,
|
self._add_skip_wall(url), show_id,
|
||||||
headers=self.geo_verification_headers())
|
headers=self.geo_verification_headers())
|
||||||
title = self._html_search_regex(
|
title = self._html_search_meta('name', webpage, default=None)
|
||||||
r'(?s)<h1[^>]*>\s*<span itemprop="name">(.*?)</span>',
|
|
||||||
webpage, 'title')
|
|
||||||
episode_paths = re.findall(
|
episode_paths = re.findall(
|
||||||
r'(?s)<li id="showview_videos_media_(\d+)"[^>]+>.*?<a href="([^"]+)"',
|
r'(?s)<li id="showview_videos_media_(\d+)"[^>]+>.*?<a href="([^"]+)"',
|
||||||
webpage)
|
webpage)
|
||||||
|
@ -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')
|
||||||
|
@ -137,10 +137,16 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _extract_urls(webpage):
|
def _extract_urls(webpage):
|
||||||
|
urls = []
|
||||||
# Look for embedded Dailymotion player
|
# Look for embedded Dailymotion player
|
||||||
matches = re.findall(
|
# https://developer.dailymotion.com/player#player-parameters
|
||||||
r'<(?:(?:embed|iframe)[^>]+?src=|input[^>]+id=[\'"]dmcloudUrlEmissionSelect[\'"][^>]+value=)(["\'])(?P<url>(?:https?:)?//(?:www\.)?dailymotion\.com/(?:embed|swf)/video/.+?)\1', webpage)
|
for mobj in re.finditer(
|
||||||
return list(map(lambda m: unescapeHTML(m[1]), matches))
|
r'<(?:(?:embed|iframe)[^>]+?src=|input[^>]+id=[\'"]dmcloudUrlEmissionSelect[\'"][^>]+value=)(["\'])(?P<url>(?:https?:)?//(?:www\.)?dailymotion\.com/(?:embed|swf)/video/.+?)\1', webpage):
|
||||||
|
urls.append(unescapeHTML(mobj.group('url')))
|
||||||
|
for mobj in re.finditer(
|
||||||
|
r'(?s)DM\.player\([^,]+,\s*{.*?video[\'"]?\s*:\s*["\']?(?P<id>[0-9a-zA-Z]+).+?}\s*\);', webpage):
|
||||||
|
urls.append('https://www.dailymotion.com/embed/video/' + mobj.group('id'))
|
||||||
|
return urls
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
@ -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/'
|
||||||
|
|
||||||
|
@ -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)
|
|
@ -24,7 +24,7 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class DRTVIE(InfoExtractor):
|
class DRTVIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?dr\.dk/(?:tv/se|nyheder|radio/ondemand)/(?:[^/]+/)*(?P<id>[\da-z-]+)(?:[/#?]|$)'
|
_VALID_URL = r'https?://(?:www\.)?dr\.dk/(?:tv/se|nyheder|radio(?:/ondemand)?)/(?:[^/]+/)*(?P<id>[\da-z-]+)(?:[/#?]|$)'
|
||||||
_GEO_BYPASS = False
|
_GEO_BYPASS = False
|
||||||
_GEO_COUNTRIES = ['DK']
|
_GEO_COUNTRIES = ['DK']
|
||||||
IE_NAME = 'drtv'
|
IE_NAME = 'drtv'
|
||||||
@ -80,6 +80,9 @@ class DRTVIE(InfoExtractor):
|
|||||||
'params': {
|
'params': {
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.dr.dk/radio/p4kbh/regionale-nyheder-kh4/p4-nyheder-2019-06-26-17-30-9',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
@ -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 {
|
||||||
|
@ -58,17 +58,8 @@ from .ard import (
|
|||||||
ARDMediathekIE,
|
ARDMediathekIE,
|
||||||
)
|
)
|
||||||
from .arte import (
|
from .arte import (
|
||||||
ArteTvIE,
|
|
||||||
ArteTVPlus7IE,
|
ArteTVPlus7IE,
|
||||||
ArteTVCreativeIE,
|
|
||||||
ArteTVConcertIE,
|
|
||||||
ArteTVInfoIE,
|
|
||||||
ArteTVFutureIE,
|
|
||||||
ArteTVCinemaIE,
|
|
||||||
ArteTVDDCIE,
|
|
||||||
ArteTVMagazineIE,
|
|
||||||
ArteTVEmbedIE,
|
ArteTVEmbedIE,
|
||||||
TheOperaPlatformIE,
|
|
||||||
ArteTVPlaylistIE,
|
ArteTVPlaylistIE,
|
||||||
)
|
)
|
||||||
from .asiancrush import (
|
from .asiancrush import (
|
||||||
@ -173,11 +164,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
|
||||||
@ -194,6 +189,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,
|
||||||
@ -236,7 +232,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,
|
||||||
@ -283,10 +278,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
|
||||||
@ -745,7 +736,6 @@ from .nexx import (
|
|||||||
NexxIE,
|
NexxIE,
|
||||||
NexxEmbedIE,
|
NexxEmbedIE,
|
||||||
)
|
)
|
||||||
from .nfb import NFBIE
|
|
||||||
from .nfl import NFLIE
|
from .nfl import NFLIE
|
||||||
from .nhk import NhkVodIE
|
from .nhk import NhkVodIE
|
||||||
from .nhl import NHLIE
|
from .nhl import NHLIE
|
||||||
@ -772,13 +762,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,
|
||||||
@ -808,6 +791,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 (
|
||||||
@ -831,7 +816,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,
|
||||||
@ -891,12 +879,12 @@ 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,
|
||||||
PornHubPlaylistIE,
|
PornHubUserIE,
|
||||||
PornHubUserVideosIE,
|
PornHubPagedVideoListIE,
|
||||||
|
PornHubUserVideosUploadIE,
|
||||||
)
|
)
|
||||||
from .pornotube import PornotubeIE
|
from .pornotube import PornotubeIE
|
||||||
from .pornovoisines import PornoVoisinesIE
|
from .pornovoisines import PornoVoisinesIE
|
||||||
@ -941,7 +929,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,
|
||||||
@ -1031,7 +1022,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
|
||||||
@ -1095,6 +1089,10 @@ from .streetvoice import StreetVoiceIE
|
|||||||
from .stretchinternet import StretchInternetIE
|
from .stretchinternet import StretchInternetIE
|
||||||
from .stv import STVPlayerIE
|
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,
|
||||||
@ -1416,10 +1414,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,
|
||||||
@ -1452,6 +1446,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)
|
||||||
|
@ -1,35 +1,84 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from .ooyala import OoyalaIE
|
from ..utils import (
|
||||||
|
determine_ext,
|
||||||
|
int_or_none,
|
||||||
|
mimetype2ext,
|
||||||
|
parse_iso8601,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class FusionIE(InfoExtractor):
|
class FusionIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?fusion\.(?:net|tv)/video/(?P<id>\d+)'
|
_VALID_URL = r'https?://(?:www\.)?fusion\.(?:net|tv)/(?:video/|show/.+?\bvideo=)(?P<id>\d+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://fusion.tv/video/201781/u-s-and-panamanian-forces-work-together-to-stop-a-vessel-smuggling-drugs/',
|
'url': 'http://fusion.tv/video/201781/u-s-and-panamanian-forces-work-together-to-stop-a-vessel-smuggling-drugs/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'ZpcWNoMTE6x6uVIIWYpHh0qQDjxBuq5P',
|
'id': '3145868',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'U.S. and Panamanian forces work together to stop a vessel smuggling drugs',
|
'title': 'U.S. and Panamanian forces work together to stop a vessel smuggling drugs',
|
||||||
'description': 'md5:0cc84a9943c064c0f46b128b41b1b0d7',
|
'description': 'md5:0cc84a9943c064c0f46b128b41b1b0d7',
|
||||||
'duration': 140.0,
|
'duration': 140.0,
|
||||||
|
'timestamp': 1442589635,
|
||||||
|
'uploader': 'UNIVISON',
|
||||||
|
'upload_date': '20150918',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
'add_ie': ['Ooyala'],
|
'add_ie': ['Anvato'],
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://fusion.tv/video/201781',
|
'url': 'http://fusion.tv/video/201781',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://fusion.tv/show/food-exposed-with-nelufar-hedayat/?ancla=full-episodes&video=588644',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
display_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
webpage = self._download_webpage(url, display_id)
|
video = self._download_json(
|
||||||
|
'https://platform.fusion.net/wp-json/fusiondotnet/v1/video/' + video_id, video_id)
|
||||||
|
|
||||||
ooyala_code = self._search_regex(
|
info = {
|
||||||
r'data-ooyala-id=(["\'])(?P<code>(?:(?!\1).)+)\1',
|
'id': video_id,
|
||||||
webpage, 'ooyala code', group='code')
|
'title': video['title'],
|
||||||
|
'description': video.get('excerpt'),
|
||||||
|
'timestamp': parse_iso8601(video.get('published')),
|
||||||
|
'series': video.get('show'),
|
||||||
|
}
|
||||||
|
|
||||||
return OoyalaIE._build_url_result(ooyala_code)
|
formats = []
|
||||||
|
src = video.get('src') or {}
|
||||||
|
for f_id, f in src.items():
|
||||||
|
for q_id, q in f.items():
|
||||||
|
q_url = q.get('url')
|
||||||
|
if not q_url:
|
||||||
|
continue
|
||||||
|
ext = determine_ext(q_url, mimetype2ext(q.get('type')))
|
||||||
|
if ext == 'smil':
|
||||||
|
formats.extend(self._extract_smil_formats(q_url, video_id, fatal=False))
|
||||||
|
elif f_id == 'm3u8-variant' or (ext == 'm3u8' and q_id == 'Variant'):
|
||||||
|
formats.extend(self._extract_m3u8_formats(
|
||||||
|
q_url, video_id, 'mp4', 'm3u8_native', m3u8_id='hls', fatal=False))
|
||||||
|
else:
|
||||||
|
formats.append({
|
||||||
|
'format_id': '-'.join([f_id, q_id]),
|
||||||
|
'url': q_url,
|
||||||
|
'width': int_or_none(q.get('width')),
|
||||||
|
'height': int_or_none(q.get('height')),
|
||||||
|
'tbr': int_or_none(self._search_regex(r'_(\d+)\.m(?:p4|3u8)', q_url, 'bitrate')),
|
||||||
|
'ext': 'mp4' if ext == 'm3u8' else ext,
|
||||||
|
'protocol': 'm3u8_native' if ext == 'm3u8' else 'https',
|
||||||
|
})
|
||||||
|
if formats:
|
||||||
|
self._sort_formats(formats)
|
||||||
|
info['formats'] = formats
|
||||||
|
else:
|
||||||
|
info.update({
|
||||||
|
'_type': 'url',
|
||||||
|
'url': 'anvato:uni:' + video['video_ids']['anvato'],
|
||||||
|
'ie_key': 'Anvato',
|
||||||
|
})
|
||||||
|
|
||||||
|
return info
|
||||||
|
@ -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
|
||||||
@ -2101,6 +2104,23 @@ class GenericIE(InfoExtractor):
|
|||||||
},
|
},
|
||||||
'expected_warnings': ['Failed to download MPD manifest'],
|
'expected_warnings': ['Failed to download MPD manifest'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
# DailyMotion embed with DM.player
|
||||||
|
'url': 'https://www.beinsports.com/us/copa-del-rey/video/the-locker-room-valencia-beat-barca-in-copa/1203804',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'k6aKkGHd9FJs4mtJN39',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'The Locker Room: Valencia Beat Barca In Copa del Rey Final',
|
||||||
|
'description': 'This video is private.',
|
||||||
|
'uploader_id': 'x1jf30l',
|
||||||
|
'uploader': 'beIN SPORTS USA',
|
||||||
|
'upload_date': '20190528',
|
||||||
|
'timestamp': 1559062971,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
},
|
||||||
# {
|
# {
|
||||||
# # TODO: find another test
|
# # TODO: find another test
|
||||||
# # http://schema.org/VideoObject
|
# # http://schema.org/VideoObject
|
||||||
@ -2546,11 +2566,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 +2600,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 +3024,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 +3225,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):
|
||||||
|
@ -34,9 +34,13 @@ class GoIE(AdobePassIE):
|
|||||||
'watchdisneyxd': {
|
'watchdisneyxd': {
|
||||||
'brand': '009',
|
'brand': '009',
|
||||||
'resource_id': 'DisneyXD',
|
'resource_id': 'DisneyXD',
|
||||||
|
},
|
||||||
|
'disneynow': {
|
||||||
|
'brand': '011',
|
||||||
|
'resource_id': 'Disney',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_VALID_URL = r'https?://(?:(?P<sub_domain>%s)\.)?go\.com/(?:(?:[^/]+/)*(?P<id>vdka\w+)|(?:[^/]+/)*(?P<display_id>[^/?#]+))'\
|
_VALID_URL = r'https?://(?:(?:(?P<sub_domain>%s)\.)?go|(?P<sub_domain_2>disneynow))\.com/(?:(?:[^/]+/)*(?P<id>vdka\w+)|(?:[^/]+/)*(?P<display_id>[^/?#]+))'\
|
||||||
% '|'.join(list(_SITE_INFO.keys()) + ['disneynow'])
|
% '|'.join(list(_SITE_INFO.keys()) + ['disneynow'])
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://abc.go.com/shows/designated-survivor/video/most-recent/VDKA3807643',
|
'url': 'http://abc.go.com/shows/designated-survivor/video/most-recent/VDKA3807643',
|
||||||
@ -71,6 +75,9 @@ class GoIE(AdobePassIE):
|
|||||||
# brand 008
|
# brand 008
|
||||||
'url': 'http://disneynow.go.com/shows/minnies-bow-toons/video/happy-campers/vdka4872013',
|
'url': 'http://disneynow.go.com/shows/minnies-bow-toons/video/happy-campers/vdka4872013',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://disneynow.com/shows/minnies-bow-toons/video/happy-campers/vdka4872013',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _extract_videos(self, brand, video_id='-1', show_id='-1'):
|
def _extract_videos(self, brand, video_id='-1', show_id='-1'):
|
||||||
@ -80,7 +87,9 @@ class GoIE(AdobePassIE):
|
|||||||
display_id)['video']
|
display_id)['video']
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
sub_domain, video_id, display_id = re.match(self._VALID_URL, url).groups()
|
mobj = re.match(self._VALID_URL, url)
|
||||||
|
sub_domain = mobj.group('sub_domain') or mobj.group('sub_domain_2')
|
||||||
|
video_id, display_id = mobj.group('id', 'display_id')
|
||||||
site_info = self._SITE_INFO.get(sub_domain, {})
|
site_info = self._SITE_INFO.get(sub_domain, {})
|
||||||
brand = site_info.get('brand')
|
brand = site_info.get('brand')
|
||||||
if not video_id or not site_info:
|
if not video_id or not site_info:
|
||||||
@ -89,7 +98,7 @@ class GoIE(AdobePassIE):
|
|||||||
# There may be inner quotes, e.g. data-video-id="'VDKA3609139'"
|
# There may be inner quotes, e.g. data-video-id="'VDKA3609139'"
|
||||||
# from http://freeform.go.com/shows/shadowhunters/episodes/season-2/1-this-guilty-blood
|
# from http://freeform.go.com/shows/shadowhunters/episodes/season-2/1-this-guilty-blood
|
||||||
r'data-video-id=["\']*(VDKA\w+)', webpage, 'video id',
|
r'data-video-id=["\']*(VDKA\w+)', webpage, 'video id',
|
||||||
default=None)
|
default=video_id)
|
||||||
if not site_info:
|
if not site_info:
|
||||||
brand = self._search_regex(
|
brand = self._search_regex(
|
||||||
(r'data-brand=\s*["\']\s*(\d+)',
|
(r'data-brand=\s*["\']\s*(\d+)',
|
||||||
|
@ -13,19 +13,7 @@ from ..utils import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class HBOIE(InfoExtractor):
|
class HBOBaseIE(InfoExtractor):
|
||||||
IE_NAME = 'hbo'
|
|
||||||
_VALID_URL = r'https?://(?:www\.)?hbo\.com/(?:video|embed)(?:/[^/]+)*/(?P<id>[^/?#]+)'
|
|
||||||
_TEST = {
|
|
||||||
'url': 'https://www.hbo.com/video/game-of-thrones/seasons/season-8/videos/trailer',
|
|
||||||
'md5': '8126210656f433c452a21367f9ad85b3',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '22113301',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'Game of Thrones - Trailer',
|
|
||||||
},
|
|
||||||
'expected_warnings': ['Unknown MIME type application/mp4 in DASH manifest'],
|
|
||||||
}
|
|
||||||
_FORMATS_INFO = {
|
_FORMATS_INFO = {
|
||||||
'pro7': {
|
'pro7': {
|
||||||
'width': 1280,
|
'width': 1280,
|
||||||
@ -65,12 +53,8 @@ class HBOIE(InfoExtractor):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _extract_info(self, url, display_id):
|
||||||
display_id = self._match_id(url)
|
video_data = self._download_xml(url, display_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']
|
|
||||||
video_data = self._download_xml(urljoin(url, location_path), display_id)
|
|
||||||
video_id = xpath_text(video_data, 'id', fatal=True)
|
video_id = xpath_text(video_data, 'id', fatal=True)
|
||||||
episode_title = title = xpath_text(video_data, 'title', fatal=True)
|
episode_title = title = xpath_text(video_data, 'title', fatal=True)
|
||||||
series = xpath_text(video_data, 'program')
|
series = xpath_text(video_data, 'program')
|
||||||
@ -167,3 +151,25 @@ class HBOIE(InfoExtractor):
|
|||||||
'thumbnails': thumbnails,
|
'thumbnails': thumbnails,
|
||||||
'subtitles': subtitles,
|
'subtitles': subtitles,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class HBOIE(HBOBaseIE):
|
||||||
|
IE_NAME = 'hbo'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?hbo\.com/(?:video|embed)(?:/[^/]+)*/(?P<id>[^/?#]+)'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'https://www.hbo.com/video/game-of-thrones/seasons/season-8/videos/trailer',
|
||||||
|
'md5': '8126210656f433c452a21367f9ad85b3',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '22113301',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Game of Thrones - Trailer',
|
||||||
|
},
|
||||||
|
'expected_warnings': ['Unknown MIME type application/mp4 in DASH manifest'],
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
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']
|
||||||
|
return self._extract_info(urljoin(url, location_path), display_id)
|
||||||
|
@ -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']
|
||||||
|
|
||||||
|
@ -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')
|
||||||
|
@ -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+)
|
||||||
|
@ -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')
|
||||||
|
@ -6,8 +6,8 @@ import re
|
|||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_str
|
from ..compat import compat_str
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
clean_html,
|
||||||
determine_ext,
|
determine_ext,
|
||||||
extract_attributes,
|
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
@ -19,6 +19,7 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class LecturioBaseIE(InfoExtractor):
|
class LecturioBaseIE(InfoExtractor):
|
||||||
|
_API_BASE_URL = 'https://app.lecturio.com/api/en/latest/html5/'
|
||||||
_LOGIN_URL = 'https://app.lecturio.com/en/login'
|
_LOGIN_URL = 'https://app.lecturio.com/en/login'
|
||||||
_NETRC_MACHINE = 'lecturio'
|
_NETRC_MACHINE = 'lecturio'
|
||||||
|
|
||||||
@ -67,51 +68,56 @@ class LecturioIE(LecturioBaseIE):
|
|||||||
_VALID_URL = r'''(?x)
|
_VALID_URL = r'''(?x)
|
||||||
https://
|
https://
|
||||||
(?:
|
(?:
|
||||||
app\.lecturio\.com/[^/]+/(?P<id>[^/?#&]+)\.lecture|
|
app\.lecturio\.com/([^/]+/(?P<nt>[^/?#&]+)\.lecture|(?:\#/)?lecture/c/\d+/(?P<id>\d+))|
|
||||||
(?:www\.)?lecturio\.de/[^/]+/(?P<id_de>[^/?#&]+)\.vortrag
|
(?:www\.)?lecturio\.de/[^/]+/(?P<nt_de>[^/?#&]+)\.vortrag
|
||||||
)
|
)
|
||||||
'''
|
'''
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'https://app.lecturio.com/medical-courses/important-concepts-and-terms-introduction-to-microbiology.lecture#tab/videos',
|
'url': 'https://app.lecturio.com/medical-courses/important-concepts-and-terms-introduction-to-microbiology.lecture#tab/videos',
|
||||||
'md5': 'f576a797a5b7a5e4e4bbdfc25a6a6870',
|
'md5': '9a42cf1d8282a6311bf7211bbde26fde',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '39634',
|
'id': '39634',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Important Concepts and Terms – Introduction to Microbiology',
|
'title': 'Important Concepts and Terms — Introduction to Microbiology',
|
||||||
},
|
},
|
||||||
'skip': 'Requires lecturio account credentials',
|
'skip': 'Requires lecturio account credentials',
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://www.lecturio.de/jura/oeffentliches-recht-staatsexamen.vortrag',
|
'url': 'https://www.lecturio.de/jura/oeffentliches-recht-staatsexamen.vortrag',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://app.lecturio.com/#/lecture/c/6434/39634',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
_CC_LANGS = {
|
_CC_LANGS = {
|
||||||
|
'Arabic': 'ar',
|
||||||
|
'Bulgarian': 'bg',
|
||||||
'German': 'de',
|
'German': 'de',
|
||||||
'English': 'en',
|
'English': 'en',
|
||||||
'Spanish': 'es',
|
'Spanish': 'es',
|
||||||
|
'Persian': 'fa',
|
||||||
'French': 'fr',
|
'French': 'fr',
|
||||||
|
'Japanese': 'ja',
|
||||||
'Polish': 'pl',
|
'Polish': 'pl',
|
||||||
|
'Pashto': 'ps',
|
||||||
'Russian': 'ru',
|
'Russian': 'ru',
|
||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
display_id = mobj.group('id') or mobj.group('id_de')
|
nt = mobj.group('nt') or mobj.group('nt_de')
|
||||||
|
lecture_id = mobj.group('id')
|
||||||
webpage = self._download_webpage(
|
display_id = nt or lecture_id
|
||||||
'https://app.lecturio.com/en/lecture/%s/player.html' % display_id,
|
api_path = 'lectures/' + lecture_id if lecture_id else 'lecture/' + nt + '.json'
|
||||||
display_id)
|
video = self._download_json(
|
||||||
|
self._API_BASE_URL + api_path, display_id)
|
||||||
lecture_id = self._search_regex(
|
|
||||||
r'lecture_id\s*=\s*(?:L_)?(\d+)', webpage, 'lecture id')
|
|
||||||
|
|
||||||
api_url = self._search_regex(
|
|
||||||
r'lectureDataLink\s*:\s*(["\'])(?P<url>(?:(?!\1).)+)\1', webpage,
|
|
||||||
'api url', group='url')
|
|
||||||
|
|
||||||
video = self._download_json(api_url, display_id)
|
|
||||||
|
|
||||||
title = video['title'].strip()
|
title = video['title'].strip()
|
||||||
|
if not lecture_id:
|
||||||
|
pid = video.get('productId') or video.get('uid')
|
||||||
|
if pid:
|
||||||
|
spid = pid.split('_')
|
||||||
|
if spid and len(spid) == 2:
|
||||||
|
lecture_id = spid[1]
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for format_ in video['content']['media']:
|
for format_ in video['content']['media']:
|
||||||
@ -129,24 +135,30 @@ class LecturioIE(LecturioBaseIE):
|
|||||||
continue
|
continue
|
||||||
label = str_or_none(format_.get('label'))
|
label = str_or_none(format_.get('label'))
|
||||||
filesize = int_or_none(format_.get('fileSize'))
|
filesize = int_or_none(format_.get('fileSize'))
|
||||||
formats.append({
|
f = {
|
||||||
'url': file_url,
|
'url': file_url,
|
||||||
'format_id': label,
|
'format_id': label,
|
||||||
'filesize': float_or_none(filesize, invscale=1000)
|
'filesize': float_or_none(filesize, invscale=1000)
|
||||||
})
|
}
|
||||||
|
if label:
|
||||||
|
mobj = re.match(r'(\d+)p\s*\(([^)]+)\)', label)
|
||||||
|
if mobj:
|
||||||
|
f.update({
|
||||||
|
'format_id': mobj.group(2),
|
||||||
|
'height': int(mobj.group(1)),
|
||||||
|
})
|
||||||
|
formats.append(f)
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
subtitles = {}
|
subtitles = {}
|
||||||
automatic_captions = {}
|
automatic_captions = {}
|
||||||
cc = self._parse_json(
|
captions = video.get('captions') or []
|
||||||
self._search_regex(
|
for cc in captions:
|
||||||
r'subtitleUrls\s*:\s*({.+?})\s*,', webpage, 'subtitles',
|
cc_url = cc.get('url')
|
||||||
default='{}'), display_id, fatal=False)
|
|
||||||
for cc_label, cc_url in cc.items():
|
|
||||||
cc_url = url_or_none(cc_url)
|
|
||||||
if not cc_url:
|
if not cc_url:
|
||||||
continue
|
continue
|
||||||
lang = self._search_regex(
|
cc_label = cc.get('translatedCode')
|
||||||
|
lang = cc.get('languageCode') or self._search_regex(
|
||||||
r'/([a-z]{2})_', cc_url, 'lang',
|
r'/([a-z]{2})_', cc_url, 'lang',
|
||||||
default=cc_label.split()[0] if cc_label else 'en')
|
default=cc_label.split()[0] if cc_label else 'en')
|
||||||
original_lang = self._search_regex(
|
original_lang = self._search_regex(
|
||||||
@ -160,7 +172,7 @@ class LecturioIE(LecturioBaseIE):
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': lecture_id,
|
'id': lecture_id or nt,
|
||||||
'title': title,
|
'title': title,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
'subtitles': subtitles,
|
'subtitles': subtitles,
|
||||||
@ -169,37 +181,40 @@ class LecturioIE(LecturioBaseIE):
|
|||||||
|
|
||||||
|
|
||||||
class LecturioCourseIE(LecturioBaseIE):
|
class LecturioCourseIE(LecturioBaseIE):
|
||||||
_VALID_URL = r'https://app\.lecturio\.com/[^/]+/(?P<id>[^/?#&]+)\.course'
|
_VALID_URL = r'https://app\.lecturio\.com/(?:[^/]+/(?P<nt>[^/?#&]+)\.course|(?:#/)?course/c/(?P<id>\d+))'
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
'url': 'https://app.lecturio.com/medical-courses/microbiology-introduction.course#/',
|
'url': 'https://app.lecturio.com/medical-courses/microbiology-introduction.course#/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'microbiology-introduction',
|
'id': 'microbiology-introduction',
|
||||||
'title': 'Microbiology: Introduction',
|
'title': 'Microbiology: Introduction',
|
||||||
|
'description': 'md5:13da8500c25880c6016ae1e6d78c386a',
|
||||||
},
|
},
|
||||||
'playlist_count': 45,
|
'playlist_count': 45,
|
||||||
'skip': 'Requires lecturio account credentials',
|
'skip': 'Requires lecturio account credentials',
|
||||||
}
|
}, {
|
||||||
|
'url': 'https://app.lecturio.com/#/course/c/6434',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
display_id = self._match_id(url)
|
nt, course_id = re.match(self._VALID_URL, url).groups()
|
||||||
|
display_id = nt or course_id
|
||||||
webpage = self._download_webpage(url, display_id)
|
api_path = 'courses/' + course_id if course_id else 'course/content/' + nt + '.json'
|
||||||
|
course = self._download_json(
|
||||||
|
self._API_BASE_URL + api_path, display_id)
|
||||||
entries = []
|
entries = []
|
||||||
for mobj in re.finditer(
|
for lecture in course.get('lectures', []):
|
||||||
r'(?s)<[^>]+\bdata-url=(["\'])(?:(?!\1).)+\.lecture\b[^>]+>',
|
lecture_id = str_or_none(lecture.get('id'))
|
||||||
webpage):
|
lecture_url = lecture.get('url')
|
||||||
params = extract_attributes(mobj.group(0))
|
if lecture_url:
|
||||||
lecture_url = urljoin(url, params.get('data-url'))
|
lecture_url = urljoin(url, lecture_url)
|
||||||
lecture_id = params.get('data-id')
|
else:
|
||||||
|
lecture_url = 'https://app.lecturio.com/#/lecture/c/%s/%s' % (course_id, lecture_id)
|
||||||
entries.append(self.url_result(
|
entries.append(self.url_result(
|
||||||
lecture_url, ie=LecturioIE.ie_key(), video_id=lecture_id))
|
lecture_url, ie=LecturioIE.ie_key(), video_id=lecture_id))
|
||||||
|
return self.playlist_result(
|
||||||
title = self._search_regex(
|
entries, display_id, course.get('title'),
|
||||||
r'<span[^>]+class=["\']content-title[^>]+>([^<]+)', webpage,
|
clean_html(course.get('description')))
|
||||||
'title', default=None)
|
|
||||||
|
|
||||||
return self.playlist_result(entries, display_id, title)
|
|
||||||
|
|
||||||
|
|
||||||
class LecturioDeCourseIE(LecturioBaseIE):
|
class LecturioDeCourseIE(LecturioBaseIE):
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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')
|
||||||
|
@ -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,112 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from .common import InfoExtractor
|
|
||||||
from ..utils import (
|
|
||||||
clean_html,
|
|
||||||
determine_ext,
|
|
||||||
int_or_none,
|
|
||||||
qualities,
|
|
||||||
urlencode_postdata,
|
|
||||||
xpath_text,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NFBIE(InfoExtractor):
|
|
||||||
IE_NAME = 'nfb'
|
|
||||||
IE_DESC = 'National Film Board of Canada'
|
|
||||||
_VALID_URL = r'https?://(?:www\.)?(?:nfb|onf)\.ca/film/(?P<id>[\da-z_-]+)'
|
|
||||||
|
|
||||||
_TEST = {
|
|
||||||
'url': 'https://www.nfb.ca/film/qallunaat_why_white_people_are_funny',
|
|
||||||
'info_dict': {
|
|
||||||
'id': 'qallunaat_why_white_people_are_funny',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'Qallunaat! Why White People Are Funny ',
|
|
||||||
'description': 'md5:6b8e32dde3abf91e58857b174916620c',
|
|
||||||
'duration': 3128,
|
|
||||||
'creator': 'Mark Sandiford',
|
|
||||||
'uploader': 'Mark Sandiford',
|
|
||||||
},
|
|
||||||
'params': {
|
|
||||||
# rtmp download
|
|
||||||
'skip_download': True,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
|
||||||
video_id = self._match_id(url)
|
|
||||||
|
|
||||||
config = self._download_xml(
|
|
||||||
'https://www.nfb.ca/film/%s/player_config' % video_id,
|
|
||||||
video_id, 'Downloading player config XML',
|
|
||||||
data=urlencode_postdata({'getConfig': 'true'}),
|
|
||||||
headers={
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
'X-NFB-Referer': 'http://www.nfb.ca/medias/flash/NFBVideoPlayer.swf'
|
|
||||||
})
|
|
||||||
|
|
||||||
title, description, thumbnail, duration, uploader, author = [None] * 6
|
|
||||||
thumbnails, formats = [[]] * 2
|
|
||||||
subtitles = {}
|
|
||||||
|
|
||||||
for media in config.findall('./player/stream/media'):
|
|
||||||
if media.get('type') == 'posterImage':
|
|
||||||
quality_key = qualities(('low', 'high'))
|
|
||||||
thumbnails = []
|
|
||||||
for asset in media.findall('assets/asset'):
|
|
||||||
asset_url = xpath_text(asset, 'default/url', default=None)
|
|
||||||
if not asset_url:
|
|
||||||
continue
|
|
||||||
quality = asset.get('quality')
|
|
||||||
thumbnails.append({
|
|
||||||
'url': asset_url,
|
|
||||||
'id': quality,
|
|
||||||
'preference': quality_key(quality),
|
|
||||||
})
|
|
||||||
elif media.get('type') == 'video':
|
|
||||||
title = xpath_text(media, 'title', fatal=True)
|
|
||||||
for asset in media.findall('assets/asset'):
|
|
||||||
quality = asset.get('quality')
|
|
||||||
height = int_or_none(self._search_regex(
|
|
||||||
r'^(\d+)[pP]$', quality or '', 'height', default=None))
|
|
||||||
for node in asset:
|
|
||||||
streamer = xpath_text(node, 'streamerURI', default=None)
|
|
||||||
if not streamer:
|
|
||||||
continue
|
|
||||||
play_path = xpath_text(node, 'url', default=None)
|
|
||||||
if not play_path:
|
|
||||||
continue
|
|
||||||
formats.append({
|
|
||||||
'url': streamer,
|
|
||||||
'app': streamer.split('/', 3)[3],
|
|
||||||
'play_path': play_path,
|
|
||||||
'rtmp_live': False,
|
|
||||||
'ext': 'flv',
|
|
||||||
'format_id': '%s-%s' % (node.tag, quality) if quality else node.tag,
|
|
||||||
'height': height,
|
|
||||||
})
|
|
||||||
self._sort_formats(formats)
|
|
||||||
description = clean_html(xpath_text(media, 'description'))
|
|
||||||
uploader = xpath_text(media, 'author')
|
|
||||||
duration = int_or_none(media.get('duration'))
|
|
||||||
for subtitle in media.findall('./subtitles/subtitle'):
|
|
||||||
subtitle_url = xpath_text(subtitle, 'url', default=None)
|
|
||||||
if not subtitle_url:
|
|
||||||
continue
|
|
||||||
lang = xpath_text(subtitle, 'lang', default='en')
|
|
||||||
subtitles.setdefault(lang, []).append({
|
|
||||||
'url': subtitle_url,
|
|
||||||
'ext': (subtitle.get('format') or determine_ext(subtitle_url)).lower(),
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
'id': video_id,
|
|
||||||
'title': title,
|
|
||||||
'description': description,
|
|
||||||
'thumbnails': thumbnails,
|
|
||||||
'duration': duration,
|
|
||||||
'creator': uploader,
|
|
||||||
'uploader': uploader,
|
|
||||||
'formats': formats,
|
|
||||||
'subtitles': subtitles,
|
|
||||||
}
|
|
@ -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,
|
|
||||||
}]
|
|
@ -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]
|
||||||
|
|
||||||
|
@ -42,9 +42,9 @@ def cookie_to_dict(cookie):
|
|||||||
if cookie.discard is not None:
|
if cookie.discard is not None:
|
||||||
cookie_dict['discard'] = cookie.discard
|
cookie_dict['discard'] = cookie.discard
|
||||||
try:
|
try:
|
||||||
if (cookie.has_nonstandard_attr('httpOnly') or
|
if (cookie.has_nonstandard_attr('httpOnly')
|
||||||
cookie.has_nonstandard_attr('httponly') or
|
or cookie.has_nonstandard_attr('httponly')
|
||||||
cookie.has_nonstandard_attr('HttpOnly')):
|
or cookie.has_nonstandard_attr('HttpOnly')):
|
||||||
cookie_dict['httponly'] = True
|
cookie_dict['httponly'] = True
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
@ -243,7 +243,7 @@ class PhantomJSwrapper(object):
|
|||||||
|
|
||||||
|
|
||||||
class OpenloadIE(InfoExtractor):
|
class OpenloadIE(InfoExtractor):
|
||||||
_DOMAINS = r'(?:openload\.(?:co|io|link|pw)|oload\.(?:tv|stream|site|xyz|win|download|cloud|cc|icu|fun|club|info|pw|live|space))'
|
_DOMAINS = r'(?:openload\.(?:co|io|link|pw)|oload\.(?:tv|biz|stream|site|xyz|win|download|cloud|cc|icu|fun|club|info|press|pw|life|live|space|services|website)|oladblock\.(?:services|xyz|me)|openloed\.co)'
|
||||||
_VALID_URL = r'''(?x)
|
_VALID_URL = r'''(?x)
|
||||||
https?://
|
https?://
|
||||||
(?P<host>
|
(?P<host>
|
||||||
@ -253,7 +253,10 @@ class OpenloadIE(InfoExtractor):
|
|||||||
(?:f|embed)/
|
(?:f|embed)/
|
||||||
(?P<id>[a-zA-Z0-9-_]+)
|
(?P<id>[a-zA-Z0-9-_]+)
|
||||||
''' % _DOMAINS
|
''' % _DOMAINS
|
||||||
|
_EMBED_WORD = 'embed'
|
||||||
|
_STREAM_WORD = 'f'
|
||||||
|
_REDIR_WORD = 'stream'
|
||||||
|
_URL_IDS = ('streamurl', 'streamuri', 'streamurj')
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'https://openload.co/f/kUEfGclsU9o',
|
'url': 'https://openload.co/f/kUEfGclsU9o',
|
||||||
'md5': 'bf1c059b004ebc7a256f89408e65c36e',
|
'md5': 'bf1c059b004ebc7a256f89408e65c36e',
|
||||||
@ -350,15 +353,45 @@ class OpenloadIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'https://oload.space/f/IY4eZSst3u8/',
|
'url': 'https://oload.space/f/IY4eZSst3u8/',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://oload.services/embed/bs1NWj1dCag/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://oload.press/embed/drTBl1aOTvk/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://oload.website/embed/drTBl1aOTvk/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://oload.life/embed/oOzZjNPw9Dc/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://oload.biz/f/bEk3Gp8ARr4/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://oladblock.services/f/b8NWEgkqNLI/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://oladblock.xyz/f/b8NWEgkqNLI/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://oladblock.me/f/b8NWEgkqNLI/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://openloed.co/f/b8NWEgkqNLI/',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
|
@classmethod
|
||||||
|
def _extract_urls(cls, webpage):
|
||||||
@staticmethod
|
|
||||||
def _extract_urls(webpage):
|
|
||||||
return re.findall(
|
return re.findall(
|
||||||
r'<iframe[^>]+src=["\']((?:https?://)?%s/embed/[a-zA-Z0-9-_]+)'
|
r'<iframe[^>]+src=["\']((?:https?://)?%s/%s/[a-zA-Z0-9-_]+)'
|
||||||
% OpenloadIE._DOMAINS, webpage)
|
% (cls._DOMAINS, cls._EMBED_WORD), webpage)
|
||||||
|
|
||||||
|
def _extract_decrypted_page(self, page_url, webpage, video_id):
|
||||||
|
phantom = PhantomJSwrapper(self, required_version='2.0')
|
||||||
|
webpage, _ = phantom.get(page_url, html=webpage, video_id=video_id)
|
||||||
|
return webpage
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
@ -366,16 +399,13 @@ class OpenloadIE(InfoExtractor):
|
|||||||
video_id = mobj.group('id')
|
video_id = mobj.group('id')
|
||||||
|
|
||||||
url_pattern = 'https://%s/%%s/%s/' % (host, video_id)
|
url_pattern = 'https://%s/%%s/%s/' % (host, video_id)
|
||||||
headers = {
|
|
||||||
'User-Agent': self._USER_AGENT,
|
|
||||||
}
|
|
||||||
|
|
||||||
for path in ('embed', 'f'):
|
for path in (self._EMBED_WORD, self._STREAM_WORD):
|
||||||
page_url = url_pattern % path
|
page_url = url_pattern % path
|
||||||
last = path == 'f'
|
last = path == self._STREAM_WORD
|
||||||
webpage = self._download_webpage(
|
webpage = self._download_webpage(
|
||||||
page_url, video_id, 'Downloading %s webpage' % path,
|
page_url, video_id, 'Downloading %s webpage' % path,
|
||||||
headers=headers, fatal=last)
|
fatal=last)
|
||||||
if not webpage:
|
if not webpage:
|
||||||
continue
|
continue
|
||||||
if 'File not found' in webpage or 'deleted by the owner' in webpage:
|
if 'File not found' in webpage or 'deleted by the owner' in webpage:
|
||||||
@ -384,21 +414,20 @@ class OpenloadIE(InfoExtractor):
|
|||||||
raise ExtractorError('File not found', expected=True, video_id=video_id)
|
raise ExtractorError('File not found', expected=True, video_id=video_id)
|
||||||
break
|
break
|
||||||
|
|
||||||
phantom = PhantomJSwrapper(self, required_version='2.0')
|
webpage = self._extract_decrypted_page(page_url, webpage, video_id)
|
||||||
webpage, _ = phantom.get(page_url, html=webpage, video_id=video_id, headers=headers)
|
for element_id in self._URL_IDS:
|
||||||
|
decoded_id = get_element_by_id(element_id, webpage)
|
||||||
decoded_id = (get_element_by_id('streamurl', webpage) or
|
if decoded_id:
|
||||||
get_element_by_id('streamuri', webpage) or
|
break
|
||||||
get_element_by_id('streamurj', webpage) or
|
if not decoded_id:
|
||||||
self._search_regex(
|
decoded_id = self._search_regex(
|
||||||
(r'>\s*([\w-]+~\d{10,}~\d+\.\d+\.0\.0~[\w-]+)\s*<',
|
(r'>\s*([\w-]+~\d{10,}~\d+\.\d+\.0\.0~[\w-]+)\s*<',
|
||||||
r'>\s*([\w~-]+~\d+\.\d+\.\d+\.\d+~[\w~-]+)',
|
r'>\s*([\w~-]+~\d+\.\d+\.\d+\.\d+~[\w~-]+)',
|
||||||
r'>\s*([\w-]+~\d{10,}~(?:[a-f\d]+:){2}:~[\w-]+)\s*<',
|
r'>\s*([\w-]+~\d{10,}~(?:[a-f\d]+:){2}:~[\w-]+)\s*<',
|
||||||
r'>\s*([\w~-]+~[a-f0-9:]+~[\w~-]+)\s*<',
|
r'>\s*([\w~-]+~[a-f0-9:]+~[\w~-]+)\s*<',
|
||||||
r'>\s*([\w~-]+~[a-f0-9:]+~[\w~-]+)'), webpage,
|
r'>\s*([\w~-]+~[a-f0-9:]+~[\w~-]+)'), webpage,
|
||||||
'stream URL'))
|
'stream URL')
|
||||||
|
video_url = 'https://%s/%s/%s?mime=true' % (host, self._REDIR_WORD, decoded_id)
|
||||||
video_url = 'https://%s/stream/%s?mime=true' % (host, decoded_id)
|
|
||||||
|
|
||||||
title = self._og_search_title(webpage, default=None) or self._search_regex(
|
title = self._og_search_title(webpage, default=None) or self._search_regex(
|
||||||
r'<span[^>]+class=["\']title["\'][^>]*>([^<]+)', webpage,
|
r'<span[^>]+class=["\']title["\'][^>]*>([^<]+)', webpage,
|
||||||
@ -416,5 +445,39 @@ class OpenloadIE(InfoExtractor):
|
|||||||
'url': video_url,
|
'url': video_url,
|
||||||
'ext': determine_ext(title, None) or determine_ext(url, 'mp4'),
|
'ext': determine_ext(title, None) or determine_ext(url, 'mp4'),
|
||||||
'subtitles': subtitles,
|
'subtitles': subtitles,
|
||||||
'http_headers': headers,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class VerystreamIE(OpenloadIE):
|
||||||
|
IE_NAME = 'verystream'
|
||||||
|
|
||||||
|
_DOMAINS = r'(?:verystream\.com)'
|
||||||
|
_VALID_URL = r'''(?x)
|
||||||
|
https?://
|
||||||
|
(?P<host>
|
||||||
|
(?:www\.)?
|
||||||
|
%s
|
||||||
|
)/
|
||||||
|
(?:stream|e)/
|
||||||
|
(?P<id>[a-zA-Z0-9-_]+)
|
||||||
|
''' % _DOMAINS
|
||||||
|
_EMBED_WORD = 'e'
|
||||||
|
_STREAM_WORD = 'stream'
|
||||||
|
_REDIR_WORD = 'gettoken'
|
||||||
|
_URL_IDS = ('videolink', )
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://verystream.com/stream/c1GWQ9ngBBx/',
|
||||||
|
'md5': 'd3e8c5628ccb9970b65fd65269886795',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'c1GWQ9ngBBx',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Big Buck Bunny.mp4',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://verystream.com/e/c1GWQ9ngBBx/',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _extract_decrypted_page(self, page_url, webpage, video_id):
|
||||||
|
return webpage # for Verystream, the webpage is already decrypted
|
||||||
|
@ -168,7 +168,7 @@ class PeerTubeIE(InfoExtractor):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _extract_peertube_url(webpage, source_url):
|
def _extract_peertube_url(webpage, source_url):
|
||||||
mobj = re.match(
|
mobj = re.match(
|
||||||
r'https?://(?P<host>[^/]+)/videos/watch/(?P<id>%s)'
|
r'https?://(?P<host>[^/]+)/videos/(?:watch|embed)/(?P<id>%s)'
|
||||||
% PeerTubeIE._UUID_RE, source_url)
|
% PeerTubeIE._UUID_RE, source_url)
|
||||||
if mobj and any(p in webpage for p in (
|
if mobj and any(p in webpage for p in (
|
||||||
'<title>PeerTube<',
|
'<title>PeerTube<',
|
||||||
|
@ -50,8 +50,8 @@ class PodomaticIE(InfoExtractor):
|
|||||||
video_id = mobj.group('id')
|
video_id = mobj.group('id')
|
||||||
channel = mobj.group('channel') or mobj.group('channel_2')
|
channel = mobj.group('channel') or mobj.group('channel_2')
|
||||||
|
|
||||||
json_url = (('%s://%s.podomatic.com/entry/embed_params/%s' +
|
json_url = (('%s://%s.podomatic.com/entry/embed_params/%s'
|
||||||
'?permalink=true&rtmp=0') %
|
+ '?permalink=true&rtmp=0') %
|
||||||
(mobj.group('proto'), channel, video_id))
|
(mobj.group('proto'), channel, video_id))
|
||||||
data_json = self._download_webpage(
|
data_json = self._download_webpage(
|
||||||
json_url, video_id, 'Downloading video info')
|
json_url, video_id, 'Downloading video info')
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user