mirror of
https://github.com/ytdl-org/youtube-dl
synced 2025-10-20 23:28:37 +09:00
Compare commits
69 Commits
2019.04.17
...
2019.05.11
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8ae32e1882 | ||
![]() |
07a2cef782 | ||
![]() |
8ed7a23328 | ||
![]() |
3089bc748c | ||
![]() |
d23e85515a | ||
![]() |
cd37ef44e9 | ||
![]() |
169f8d0fe1 | ||
![]() |
0db2b275dd | ||
![]() |
ab11674502 | ||
![]() |
68b92aa1b4 | ||
![]() |
fd35d8cdfd | ||
![]() |
4eec112740 | ||
![]() |
027ffdca0d | ||
![]() |
a5b92d3590 | ||
![]() |
b45a9e698e | ||
![]() |
71ebd35d50 | ||
![]() |
f8c55c6664 | ||
![]() |
876fed6bf3 | ||
![]() |
c9856648db | ||
![]() |
2533f5b691 | ||
![]() |
e0dde1d8e2 | ||
![]() |
62d10f0d32 | ||
![]() |
274519dd08 | ||
![]() |
091200c368 | ||
![]() |
67bfbe4942 | ||
![]() |
54f3b61216 | ||
![]() |
a61ce71468 | ||
![]() |
026fbedc85 | ||
![]() |
6e07b5a6d5 | ||
![]() |
c464e1df2c | ||
![]() |
92bc97d398 | ||
![]() |
f916abc0ac | ||
![]() |
24510bdcfa | ||
![]() |
ae8c13565e | ||
![]() |
280913800d | ||
![]() |
7ff8ad80f1 | ||
![]() |
4e4db743e7 | ||
![]() |
3545d38bfb | ||
![]() |
2309d6bf92 | ||
![]() |
822b9d9cb0 | ||
![]() |
5caabd3c70 | ||
![]() |
aa05a093bb | ||
![]() |
60e67c5b2c | ||
![]() |
eefa0f2157 | ||
![]() |
6f366ef30c | ||
![]() |
88b547492f | ||
![]() |
00a9a25cf9 | ||
![]() |
97abf05ad3 | ||
![]() |
da668a23bd | ||
![]() |
58ef5e7881 | ||
![]() |
3e7ec5330a | ||
![]() |
98933c14e1 | ||
![]() |
56667d622c | ||
![]() |
50d660479d | ||
![]() |
1fa8893734 | ||
![]() |
15be3eb5e5 | ||
![]() |
e09965d550 | ||
![]() |
3fd86cfe13 | ||
![]() |
fdc2183650 | ||
![]() |
85b6335d55 | ||
![]() |
c25720ef6a | ||
![]() |
c9b19d7a55 | ||
![]() |
47cfa00516 | ||
![]() |
061d1cd948 | ||
![]() |
5de538787d | ||
![]() |
9abeefd527 | ||
![]() |
f3914b06a0 | ||
![]() |
81d989c21e | ||
![]() |
cd6c75b05f |
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.05.11. 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.05.11**
|
||||
- [ ] 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.05.11
|
||||
[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.05.11. 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.05.11**
|
||||
- [ ] 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.05.11. 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.05.11**
|
||||
- [ ] 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.05.11. 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.05.11**
|
||||
- [ ] 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.05.11
|
||||
[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.05.11. 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.05.11**
|
||||
- [ ] 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
|
68
ChangeLog
68
ChangeLog
@@ -1,4 +1,70 @@
|
||||
version <unreleased>
|
||||
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)
|
||||
|
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
|
||||
|
||||
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 "*.class" -delete
|
||||
|
||||
@@ -78,8 +78,12 @@ README.md: youtube_dl/*.py youtube_dl/*/*.py
|
||||
CONTRIBUTING.md: README.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
|
||||
$(PYTHON) devscripts/make_issue_template.py .github/ISSUE_TEMPLATE_tmpl.md .github/ISSUE_TEMPLATE.md
|
||||
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/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:
|
||||
$(PYTHON) devscripts/make_supportedsites.py docs/supportedsites.md
|
||||
|
@@ -45,12 +45,12 @@ for test in gettestcases():
|
||||
|
||||
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
|
||||
test['info_dict']['age_limit'] != 18):
|
||||
if RESULT and ('info_dict' not in test or 'age_limit' not in test['info_dict']
|
||||
or test['info_dict']['age_limit'] != 18):
|
||||
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
|
||||
test['info_dict']['age_limit'] == 18):
|
||||
elif not RESULT and ('info_dict' in test and 'age_limit' in test['info_dict']
|
||||
and test['info_dict']['age_limit'] == 18):
|
||||
print('\nPotential false negative: {0}'.format(test['name']))
|
||||
|
||||
else:
|
||||
|
@@ -78,8 +78,8 @@ sed -i "s/__version__ = '.*'/__version__ = '$version'/" youtube_dl/version.py
|
||||
sed -i "s/<unreleased>/$version/" ChangeLog
|
||||
|
||||
/bin/echo -e "\n### Committing documentation, templates and youtube_dl/version.py..."
|
||||
make README.md CONTRIBUTING.md .github/ISSUE_TEMPLATE.md supportedsites
|
||||
git add README.md CONTRIBUTING.md .github/ISSUE_TEMPLATE.md docs/supportedsites.md youtube_dl/version.py ChangeLog
|
||||
make README.md CONTRIBUTING.md issuetemplates supportedsites
|
||||
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"
|
||||
|
||||
/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
|
||||
- **AnimeOnDemand**
|
||||
- **Anvato**
|
||||
- **aol.com**
|
||||
- **APA**
|
||||
- **Aparat**
|
||||
- **AppleConnect**
|
||||
@@ -163,6 +164,7 @@
|
||||
- **chirbit**
|
||||
- **chirbit:profile**
|
||||
- **Cinchcast**
|
||||
- **Cinemax**
|
||||
- **CiscoLiveSearch**
|
||||
- **CiscoLiveSession**
|
||||
- **CJSW**
|
||||
@@ -200,6 +202,7 @@
|
||||
- **CSpan**: C-SPAN
|
||||
- **CtsNews**: 華視新聞
|
||||
- **CTVNews**
|
||||
- **cu.ntv.co.jp**: Nippon Television Network
|
||||
- **Culturebox**
|
||||
- **CultureUnplugged**
|
||||
- **curiositystream**
|
||||
@@ -235,8 +238,6 @@
|
||||
- **DouyuTV**: 斗鱼
|
||||
- **DPlay**
|
||||
- **DPlayIt**
|
||||
- **dramafever**
|
||||
- **dramafever:series**
|
||||
- **DRBonanza**
|
||||
- **Dropbox**
|
||||
- **DrTuber**
|
||||
@@ -486,6 +487,7 @@
|
||||
- **MatchTV**
|
||||
- **MDR**: MDR.DE and KiKA
|
||||
- **media.ccc.de**
|
||||
- **media.ccc.de:lists**
|
||||
- **Medialaan**
|
||||
- **Mediaset**
|
||||
- **Mediasite**
|
||||
@@ -623,6 +625,7 @@
|
||||
- **NRKTVEpisodes**
|
||||
- **NRKTVSeason**
|
||||
- **NRKTVSeries**
|
||||
- **NRLTV**
|
||||
- **ntv.ru**
|
||||
- **Nuvid**
|
||||
- **NYTimes**
|
||||
@@ -632,7 +635,6 @@
|
||||
- **OdaTV**
|
||||
- **Odnoklassniki**
|
||||
- **OktoberfestTV**
|
||||
- **on.aol.com**
|
||||
- **OnDemandKorea**
|
||||
- **onet.pl**
|
||||
- **onet.tv**
|
||||
@@ -803,6 +805,7 @@
|
||||
- **ShowRoomLive**
|
||||
- **Sina**
|
||||
- **SkylineWebcams**
|
||||
- **SkyNews**
|
||||
- **skynewsarabia:article**
|
||||
- **skynewsarabia:video**
|
||||
- **SkySports**
|
||||
@@ -853,7 +856,10 @@
|
||||
- **StreamCZ**
|
||||
- **StreetVoice**
|
||||
- **StretchInternet**
|
||||
- **stv:player**
|
||||
- **SunPorno**
|
||||
- **sverigesradio:episode**
|
||||
- **sverigesradio:publication**
|
||||
- **SVT**
|
||||
- **SVTPage**
|
||||
- **SVTPlay**: SVT Play and Öppet arkiv
|
||||
@@ -994,6 +1000,7 @@
|
||||
- **Vbox7**
|
||||
- **VeeHD**
|
||||
- **Veoh**
|
||||
- **verystream**
|
||||
- **Vessel**
|
||||
- **Vesti**: Вести.Ru
|
||||
- **Vevo**
|
||||
@@ -1099,8 +1106,6 @@
|
||||
- **Wistia**
|
||||
- **wnl**: npo.nl, ntr.nl, omroepwnl.nl, zapp.nl and npo3.nl
|
||||
- **WorldStarHipHop**
|
||||
- **wrzuta.pl**
|
||||
- **wrzuta.pl:playlist**
|
||||
- **WSJ**: Wall Street Journal
|
||||
- **WSJArticle**
|
||||
- **WWE**
|
||||
@@ -1124,6 +1129,8 @@
|
||||
- **XVideos**
|
||||
- **XXXYMovies**
|
||||
- **Yahoo**: Yahoo screen and movies
|
||||
- **yahoo:gyao**
|
||||
- **yahoo:gyao:player**
|
||||
- **YandexDisk**
|
||||
- **yandexmusic:album**: Яндекс.Музыка - Альбом
|
||||
- **yandexmusic:playlist**: Яндекс.Музыка - Плейлист
|
||||
|
@@ -3,4 +3,4 @@ universal = True
|
||||
|
||||
[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
|
||||
ignore = E402,E501,E731,E741
|
||||
ignore = E402,E501,E731,E741,W503
|
||||
|
@@ -44,16 +44,16 @@ class TestAES(unittest.TestCase):
|
||||
def test_decrypt_text(self):
|
||||
password = intlist_to_bytes(self.key).decode('utf-8')
|
||||
encrypted = base64.b64encode(
|
||||
intlist_to_bytes(self.iv[:8]) +
|
||||
b'\x17\x15\x93\xab\x8d\x80V\xcdV\xe0\t\xcdo\xc2\xa5\xd8ksM\r\xe27N\xae'
|
||||
intlist_to_bytes(self.iv[:8])
|
||||
+ b'\x17\x15\x93\xab\x8d\x80V\xcdV\xe0\t\xcdo\xc2\xa5\xd8ksM\r\xe27N\xae'
|
||||
).decode('utf-8')
|
||||
decrypted = (aes_decrypt_text(encrypted, password, 16))
|
||||
self.assertEqual(decrypted, self.secret_msg)
|
||||
|
||||
password = intlist_to_bytes(self.key).decode('utf-8')
|
||||
encrypted = base64.b64encode(
|
||||
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'
|
||||
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'
|
||||
).decode('utf-8')
|
||||
decrypted = (aes_decrypt_text(encrypted, password, 32))
|
||||
self.assertEqual(decrypted, self.secret_msg)
|
||||
|
@@ -34,8 +34,8 @@ def _make_testfunc(testfile):
|
||||
def test_func(self):
|
||||
as_file = os.path.join(TEST_DIR, testfile)
|
||||
swf_file = os.path.join(TEST_DIR, test_id + '.swf')
|
||||
if ((not os.path.exists(swf_file)) or
|
||||
os.path.getmtime(swf_file) < os.path.getmtime(as_file)):
|
||||
if ((not os.path.exists(swf_file))
|
||||
or os.path.getmtime(swf_file) < os.path.getmtime(as_file)):
|
||||
# Recompile
|
||||
try:
|
||||
subprocess.check_call([
|
||||
|
@@ -183,7 +183,7 @@ class TestUtil(unittest.TestCase):
|
||||
|
||||
self.assertEqual(sanitize_filename(
|
||||
'ÂÃÄÀÁÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖŐØŒÙÚÛÜŰÝÞßàáâãäåæçèéêëìíîïðñòóôõöőøœùúûüűýþÿ', restricted=True),
|
||||
'AAAAAAAECEEEEIIIIDNOOOOOOOOEUUUUUYPssaaaaaaaeceeeeiiiionooooooooeuuuuuypy')
|
||||
'AAAAAAAECEEEEIIIIDNOOOOOOOOEUUUUUYTHssaaaaaaaeceeeeiiiionooooooooeuuuuuythy')
|
||||
|
||||
def test_sanitize_ids(self):
|
||||
self.assertEqual(sanitize_filename('_n_cd26wFpw', is_id=True), '_n_cd26wFpw')
|
||||
|
@@ -400,9 +400,9 @@ class YoutubeDL(object):
|
||||
else:
|
||||
raise
|
||||
|
||||
if (sys.platform != 'win32' and
|
||||
sys.getfilesystemencoding() in ['ascii', 'ANSI_X3.4-1968'] and
|
||||
not params.get('restrictfilenames', False)):
|
||||
if (sys.platform != 'win32'
|
||||
and sys.getfilesystemencoding() in ['ascii', 'ANSI_X3.4-1968']
|
||||
and not params.get('restrictfilenames', False)):
|
||||
# Unicode filesystem API will throw errors (#1474, #13027)
|
||||
self.report_warning(
|
||||
'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 idxs:
|
||||
correct_argv = (
|
||||
['youtube-dl'] +
|
||||
[a for i, a in enumerate(argv) if i not in idxs] +
|
||||
['--'] + [argv[i] for i in idxs]
|
||||
['youtube-dl']
|
||||
+ [a for i, a in enumerate(argv) if i not in idxs]
|
||||
+ ['--'] + [argv[i] for i in idxs]
|
||||
)
|
||||
self.report_warning(
|
||||
'Long argument string detected. '
|
||||
@@ -850,8 +850,8 @@ class YoutubeDL(object):
|
||||
if result_type in ('url', 'url_transparent'):
|
||||
ie_result['url'] = sanitize_url(ie_result['url'])
|
||||
extract_flat = self.params.get('extract_flat', False)
|
||||
if ((extract_flat == 'in_playlist' and 'playlist' in extra_info) or
|
||||
extract_flat is True):
|
||||
if ((extract_flat == 'in_playlist' and 'playlist' in extra_info)
|
||||
or extract_flat is True):
|
||||
if self.params.get('forcejson', False):
|
||||
self.to_stdout(json.dumps(ie_result))
|
||||
return ie_result
|
||||
@@ -1619,9 +1619,9 @@ class YoutubeDL(object):
|
||||
# https://github.com/ytdl-org/youtube-dl/issues/10083).
|
||||
incomplete_formats = (
|
||||
# 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(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 = {
|
||||
'formats': formats,
|
||||
@@ -1947,8 +1947,8 @@ class YoutubeDL(object):
|
||||
else:
|
||||
assert fixup_policy in ('ignore', 'never')
|
||||
|
||||
if (info_dict.get('requested_formats') is None and
|
||||
info_dict.get('container') == 'm4a_dash'):
|
||||
if (info_dict.get('requested_formats') is None
|
||||
and info_dict.get('container') == 'm4a_dash'):
|
||||
if fixup_policy == 'warn':
|
||||
self.report_warning(
|
||||
'%s: writing DASH m4a. '
|
||||
@@ -1967,9 +1967,9 @@ class YoutubeDL(object):
|
||||
else:
|
||||
assert fixup_policy in ('ignore', 'never')
|
||||
|
||||
if (info_dict.get('protocol') == 'm3u8_native' or
|
||||
info_dict.get('protocol') == 'm3u8' and
|
||||
self.params.get('hls_prefer_native')):
|
||||
if (info_dict.get('protocol') == 'm3u8_native'
|
||||
or info_dict.get('protocol') == 'm3u8'
|
||||
and self.params.get('hls_prefer_native')):
|
||||
if fixup_policy == 'warn':
|
||||
self.report_warning('%s: malformed AAC bitstream detected.' % (
|
||||
info_dict['id']))
|
||||
@@ -1995,10 +1995,10 @@ class YoutubeDL(object):
|
||||
def download(self, url_list):
|
||||
"""Download a given list of URLs."""
|
||||
outtmpl = self.params.get('outtmpl', DEFAULT_OUTTMPL)
|
||||
if (len(url_list) > 1 and
|
||||
outtmpl != '-' and
|
||||
'%' not in outtmpl and
|
||||
self.params.get('max_downloads') != 1):
|
||||
if (len(url_list) > 1
|
||||
and outtmpl != '-'
|
||||
and '%' not in outtmpl
|
||||
and self.params.get('max_downloads') != 1):
|
||||
raise SameFileError(outtmpl)
|
||||
|
||||
for url in url_list:
|
||||
@@ -2143,8 +2143,8 @@ class YoutubeDL(object):
|
||||
if res:
|
||||
res += ', '
|
||||
res += '%s container' % fdict['container']
|
||||
if (fdict.get('vcodec') is not None and
|
||||
fdict.get('vcodec') != 'none'):
|
||||
if (fdict.get('vcodec') is not None
|
||||
and fdict.get('vcodec') != 'none'):
|
||||
if res:
|
||||
res += ', '
|
||||
res += fdict['vcodec']
|
||||
|
@@ -230,14 +230,14 @@ def _real_main(argv=None):
|
||||
if opts.allsubtitles and not opts.writeautomaticsub:
|
||||
opts.writesubtitles = True
|
||||
|
||||
outtmpl = ((opts.outtmpl is not None and opts.outtmpl) or
|
||||
(opts.format == '-1' and opts.usetitle and '%(title)s-%(id)s-%(format)s.%(ext)s') or
|
||||
(opts.format == '-1' and '%(id)s-%(format)s.%(ext)s') or
|
||||
(opts.usetitle and opts.autonumber and '%(autonumber)s-%(title)s-%(id)s.%(ext)s') or
|
||||
(opts.usetitle and '%(title)s-%(id)s.%(ext)s') or
|
||||
(opts.useid and '%(id)s.%(ext)s') or
|
||||
(opts.autonumber and '%(autonumber)s-%(id)s.%(ext)s') or
|
||||
DEFAULT_OUTTMPL)
|
||||
outtmpl = ((opts.outtmpl is not None and opts.outtmpl)
|
||||
or (opts.format == '-1' and opts.usetitle and '%(title)s-%(id)s-%(format)s.%(ext)s')
|
||||
or (opts.format == '-1' and '%(id)s-%(format)s.%(ext)s')
|
||||
or (opts.usetitle and opts.autonumber and '%(autonumber)s-%(title)s-%(id)s.%(ext)s')
|
||||
or (opts.usetitle and '%(title)s-%(id)s.%(ext)s')
|
||||
or (opts.useid and '%(id)s.%(ext)s')
|
||||
or (opts.autonumber and '%(autonumber)s-%(id)s.%(ext)s')
|
||||
or DEFAULT_OUTTMPL)
|
||||
if not os.path.splitext(outtmpl)[1] and opts.extractaudio:
|
||||
parser.error('Cannot download a video and extract audio into the same'
|
||||
' file! Use "{0}.%(ext)s" instead of "{0}" as the output'
|
||||
|
@@ -2649,9 +2649,9 @@ else:
|
||||
|
||||
try:
|
||||
args = shlex.split('中文')
|
||||
assert (isinstance(args, list) and
|
||||
isinstance(args[0], compat_str) and
|
||||
args[0] == '中文')
|
||||
assert (isinstance(args, list)
|
||||
and isinstance(args[0], compat_str)
|
||||
and args[0] == '中文')
|
||||
compat_shlex_split = shlex.split
|
||||
except (AssertionError, UnicodeEncodeError):
|
||||
# Working around shlex issue with unicode strings on some python 2
|
||||
|
@@ -330,15 +330,15 @@ class FileDownloader(object):
|
||||
"""
|
||||
|
||||
nooverwrites_and_exists = (
|
||||
self.params.get('nooverwrites', False) and
|
||||
os.path.exists(encodeFilename(filename))
|
||||
self.params.get('nooverwrites', False)
|
||||
and os.path.exists(encodeFilename(filename))
|
||||
)
|
||||
|
||||
if not hasattr(filename, 'write'):
|
||||
continuedl_and_exists = (
|
||||
self.params.get('continuedl', True) and
|
||||
os.path.isfile(encodeFilename(filename)) and
|
||||
not self.params.get('nopart', False)
|
||||
self.params.get('continuedl', True)
|
||||
and os.path.isfile(encodeFilename(filename))
|
||||
and not self.params.get('nopart', False)
|
||||
)
|
||||
|
||||
# Check file already present
|
||||
|
@@ -238,8 +238,8 @@ def write_metadata_tag(stream, metadata):
|
||||
|
||||
|
||||
def remove_encrypted_media(media):
|
||||
return list(filter(lambda e: 'drmAdditionalHeaderId' not in e.attrib and
|
||||
'drmAdditionalHeaderSetId' not in e.attrib,
|
||||
return list(filter(lambda e: 'drmAdditionalHeaderId' not in e.attrib
|
||||
and 'drmAdditionalHeaderSetId' not in e.attrib,
|
||||
media))
|
||||
|
||||
|
||||
@@ -267,8 +267,8 @@ class F4mFD(FragmentFD):
|
||||
media = doc.findall(_add_ns('media'))
|
||||
if not media:
|
||||
self.report_error('No media found')
|
||||
for e in (doc.findall(_add_ns('drmAdditionalHeader')) +
|
||||
doc.findall(_add_ns('drmAdditionalHeaderSet'))):
|
||||
for e in (doc.findall(_add_ns('drmAdditionalHeader'))
|
||||
+ doc.findall(_add_ns('drmAdditionalHeaderSet'))):
|
||||
# If id attribute is missing it's valid for all media nodes
|
||||
# without drmAdditionalHeaderId or drmAdditionalHeaderSetId attribute
|
||||
if 'id' not in e.attrib:
|
||||
|
@@ -219,8 +219,8 @@ class FragmentFD(FileDownloader):
|
||||
frag_total_bytes = s.get('total_bytes') or 0
|
||||
if not ctx['live']:
|
||||
estimated_size = (
|
||||
(ctx['complete_frags_downloaded_bytes'] + frag_total_bytes) /
|
||||
(state['fragment_index'] + 1) * total_frags)
|
||||
(ctx['complete_frags_downloaded_bytes'] + frag_total_bytes)
|
||||
/ (state['fragment_index'] + 1) * total_frags)
|
||||
state['total_bytes_estimate'] = estimated_size
|
||||
|
||||
if s['status'] == 'finished':
|
||||
|
@@ -76,12 +76,12 @@ class HlsFD(FragmentFD):
|
||||
return fd.real_download(filename, info_dict)
|
||||
|
||||
def is_ad_fragment_start(s):
|
||||
return (s.startswith('#ANVATO-SEGMENT-INFO') and 'type=ad' in s or
|
||||
s.startswith('#UPLYNK-SEGMENT') and s.endswith(',ad'))
|
||||
return (s.startswith('#ANVATO-SEGMENT-INFO') and 'type=ad' in s
|
||||
or s.startswith('#UPLYNK-SEGMENT') and s.endswith(',ad'))
|
||||
|
||||
def is_ad_fragment_end(s):
|
||||
return (s.startswith('#ANVATO-SEGMENT-INFO') and 'type=master' in s or
|
||||
s.startswith('#UPLYNK-SEGMENT') and s.endswith(',segment'))
|
||||
return (s.startswith('#ANVATO-SEGMENT-INFO') and 'type=master' in s
|
||||
or s.startswith('#UPLYNK-SEGMENT') and s.endswith(',segment'))
|
||||
|
||||
media_frags = 0
|
||||
ad_frags = 0
|
||||
|
@@ -46,8 +46,8 @@ class HttpFD(FileDownloader):
|
||||
|
||||
is_test = self.params.get('test', False)
|
||||
chunk_size = self._TEST_FILE_SIZE if is_test else (
|
||||
info_dict.get('downloader_options', {}).get('http_chunk_size') or
|
||||
self.params.get('http_chunk_size') or 0)
|
||||
info_dict.get('downloader_options', {}).get('http_chunk_size')
|
||||
or self.params.get('http_chunk_size') or 0)
|
||||
|
||||
ctx.open_mode = 'wb'
|
||||
ctx.resume_len = 0
|
||||
@@ -123,11 +123,11 @@ class HttpFD(FileDownloader):
|
||||
content_len = int_or_none(content_range_m.group(3))
|
||||
accept_content_len = (
|
||||
# Non-chunked download
|
||||
not ctx.chunk_size or
|
||||
not ctx.chunk_size
|
||||
# Chunked download and requested piece or
|
||||
# its part is promised to be served
|
||||
content_range_end == range_end or
|
||||
content_len < range_end)
|
||||
or content_range_end == range_end
|
||||
or content_len < range_end)
|
||||
if accept_content_len:
|
||||
ctx.data_len = content_len
|
||||
return
|
||||
@@ -152,8 +152,8 @@ class HttpFD(FileDownloader):
|
||||
raise
|
||||
else:
|
||||
# Examine the reported length
|
||||
if (content_length is not None and
|
||||
(ctx.resume_len - 100 < int(content_length) < ctx.resume_len + 100)):
|
||||
if (content_length is not None
|
||||
and (ctx.resume_len - 100 < int(content_length) < ctx.resume_len + 100)):
|
||||
# The file had already been fully downloaded.
|
||||
# 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,
|
||||
|
@@ -59,9 +59,9 @@ class AddAnimeIE(InfoExtractor):
|
||||
parsed_url = compat_urllib_parse_urlparse(url)
|
||||
av_val = av_res + len(parsed_url.netloc)
|
||||
confirm_url = (
|
||||
parsed_url.scheme + '://' + parsed_url.netloc +
|
||||
action + '?' +
|
||||
compat_urllib_parse_urlencode({
|
||||
parsed_url.scheme + '://' + parsed_url.netloc
|
||||
+ action + '?'
|
||||
+ compat_urllib_parse_urlencode({
|
||||
'jschl_vc': vc, 'jschl_answer': compat_str(av_val)}))
|
||||
self._download_webpage(
|
||||
confirm_url, video_id,
|
||||
|
@@ -65,14 +65,15 @@ class ADNIE(InfoExtractor):
|
||||
if subtitle_location:
|
||||
enc_subtitles = self._download_webpage(
|
||||
urljoin(self._BASE_URL, subtitle_location),
|
||||
video_id, 'Downloading subtitles data', fatal=False)
|
||||
video_id, 'Downloading subtitles data', fatal=False,
|
||||
headers={'Origin': 'https://animedigitalnetwork.fr'})
|
||||
if not enc_subtitles:
|
||||
return None
|
||||
|
||||
# http://animedigitalnetwork.fr/components/com_vodvideo/videojs/adn-vjs.min.js
|
||||
dec_subtitles = intlist_to_bytes(aes_cbc_decrypt(
|
||||
bytes_to_intlist(compat_b64decode(enc_subtitles[24:])),
|
||||
bytes_to_intlist(binascii.unhexlify(self._K + '4421de0a5f0814ba')),
|
||||
bytes_to_intlist(binascii.unhexlify(self._K + '4b8ef13ec1872730')),
|
||||
bytes_to_intlist(compat_b64decode(enc_subtitles[:24]))
|
||||
))
|
||||
subtitles_json = self._parse_json(
|
||||
|
@@ -32,8 +32,8 @@ class BlinkxIE(InfoExtractor):
|
||||
video_id = self._match_id(url)
|
||||
display_id = video_id[:8]
|
||||
|
||||
api_url = ('https://apib4.blinkx.com/api.php?action=play_video&' +
|
||||
'video=%s' % video_id)
|
||||
api_url = ('https://apib4.blinkx.com/api.php?action=play_video&'
|
||||
+ 'video=%s' % video_id)
|
||||
data_json = self._download_webpage(api_url, display_id)
|
||||
data = json.loads(data_json)['api']['results'][0]
|
||||
duration = None
|
||||
|
@@ -1,6 +1,8 @@
|
||||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
|
||||
from .adobepass import AdobePassIE
|
||||
from ..utils import (
|
||||
smuggle_url,
|
||||
@@ -12,16 +14,16 @@ from ..utils import (
|
||||
class BravoTVIE(AdobePassIE):
|
||||
_VALID_URL = r'https?://(?:www\.)?bravotv\.com/(?:[^/]+/)+(?P<id>[^/?#]+)'
|
||||
_TESTS = [{
|
||||
'url': 'http://www.bravotv.com/last-chance-kitchen/season-5/videos/lck-ep-12-fishy-finale',
|
||||
'md5': '9086d0b7ef0ea2aabc4781d75f4e5863',
|
||||
'url': 'https://www.bravotv.com/top-chef/season-16/episode-15/videos/the-top-chef-season-16-winner-is',
|
||||
'md5': 'e34684cfea2a96cd2ee1ef3a60909de9',
|
||||
'info_dict': {
|
||||
'id': 'zHyk1_HU_mPy',
|
||||
'id': 'epL0pmK1kQlT',
|
||||
'ext': 'mp4',
|
||||
'title': 'LCK Ep 12: Fishy Finale',
|
||||
'description': 'S13/E12: Two eliminated chefs have just 12 minutes to cook up a delicious fish dish.',
|
||||
'title': 'The Top Chef Season 16 Winner Is...',
|
||||
'description': 'Find out who takes the title of Top Chef!',
|
||||
'uploader': 'NBCU-BRAV',
|
||||
'upload_date': '20160302',
|
||||
'timestamp': 1456945320,
|
||||
'upload_date': '20190314',
|
||||
'timestamp': 1552591860,
|
||||
}
|
||||
}, {
|
||||
'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)
|
||||
webpage = self._download_webpage(url, display_id)
|
||||
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)
|
||||
info = {}
|
||||
query = {
|
||||
'mbr': 'true',
|
||||
}
|
||||
account_pid, release_pid = [None] * 2
|
||||
tve = settings.get('sharedTVE')
|
||||
tve = settings.get('ls_tve')
|
||||
if tve:
|
||||
query['manifest'] = 'm3u'
|
||||
account_pid = 'HNK2IC'
|
||||
release_pid = tve['release_pid']
|
||||
mobj = re.search(r'<[^>]+id="pdk-player"[^>]+data-url=["\']?(?:https?:)?//player\.theplatform\.com/p/([^/]+)/(?:[^/]+/)*select/([^?#&"\']+)', webpage)
|
||||
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':
|
||||
adobe_pass = settings.get('adobePass', {})
|
||||
adobe_pass = settings.get('tve_adobe_auth', {})
|
||||
resource = self._get_mvpd_resource(
|
||||
adobe_pass.get('adobePassResourceId', 'bravo'),
|
||||
tve['title'], release_pid, tve.get('rating'))
|
||||
query['auth'] = self._extract_mvpd_auth(
|
||||
url, release_pid, adobe_pass.get('adobePassRequestorId', 'bravo'), resource)
|
||||
else:
|
||||
shared_playlist = settings['shared_playlist']
|
||||
shared_playlist = settings['ls_playlist']
|
||||
account_pid = shared_playlist['account_pid']
|
||||
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({
|
||||
'title': metadata['title'],
|
||||
'description': metadata.get('description'),
|
||||
@@ -67,7 +77,7 @@ class BravoTVIE(AdobePassIE):
|
||||
'_type': 'url_transparent',
|
||||
'id': release_pid,
|
||||
'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}),
|
||||
'ie_key': 'ThePlatform',
|
||||
})
|
||||
|
@@ -3,11 +3,13 @@ from __future__ import unicode_literals
|
||||
import re
|
||||
|
||||
from .common import InfoExtractor
|
||||
from ..utils import parse_duration
|
||||
|
||||
|
||||
class BYUtvIE(InfoExtractor):
|
||||
_VALID_URL = r'https?://(?:www\.)?byutv\.org/(?:watch|player)/(?!event/)(?P<id>[0-9a-f-]+)(?:/(?P<display_id>[^/?#&]+))?'
|
||||
_TESTS = [{
|
||||
# ooyalaVOD
|
||||
'url': 'http://www.byutv.org/watch/6587b9a3-89d2-42a6-a7f7-fd2f81840a7d/studio-c-season-5-episode-5',
|
||||
'info_dict': {
|
||||
'id': 'ZvanRocTpW-G5_yZFeltTAMv6jxOU9KH',
|
||||
@@ -22,6 +24,20 @@ class BYUtvIE(InfoExtractor):
|
||||
'skip_download': True,
|
||||
},
|
||||
'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',
|
||||
'only_matching': True,
|
||||
@@ -35,24 +51,42 @@ class BYUtvIE(InfoExtractor):
|
||||
video_id = mobj.group('id')
|
||||
display_id = mobj.group('display_id') or video_id
|
||||
|
||||
ep = self._download_json(
|
||||
'https://api.byutv.org/api3/catalog/getvideosforcontent', video_id,
|
||||
query={
|
||||
info = self._download_json(
|
||||
'https://api.byutv.org/api3/catalog/getvideosforcontent',
|
||||
display_id, query={
|
||||
'contentid': video_id,
|
||||
'channel': 'byutv',
|
||||
'x-byutv-context': 'web$US',
|
||||
}, headers={
|
||||
'x-byutv-context': 'web$US',
|
||||
'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 {
|
||||
'_type': 'url_transparent',
|
||||
'ie_key': 'Ooyala',
|
||||
'url': 'ooyala:%s' % ep['providerId'],
|
||||
'id': video_id,
|
||||
'display_id': display_id,
|
||||
'title': ep.get('title'),
|
||||
'title': title,
|
||||
'description': ep.get('description'),
|
||||
'thumbnail': ep.get('imageThumbnail'),
|
||||
'duration': parse_duration(ep.get('length')),
|
||||
'formats': formats,
|
||||
}
|
||||
|
@@ -1,9 +1,12 @@
|
||||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .common import InfoExtractor
|
||||
from ..utils import (
|
||||
int_or_none,
|
||||
parse_iso8601,
|
||||
try_get,
|
||||
url_or_none,
|
||||
)
|
||||
|
||||
|
||||
@@ -18,11 +21,13 @@ class CCCIE(InfoExtractor):
|
||||
'id': '1839',
|
||||
'ext': 'mp4',
|
||||
'title': 'Introduction to Processor Design',
|
||||
'creator': 'byterazor',
|
||||
'description': 'md5:df55f6d073d4ceae55aae6f2fd98a0ac',
|
||||
'thumbnail': r're:^https?://.*\.jpg$',
|
||||
'upload_date': '20131228',
|
||||
'timestamp': 1388188800,
|
||||
'duration': 3710,
|
||||
'tags': list,
|
||||
}
|
||||
}, {
|
||||
'url': 'https://media.ccc.de/v/32c3-7368-shopshifting#download',
|
||||
@@ -68,6 +73,7 @@ class CCCIE(InfoExtractor):
|
||||
'id': event_id,
|
||||
'display_id': display_id,
|
||||
'title': event_data['title'],
|
||||
'creator': try_get(event_data, lambda x: ', '.join(x['persons'])),
|
||||
'description': event_data.get('description'),
|
||||
'thumbnail': event_data.get('thumb_url'),
|
||||
'timestamp': parse_iso8601(event_data.get('date')),
|
||||
@@ -75,3 +81,31 @@ class CCCIE(InfoExtractor):
|
||||
'tags': event_data.get('tags'),
|
||||
'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)
|
||||
https?://
|
||||
(?:
|
||||
(?:watch\.)?cloudflarestream\.com/|
|
||||
embed\.cloudflarestream\.com/embed/[^/]+\.js\?.*?\bvideo=
|
||||
(?:watch\.)?(?:cloudflarestream\.com|videodelivery\.net)/|
|
||||
embed\.(?:cloudflarestream\.com|videodelivery\.net)/embed/[^/]+\.js\?.*?\bvideo=
|
||||
)
|
||||
(?P<id>[\da-f]+)
|
||||
'''
|
||||
@@ -31,6 +31,9 @@ class CloudflareStreamIE(InfoExtractor):
|
||||
}, {
|
||||
'url': 'https://cloudflarestream.com/31c9291ab41fac05471db4e73aa11717/manifest/video.mpd',
|
||||
'only_matching': True,
|
||||
}, {
|
||||
'url': 'https://embed.videodelivery.net/embed/r4xu.fla9.latest.js?video=81d80727f3022488598f68d323c1ad5e',
|
||||
'only_matching': True,
|
||||
}]
|
||||
|
||||
@staticmethod
|
||||
@@ -38,7 +41,7 @@ class CloudflareStreamIE(InfoExtractor):
|
||||
return [
|
||||
mobj.group('url')
|
||||
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)]
|
||||
|
||||
def _real_extract(self, url):
|
||||
|
@@ -117,7 +117,7 @@ class InfoExtractor(object):
|
||||
unfragmented media)
|
||||
- URL of the MPD manifest or base URL
|
||||
representing the media if MPD manifest
|
||||
is parsed froma string (in case of
|
||||
is parsed from a string (in case of
|
||||
fragmented media)
|
||||
for MSS - URL of the ISM manifest.
|
||||
* manifest_url
|
||||
@@ -542,11 +542,11 @@ class InfoExtractor(object):
|
||||
raise ExtractorError('An extractor error has occurred.', cause=e)
|
||||
|
||||
def __maybe_fake_ip_and_retry(self, countries):
|
||||
if (not self._downloader.params.get('geo_bypass_country', None) and
|
||||
self._GEO_BYPASS and
|
||||
self._downloader.params.get('geo_bypass', True) and
|
||||
not self._x_forwarded_for_ip and
|
||||
countries):
|
||||
if (not self._downloader.params.get('geo_bypass_country', None)
|
||||
and self._GEO_BYPASS
|
||||
and self._downloader.params.get('geo_bypass', True)
|
||||
and not self._x_forwarded_for_ip
|
||||
and countries):
|
||||
country_code = random.choice(countries)
|
||||
self._x_forwarded_for_ip = GeoUtils.random_ipv4(country_code)
|
||||
if self._x_forwarded_for_ip:
|
||||
@@ -682,8 +682,8 @@ class InfoExtractor(object):
|
||||
|
||||
def __check_blocked(self, content):
|
||||
first_block = content[:512]
|
||||
if ('<title>Access to this site is blocked</title>' in content and
|
||||
'Websense' in first_block):
|
||||
if ('<title>Access to this site is blocked</title>' in content
|
||||
and 'Websense' in first_block):
|
||||
msg = 'Access to this webpage has been blocked by Websense filtering software in your network.'
|
||||
blocked_iframe = self._html_search_regex(
|
||||
r'<iframe src="([^"]+)"', content,
|
||||
@@ -701,8 +701,8 @@ class InfoExtractor(object):
|
||||
if block_msg:
|
||||
msg += ' (Message: "%s")' % block_msg.replace('\n', ' ')
|
||||
raise ExtractorError(msg, expected=True)
|
||||
if ('<title>TTK :: Доступ к ресурсу ограничен</title>' in content and
|
||||
'blocklist.rkn.gov.ru' in content):
|
||||
if ('<title>TTK :: Доступ к ресурсу ограничен</title>' in content
|
||||
and 'blocklist.rkn.gov.ru' in content):
|
||||
raise ExtractorError(
|
||||
'Access to this webpage has been blocked by decision of the Russian government. '
|
||||
'Visit http://blocklist.rkn.gov.ru/ for a block reason.',
|
||||
@@ -1709,8 +1709,8 @@ class InfoExtractor(object):
|
||||
continue
|
||||
else:
|
||||
tbr = float_or_none(
|
||||
last_stream_inf.get('AVERAGE-BANDWIDTH') or
|
||||
last_stream_inf.get('BANDWIDTH'), scale=1000)
|
||||
last_stream_inf.get('AVERAGE-BANDWIDTH')
|
||||
or last_stream_inf.get('BANDWIDTH'), scale=1000)
|
||||
format_id = []
|
||||
if m3u8_id:
|
||||
format_id.append(m3u8_id)
|
||||
@@ -2019,6 +2019,8 @@ class InfoExtractor(object):
|
||||
if res is False:
|
||||
return []
|
||||
mpd_doc, urlh = res
|
||||
if mpd_doc is None:
|
||||
return []
|
||||
mpd_base_url = base_url(urlh.geturl())
|
||||
|
||||
return self._parse_mpd_formats(
|
||||
@@ -2502,8 +2504,8 @@ class InfoExtractor(object):
|
||||
if str_or_none(s_attr.get(lbl))
|
||||
]
|
||||
width = int_or_none(s_attr.get('width'))
|
||||
height = (int_or_none(s_attr.get('height')) or
|
||||
int_or_none(s_attr.get('res')))
|
||||
height = (int_or_none(s_attr.get('height'))
|
||||
or int_or_none(s_attr.get('res')))
|
||||
if not width or not height:
|
||||
for lbl in labels:
|
||||
resolution = parse_resolution(lbl)
|
||||
@@ -2845,8 +2847,8 @@ class InfoExtractor(object):
|
||||
return not any_restricted
|
||||
|
||||
def extract_subtitles(self, *args, **kwargs):
|
||||
if (self._downloader.params.get('writesubtitles', False) or
|
||||
self._downloader.params.get('listsubtitles')):
|
||||
if (self._downloader.params.get('writesubtitles', False)
|
||||
or self._downloader.params.get('listsubtitles')):
|
||||
return self._get_subtitles(*args, **kwargs)
|
||||
return {}
|
||||
|
||||
@@ -2871,8 +2873,8 @@ class InfoExtractor(object):
|
||||
return ret
|
||||
|
||||
def extract_automatic_captions(self, *args, **kwargs):
|
||||
if (self._downloader.params.get('writeautomaticsub', False) or
|
||||
self._downloader.params.get('listsubtitles')):
|
||||
if (self._downloader.params.get('writeautomaticsub', False)
|
||||
or self._downloader.params.get('listsubtitles')):
|
||||
return self._get_automatic_captions(*args, **kwargs)
|
||||
return {}
|
||||
|
||||
@@ -2880,9 +2882,9 @@ class InfoExtractor(object):
|
||||
raise NotImplementedError('This method must be implemented by subclasses')
|
||||
|
||||
def mark_watched(self, *args, **kwargs):
|
||||
if (self._downloader.params.get('mark_watched', False) and
|
||||
(self._get_login_info()[0] is not None or
|
||||
self._downloader.params.get('cookiefile') is not None)):
|
||||
if (self._downloader.params.get('mark_watched', False)
|
||||
and (self._get_login_info()[0] is not None
|
||||
or self._downloader.params.get('cookiefile') is not None)):
|
||||
self._mark_watched(*args, **kwargs)
|
||||
|
||||
def _mark_watched(self, *args, **kwargs):
|
||||
|
@@ -32,19 +32,19 @@ class CommonMistakesIE(InfoExtractor):
|
||||
|
||||
|
||||
class UnicodeBOMIE(InfoExtractor):
|
||||
IE_DESC = False
|
||||
_VALID_URL = r'(?P<bom>\ufeff)(?P<id>.*)$'
|
||||
IE_DESC = False
|
||||
_VALID_URL = r'(?P<bom>\ufeff)(?P<id>.*)$'
|
||||
|
||||
# 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)
|
||||
_TESTS = [] if (3, 0) < sys.version_info <= (3, 3) else [{
|
||||
'url': '\ufeffhttp://www.youtube.com/watch?v=BaW_jenozKc',
|
||||
'only_matching': True,
|
||||
}]
|
||||
# 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)
|
||||
_TESTS = [] if (3, 0) < sys.version_info <= (3, 3) else [{
|
||||
'url': '\ufeffhttp://www.youtube.com/watch?v=BaW_jenozKc',
|
||||
'only_matching': True,
|
||||
}]
|
||||
|
||||
def _real_extract(self, url):
|
||||
real_url = self._match_id(url)
|
||||
self.report_warning(
|
||||
'Your URL starts with a Byte Order Mark (BOM). '
|
||||
'Removing the BOM and looking for "%s" ...' % real_url)
|
||||
return self.url_result(real_url)
|
||||
def _real_extract(self, url):
|
||||
real_url = self._match_id(url)
|
||||
self.report_warning(
|
||||
'Your URL starts with a Byte Order Mark (BOM). '
|
||||
'Removing the BOM and looking for "%s" ...' % real_url)
|
||||
return self.url_result(real_url)
|
||||
|
@@ -45,8 +45,8 @@ class DailyMailIE(InfoExtractor):
|
||||
sources_url = (try_get(
|
||||
video_data,
|
||||
(lambda x: x['plugins']['sources']['url'],
|
||||
lambda x: x['sources']['url']), compat_str) or
|
||||
'http://www.dailymail.co.uk/api/player/%s/video-sources.json' % video_id)
|
||||
lambda x: x['sources']['url']), compat_str)
|
||||
or 'http://www.dailymail.co.uk/api/player/%s/video-sources.json' % video_id)
|
||||
|
||||
video_sources = self._download_json(sources_url, video_id)
|
||||
body = video_sources.get('body')
|
||||
|
@@ -70,8 +70,8 @@ class DctpTvIE(InfoExtractor):
|
||||
endpoint = next(
|
||||
server['endpoint']
|
||||
for server in servers
|
||||
if url_or_none(server.get('endpoint')) and
|
||||
'cloudfront' in server['endpoint'])
|
||||
if url_or_none(server.get('endpoint'))
|
||||
and 'cloudfront' in server['endpoint'])
|
||||
else:
|
||||
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)
|
@@ -82,8 +82,8 @@ class ExpressenIE(InfoExtractor):
|
||||
title = info.get('titleRaw') or data['title']
|
||||
description = info.get('descriptionRaw')
|
||||
thumbnail = info.get('socialMediaImage') or data.get('image')
|
||||
duration = int_or_none(info.get('videoTotalSecondsDuration') or
|
||||
data.get('totalSecondsDuration'))
|
||||
duration = int_or_none(info.get('videoTotalSecondsDuration')
|
||||
or data.get('totalSecondsDuration'))
|
||||
timestamp = unified_timestamp(info.get('publishDate'))
|
||||
|
||||
return {
|
||||
|
@@ -177,7 +177,10 @@ from .cbsnews import (
|
||||
CBSNewsLiveVideoIE,
|
||||
)
|
||||
from .cbssports import CBSSportsIE
|
||||
from .ccc import CCCIE
|
||||
from .ccc import (
|
||||
CCCIE,
|
||||
CCCPlaylistIE,
|
||||
)
|
||||
from .ccma import CCMAIE
|
||||
from .cctv import CCTVIE
|
||||
from .cda import CDAIE
|
||||
@@ -194,6 +197,7 @@ from .chirbit import (
|
||||
ChirbitProfileIE,
|
||||
)
|
||||
from .cinchcast import CinchcastIE
|
||||
from .cinemax import CinemaxIE
|
||||
from .ciscolive import (
|
||||
CiscoLiveSessionIE,
|
||||
CiscoLiveSearchIE,
|
||||
@@ -283,10 +287,6 @@ from .dplay import (
|
||||
DPlayIE,
|
||||
DPlayItIE,
|
||||
)
|
||||
from .dramafever import (
|
||||
DramaFeverIE,
|
||||
DramaFeverSeriesIE,
|
||||
)
|
||||
from .dreisat import DreiSatIE
|
||||
from .drbonanza import DRBonanzaIE
|
||||
from .drtuber import DrTuberIE
|
||||
@@ -808,6 +808,8 @@ from .nrk import (
|
||||
NRKTVSeasonIE,
|
||||
NRKTVSeriesIE,
|
||||
)
|
||||
from .nrl import NRLTVIE
|
||||
from .ntvcojp import NTVCoJpCUIE
|
||||
from .ntvde import NTVDeIE
|
||||
from .ntvru import NTVRuIE
|
||||
from .nytimes import (
|
||||
@@ -831,7 +833,10 @@ from .ooyala import (
|
||||
OoyalaIE,
|
||||
OoyalaExternalIE,
|
||||
)
|
||||
from .openload import OpenloadIE
|
||||
from .openload import (
|
||||
OpenloadIE,
|
||||
VerystreamIE,
|
||||
)
|
||||
from .ora import OraTVIE
|
||||
from .orf import (
|
||||
ORFTVthekIE,
|
||||
@@ -1031,7 +1036,10 @@ from .skynewsarabia import (
|
||||
SkyNewsArabiaIE,
|
||||
SkyNewsArabiaArticleIE,
|
||||
)
|
||||
from .skysports import SkySportsIE
|
||||
from .sky import (
|
||||
SkyNewsIE,
|
||||
SkySportsIE,
|
||||
)
|
||||
from .slideshare import SlideshareIE
|
||||
from .slideslive import SlidesLiveIE
|
||||
from .slutload import SlutloadIE
|
||||
@@ -1095,6 +1103,10 @@ from .streetvoice import StreetVoiceIE
|
||||
from .stretchinternet import StretchInternetIE
|
||||
from .stv import STVPlayerIE
|
||||
from .sunporno import SunPornoIE
|
||||
from .sverigesradio import (
|
||||
SverigesRadioEpisodeIE,
|
||||
SverigesRadioPublicationIE,
|
||||
)
|
||||
from .svt import (
|
||||
SVTIE,
|
||||
SVTPageIE,
|
||||
@@ -1416,10 +1428,6 @@ from .weiqitv import WeiqiTVIE
|
||||
from .wimp import WimpIE
|
||||
from .wistia import WistiaIE
|
||||
from .worldstarhiphop import WorldStarHipHopIE
|
||||
from .wrzuta import (
|
||||
WrzutaIE,
|
||||
WrzutaPlaylistIE,
|
||||
)
|
||||
from .wsj import (
|
||||
WSJIE,
|
||||
WSJArticleIE,
|
||||
|
@@ -22,8 +22,6 @@ from ..utils import (
|
||||
|
||||
|
||||
class FourTubeBaseIE(InfoExtractor):
|
||||
_TKN_HOST = 'tkn.kodicdn.com'
|
||||
|
||||
def _extract_formats(self, url, video_id, media_id, sources):
|
||||
token_url = 'https://%s/%s/desktop/%s' % (
|
||||
self._TKN_HOST, media_id, '+'.join(sources))
|
||||
@@ -120,6 +118,7 @@ class FourTubeIE(FourTubeBaseIE):
|
||||
IE_NAME = '4tube'
|
||||
_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'
|
||||
_TKN_HOST = 'token.4tube.com'
|
||||
_TESTS = [{
|
||||
'url': 'http://www.4tube.com/videos/209733/hot-babe-holly-michaels-gets-her-ass-stuffed-by-black',
|
||||
'md5': '6516c8ac63b03de06bc8eac14362db4f',
|
||||
@@ -149,6 +148,7 @@ class FourTubeIE(FourTubeBaseIE):
|
||||
class FuxIE(FourTubeBaseIE):
|
||||
_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'
|
||||
_TKN_HOST = 'token.fux.com'
|
||||
_TESTS = [{
|
||||
'url': 'https://www.fux.com/video/195359/awesome-fucking-kitchen-ends-cum-swallow',
|
||||
'info_dict': {
|
||||
@@ -280,6 +280,7 @@ class PornTubeIE(FourTubeBaseIE):
|
||||
class PornerBrosIE(FourTubeBaseIE):
|
||||
_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'
|
||||
_TKN_HOST = 'token.pornerbros.com'
|
||||
_TESTS = [{
|
||||
'url': 'https://www.pornerbros.com/videos/skinny-brunette-takes-big-cock-down-her-anal-hole_181369',
|
||||
'md5': '6516c8ac63b03de06bc8eac14362db4f',
|
||||
|
@@ -66,7 +66,7 @@ class FOXIE(AdobePassIE):
|
||||
'https://api2.fox.com/v2.0/' + path,
|
||||
video_id, data=data, headers=headers)
|
||||
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(
|
||||
e.cause.read().decode(), video_id)['entitlementIssues']
|
||||
for e in entitlement_issues:
|
||||
@@ -100,7 +100,7 @@ class FOXIE(AdobePassIE):
|
||||
try:
|
||||
m3u8_url = self._download_json(release_url, video_id)['playURL']
|
||||
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)
|
||||
if error.get('exception') == 'GeoLocationBlocked':
|
||||
self.raise_geo_restricted(countries=['US'])
|
||||
|
@@ -371,12 +371,13 @@ class FranceTVInfoIE(FranceTVBaseInfoExtractor):
|
||||
self.url_result(dailymotion_url, DailymotionIE.ie_key())
|
||||
for dailymotion_url in dailymotion_urls])
|
||||
|
||||
video_id, catalogue = self._search_regex(
|
||||
(r'id-video=([^@]+@[^"]+)',
|
||||
video_id = self._search_regex(
|
||||
(r'player\.load[^;]+src:\s*["\']([^"\']+)',
|
||||
r'id-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):
|
||||
|
@@ -94,8 +94,8 @@ class FrontendMastersPageBaseIE(FrontendMastersBaseIE):
|
||||
chapter_number = None
|
||||
index = lesson.get('index')
|
||||
element_index = lesson.get('elementIndex')
|
||||
if (isinstance(index, int) and isinstance(element_index, int) and
|
||||
index < element_index):
|
||||
if (isinstance(index, int) and isinstance(element_index, int)
|
||||
and index < element_index):
|
||||
chapter_number = element_index - index
|
||||
chapter = (chapters[chapter_number - 1]
|
||||
if chapter_number - 1 < len(chapters) else None)
|
||||
|
@@ -89,7 +89,10 @@ from .piksel import PikselIE
|
||||
from .videa import VideaIE
|
||||
from .twentymin import TwentyMinutenIE
|
||||
from .ustream import UstreamIE
|
||||
from .openload import OpenloadIE
|
||||
from .openload import (
|
||||
OpenloadIE,
|
||||
VerystreamIE,
|
||||
)
|
||||
from .videopress import VideoPressIE
|
||||
from .rutube import RutubeIE
|
||||
from .limelight import LimelightBaseIE
|
||||
@@ -2546,11 +2549,11 @@ class GenericIE(InfoExtractor):
|
||||
return self.url_result(mobj.group('url'))
|
||||
|
||||
# Look for Ooyala videos
|
||||
mobj = (re.search(r'player\.ooyala\.com/[^"?]+[?#][^"]*?(?:embedCode|ec)=(?P<ec>[^"&]+)', webpage) or
|
||||
re.search(r'OO\.Player\.create\([\'"].*?[\'"],\s*[\'"](?P<ec>.{32})[\'"]', webpage) or
|
||||
re.search(r'OO\.Player\.create\.apply\(\s*OO\.Player\s*,\s*op\(\s*\[\s*[\'"][^\'"]*[\'"]\s*,\s*[\'"](?P<ec>.{32})[\'"]', webpage) or
|
||||
re.search(r'SBN\.VideoLinkset\.ooyala\([\'"](?P<ec>.{32})[\'"]\)', webpage) or
|
||||
re.search(r'data-ooyala-video-id\s*=\s*[\'"](?P<ec>.{32})[\'"]', webpage))
|
||||
mobj = (re.search(r'player\.ooyala\.com/[^"?]+[?#][^"]*?(?:embedCode|ec)=(?P<ec>[^"&]+)', webpage)
|
||||
or re.search(r'OO\.Player\.create\([\'"].*?[\'"],\s*[\'"](?P<ec>.{32})[\'"]', webpage)
|
||||
or re.search(r'OO\.Player\.create\.apply\(\s*OO\.Player\s*,\s*op\(\s*\[\s*[\'"][^\'"]*[\'"]\s*,\s*[\'"](?P<ec>.{32})[\'"]', webpage)
|
||||
or re.search(r'SBN\.VideoLinkset\.ooyala\([\'"](?P<ec>.{32})[\'"]\)', webpage)
|
||||
or re.search(r'data-ooyala-video-id\s*=\s*[\'"](?P<ec>.{32})[\'"]', webpage))
|
||||
if mobj is not None:
|
||||
embed_token = self._search_regex(
|
||||
r'embedToken[\'"]?\s*:\s*[\'"]([^\'"]+)',
|
||||
@@ -3017,6 +3020,12 @@ class GenericIE(InfoExtractor):
|
||||
return self.playlist_from_matches(
|
||||
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
|
||||
videopress_urls = VideoPressIE._extract_urls(webpage)
|
||||
if videopress_urls:
|
||||
@@ -3212,8 +3221,8 @@ class GenericIE(InfoExtractor):
|
||||
else:
|
||||
formats.append({
|
||||
'url': src,
|
||||
'ext': (mimetype2ext(src_type) or
|
||||
ext if ext in KNOWN_EXTENSIONS else 'mp4'),
|
||||
'ext': (mimetype2ext(src_type)
|
||||
or ext if ext in KNOWN_EXTENSIONS else 'mp4'),
|
||||
})
|
||||
if formats:
|
||||
self._sort_formats(formats)
|
||||
|
@@ -11,7 +11,7 @@ from ..utils import (
|
||||
|
||||
|
||||
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 = [{
|
||||
'url': 'http://gfycat.com/DeadlyDecisiveGermanpinscher',
|
||||
'info_dict': {
|
||||
@@ -47,6 +47,9 @@ class GfycatIE(InfoExtractor):
|
||||
}, {
|
||||
'url': 'https://gfycat.com/gifs/detail/UnconsciousLankyIvorygull',
|
||||
'only_matching': True
|
||||
}, {
|
||||
'url': 'https://gfycat.com/acceptablehappygoluckyharborporpoise-baseball',
|
||||
'only_matching': True
|
||||
}]
|
||||
|
||||
def _real_extract(self, url):
|
||||
|
@@ -13,19 +13,7 @@ from ..utils import (
|
||||
)
|
||||
|
||||
|
||||
class HBOIE(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'],
|
||||
}
|
||||
class HBOBaseIE(InfoExtractor):
|
||||
_FORMATS_INFO = {
|
||||
'pro7': {
|
||||
'width': 1280,
|
||||
@@ -65,12 +53,8 @@ class HBOIE(InfoExtractor):
|
||||
},
|
||||
}
|
||||
|
||||
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']
|
||||
video_data = self._download_xml(urljoin(url, location_path), display_id)
|
||||
def _extract_info(self, url, display_id):
|
||||
video_data = self._download_xml(url, display_id)
|
||||
video_id = xpath_text(video_data, 'id', fatal=True)
|
||||
episode_title = title = xpath_text(video_data, 'title', fatal=True)
|
||||
series = xpath_text(video_data, 'program')
|
||||
@@ -167,3 +151,25 @@ class HBOIE(InfoExtractor):
|
||||
'thumbnails': thumbnails,
|
||||
'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,
|
||||
'title': title,
|
||||
'description': description,
|
||||
'thumbnail': (xpath_text(doc, './/{http://rss.jwpcdn.com/}image') or
|
||||
self._og_search_thumbnail(webpage)),
|
||||
'thumbnail': (xpath_text(doc, './/{http://rss.jwpcdn.com/}image')
|
||||
or self._og_search_thumbnail(webpage)),
|
||||
'timestamp': parse_iso8601(
|
||||
self._html_search_meta('date', webpage)),
|
||||
'formats': formats,
|
||||
|
@@ -58,8 +58,8 @@ class HitboxIE(InfoExtractor):
|
||||
title = video_meta.get('media_status')
|
||||
alt_title = video_meta.get('media_title')
|
||||
description = clean_html(
|
||||
video_meta.get('media_description') or
|
||||
video_meta.get('media_description_md'))
|
||||
video_meta.get('media_description')
|
||||
or video_meta.get('media_description_md'))
|
||||
duration = float_or_none(video_meta.get('media_duration'))
|
||||
uploader = video_meta.get('media_user_name')
|
||||
views = int_or_none(video_meta.get('media_views'))
|
||||
|
@@ -47,8 +47,8 @@ class HitRecordIE(InfoExtractor):
|
||||
tags = [
|
||||
t['text']
|
||||
for t in tags_list
|
||||
if isinstance(t, dict) and t.get('text') and
|
||||
isinstance(t['text'], compat_str)]
|
||||
if isinstance(t, dict) and t.get('text')
|
||||
and isinstance(t['text'], compat_str)]
|
||||
|
||||
return {
|
||||
'id': video_id,
|
||||
|
@@ -77,13 +77,13 @@ class HKETVIE(InfoExtractor):
|
||||
|
||||
title = (
|
||||
self._html_search_meta(
|
||||
('ed_title', 'search.ed_title'), webpage, default=None) or
|
||||
self._search_regex(
|
||||
('ed_title', 'search.ed_title'), webpage, default=None)
|
||||
or self._search_regex(
|
||||
r'data-favorite_title_(?:eng|chi)=(["\'])(?P<id>(?:(?!\1).)+)\1',
|
||||
webpage, 'title', default=None, group='url') or
|
||||
self._html_search_regex(
|
||||
r'<h1>([^<]+)</h1>', webpage, 'title', default=None) or
|
||||
self._og_search_title(webpage)
|
||||
webpage, 'title', default=None, group='url')
|
||||
or self._html_search_regex(
|
||||
r'<h1>([^<]+)</h1>', webpage, 'title', default=None)
|
||||
or self._og_search_title(webpage)
|
||||
)
|
||||
|
||||
file_id = self._search_regex(
|
||||
|
@@ -4,40 +4,59 @@ from __future__ import unicode_literals
|
||||
import hashlib
|
||||
import hmac
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from .common import InfoExtractor
|
||||
from ..compat import compat_HTTPError
|
||||
from ..compat import (
|
||||
compat_HTTPError,
|
||||
compat_str,
|
||||
)
|
||||
from ..utils import (
|
||||
determine_ext,
|
||||
ExtractorError,
|
||||
int_or_none,
|
||||
str_or_none,
|
||||
try_get,
|
||||
url_or_none,
|
||||
)
|
||||
|
||||
|
||||
class HotStarBaseIE(InfoExtractor):
|
||||
_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())
|
||||
exp = st + 6000
|
||||
auth = 'st=%d~exp=%d~acl=/*' % (st, exp)
|
||||
auth += '~hmac=' + hmac.new(self._AKAMAI_ENCRYPTION_KEY, auth.encode(), hashlib.sha256).hexdigest()
|
||||
response = self._download_json(
|
||||
'https://api.hotstar.com/' + path,
|
||||
video_id, headers={
|
||||
'https://api.hotstar.com/' + path, video_id, headers={
|
||||
'hotstarauth': auth,
|
||||
'x-country-code': 'IN',
|
||||
'x-platform-code': 'JIO',
|
||||
}, query={
|
||||
query_name: video_id,
|
||||
'tas': 10000,
|
||||
})
|
||||
}, query=query)
|
||||
if response['statusCode'] != 'OK':
|
||||
raise ExtractorError(
|
||||
response['body']['message'], expected=True)
|
||||
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):
|
||||
IE_NAME = 'hotstar'
|
||||
@@ -68,6 +87,10 @@ class HotStarIE(HotStarBaseIE):
|
||||
}, {
|
||||
'url': 'http://www.hotstar.com/1000000515',
|
||||
'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
|
||||
|
||||
@@ -95,26 +118,40 @@ class HotStarIE(HotStarBaseIE):
|
||||
raise ExtractorError('This video is DRM protected.', expected=True)
|
||||
|
||||
formats = []
|
||||
format_data = self._call_api('h/v1/play', video_id)['item']
|
||||
format_url = format_data['playbackUrl']
|
||||
ext = determine_ext(format_url)
|
||||
if ext == 'm3u8':
|
||||
geo_restricted = False
|
||||
playback_sets = self._call_api_v2('h/v2/play', video_id)['playBackSets']
|
||||
for playback_set in playback_sets:
|
||||
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:
|
||||
formats.extend(self._extract_m3u8_formats(
|
||||
format_url, video_id, 'mp4', m3u8_id='hls'))
|
||||
if 'package:hls' in tags or ext == 'm3u8':
|
||||
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:
|
||||
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
|
||||
self.raise_geo_restricted(countries=['IN'])
|
||||
raise
|
||||
elif ext == 'f4m':
|
||||
# produce broken files
|
||||
pass
|
||||
else:
|
||||
formats.append({
|
||||
'url': format_url,
|
||||
'width': int_or_none(format_data.get('width')),
|
||||
'height': int_or_none(format_data.get('height')),
|
||||
})
|
||||
geo_restricted = True
|
||||
continue
|
||||
if not formats and geo_restricted:
|
||||
self.raise_geo_restricted(countries=['IN'])
|
||||
self._sort_formats(formats)
|
||||
|
||||
return {
|
||||
|
@@ -60,8 +60,8 @@ class HRTiBaseIE(InfoExtractor):
|
||||
language=self._APP_LANGUAGE,
|
||||
application_id=self._APP_PUBLICATION_ID)
|
||||
|
||||
self._login_url = (modules['user']['resources']['login']['uri'] +
|
||||
'/format/json').format(session_id=self._session_id)
|
||||
self._login_url = (modules['user']['resources']['login']['uri']
|
||||
+ '/format/json').format(session_id=self._session_id)
|
||||
|
||||
self._logout_url = modules['user']['resources']['logout']['uri']
|
||||
|
||||
|
@@ -122,9 +122,9 @@ class InfoQIE(BokeCCBaseIE):
|
||||
formats = self._extract_bokecc_formats(webpage, video_id)
|
||||
else:
|
||||
formats = (
|
||||
self._extract_rtmp_video(webpage) +
|
||||
self._extract_http_video(webpage) +
|
||||
self._extract_http_audio(webpage, video_id))
|
||||
self._extract_rtmp_video(webpage)
|
||||
+ self._extract_http_video(webpage)
|
||||
+ self._extract_http_audio(webpage, video_id))
|
||||
|
||||
self._sort_formats(formats)
|
||||
|
||||
|
@@ -383,9 +383,9 @@ class IqiyiIE(InfoExtractor):
|
||||
self._sleep(5, video_id)
|
||||
|
||||
self._sort_formats(formats)
|
||||
title = (get_element_by_id('widget-videotitle', webpage) or
|
||||
clean_html(get_element_by_attribute('class', 'mod-play-tit', webpage)) or
|
||||
self._html_search_regex(r'<span[^>]+data-videochanged-title="word"[^>]*>([^<]+)</span>', webpage, 'title'))
|
||||
title = (get_element_by_id('widget-videotitle', webpage)
|
||||
or clean_html(get_element_by_attribute('class', 'mod-play-tit', webpage))
|
||||
or self._html_search_regex(r'<span[^>]+data-videochanged-title="word"[^>]*>([^<]+)</span>', webpage, 'title'))
|
||||
|
||||
return {
|
||||
'id': video_id,
|
||||
|
@@ -77,10 +77,10 @@ class ITVIE(InfoExtractor):
|
||||
return etree.SubElement(element, _add_ns(name))
|
||||
|
||||
production_id = (
|
||||
params.get('data-video-autoplay-id') or
|
||||
'%s#001' % (
|
||||
params.get('data-video-episode-id') or
|
||||
video_id.replace('a', '/')))
|
||||
params.get('data-video-autoplay-id')
|
||||
or '%s#001' % (
|
||||
params.get('data-video-episode-id')
|
||||
or video_id.replace('a', '/')))
|
||||
|
||||
req_env = etree.Element(_add_ns('soapenv:Envelope'))
|
||||
_add_sub_element(req_env, 'soapenv:Header')
|
||||
|
@@ -118,8 +118,8 @@ class KalturaIE(InfoExtractor):
|
||||
(?P<q2>['"])_?(?P<partner_id>(?:(?!(?P=q2)).)+)(?P=q2),.*?
|
||||
(?P<q3>['"])entry_?[Ii]d(?P=q3)\s*:\s*
|
||||
(?P<q4>['"])(?P<id>(?:(?!(?P=q4)).)+)(?P=q4)(?:,|\s*\})
|
||||
""", webpage) or
|
||||
re.search(
|
||||
""", webpage)
|
||||
or re.search(
|
||||
r'''(?xs)
|
||||
(?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*
|
||||
)
|
||||
(?P<q3>["'])(?P<id>(?:(?!(?P=q3)).)+)(?P=q3)
|
||||
''', webpage) or
|
||||
re.search(
|
||||
''', webpage)
|
||||
or re.search(
|
||||
r'''(?xs)
|
||||
<(?:iframe[^>]+src|meta[^>]+\bcontent)=(?P<q1>["'])
|
||||
(?: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)
|
||||
|
||||
title = (self._html_search_meta('title', webpage, default=None) or
|
||||
self._search_regex(r'<h1 class="title">([^<]+)</h1>', webpage, 'video title'))
|
||||
title = (self._html_search_meta('title', webpage, default=None)
|
||||
or self._search_regex(r'<h1 class="title">([^<]+)</h1>', webpage, 'video title'))
|
||||
|
||||
video_id = self._search_regex(
|
||||
r'/config/video/(.+?)\.xml', webpage, 'video id')
|
||||
|
@@ -80,8 +80,8 @@ class MotherlessIE(InfoExtractor):
|
||||
video_url = (self._html_search_regex(
|
||||
(r'setup\(\{\s*["\']file["\']\s*:\s*(["\'])(?P<url>(?:(?!\1).)+)\1',
|
||||
r'fileurl\s*=\s*(["\'])(?P<url>(?:(?!\1).)+)\1'),
|
||||
webpage, 'video URL', default=None, group='url') or
|
||||
'http://cdn4.videos.motherlessmedia.com/videos/%s.mp4?fs=opencloud' % video_id)
|
||||
webpage, 'video URL', default=None, group='url')
|
||||
or 'http://cdn4.videos.motherlessmedia.com/videos/%s.mp4?fs=opencloud' % video_id)
|
||||
age_limit = self._rta_search(webpage)
|
||||
view_count = str_to_int(self._html_search_regex(
|
||||
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 = compat_urllib_parse_unquote_plus(
|
||||
self._search_regex(r"__title\s*=\s*'([^']+)'", webpage, 'title', default=None) or
|
||||
self._og_search_title(webpage))
|
||||
self._search_regex(r"__title\s*=\s*'([^']+)'", webpage, 'title', default=None)
|
||||
or self._og_search_title(webpage))
|
||||
|
||||
filename = self._search_regex(
|
||||
r"(?:__)?filename\s*[:=]\s*'([^']+)'", webpage, 'video filename')
|
||||
|
@@ -180,8 +180,8 @@ class AppleDailyIE(NextMediaIE):
|
||||
_URL_PATTERN = r'\{url: \'(.+)\'\}'
|
||||
|
||||
def _fetch_title(self, page):
|
||||
return (self._html_search_regex(r'<h1 id="h1">([^<>]+)</h1>', page, 'news title', default=None) or
|
||||
self._html_search_meta('description', page, 'news title'))
|
||||
return (self._html_search_regex(r'<h1 id="h1">([^<>]+)</h1>', page, 'news title', default=None)
|
||||
or self._html_search_meta('description', page, 'news title'))
|
||||
|
||||
def _fetch_thumbnail(self, page):
|
||||
return self._html_search_regex(r"setInitialImage\(\'([^']+)'\)", page, 'video thumbnail', fatal=False)
|
||||
|
@@ -1,54 +1,81 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
|
||||
from .common import InfoExtractor
|
||||
from ..utils import ExtractorError
|
||||
|
||||
|
||||
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 = [{
|
||||
# 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/',
|
||||
'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):
|
||||
video_id = self._match_id(url)
|
||||
|
||||
data = self._download_json(self._API_URL, video_id)
|
||||
|
||||
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']
|
||||
lang, m_type, episode_id = re.match(self._VALID_URL, url).groups()
|
||||
if episode_id.isdigit():
|
||||
episode_id = episode_id[:4] + '-' + episode_id[4:]
|
||||
|
||||
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']
|
||||
description = episode.get('description_clean') or episode.get('description')
|
||||
series = episode.get('title_clean') or episode.get('title')
|
||||
|
||||
return {
|
||||
'_type': 'url_transparent',
|
||||
'ie_key': 'Ooyala',
|
||||
'url': 'ooyala:%s' % embed_code,
|
||||
def get_clean_field(key):
|
||||
return episode.get(key + '_clean') or episode.get(key)
|
||||
|
||||
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,
|
||||
'description': description,
|
||||
'description': get_clean_field('description'),
|
||||
'thumbnails': thumbnails,
|
||||
'series': series,
|
||||
'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', {})
|
||||
|
||||
thumbnail = (
|
||||
get_video_info(['thumbnail_url', 'thumbnailURL']) or
|
||||
self._html_search_meta('image', webpage, 'thumbnail', default=None) or
|
||||
video_detail.get('thumbnail'))
|
||||
get_video_info(['thumbnail_url', 'thumbnailURL'])
|
||||
or self._html_search_meta('image', webpage, 'thumbnail', default=None)
|
||||
or video_detail.get('thumbnail'))
|
||||
|
||||
description = get_video_info('description')
|
||||
|
||||
timestamp = (parse_iso8601(get_video_info('first_retrieve')) or
|
||||
unified_timestamp(get_video_info('postedDateTime')))
|
||||
timestamp = (parse_iso8601(get_video_info('first_retrieve'))
|
||||
or unified_timestamp(get_video_info('postedDateTime')))
|
||||
if not timestamp:
|
||||
match = self._html_search_meta('datePublished', webpage, 'date published', default=None)
|
||||
if match:
|
||||
@@ -395,9 +395,9 @@ class NiconicoIE(InfoExtractor):
|
||||
view_count = int_or_none(match.replace(',', ''))
|
||||
view_count = view_count or video_detail.get('viewCount')
|
||||
|
||||
comment_count = (int_or_none(get_video_info('comment_num')) or
|
||||
video_detail.get('commentCount') or
|
||||
try_get(api_data, lambda x: x['thread']['commentCount']))
|
||||
comment_count = (int_or_none(get_video_info('comment_num'))
|
||||
or video_detail.get('commentCount')
|
||||
or try_get(api_data, lambda x: x['thread']['commentCount']))
|
||||
if not comment_count:
|
||||
match = self._html_search_regex(
|
||||
r'>Comments: <strong[^>]*>([^<]+)</strong>',
|
||||
@@ -406,11 +406,11 @@ class NiconicoIE(InfoExtractor):
|
||||
comment_count = int_or_none(match.replace(',', ''))
|
||||
|
||||
duration = (parse_duration(
|
||||
get_video_info('length') or
|
||||
self._html_search_meta(
|
||||
'video:duration', webpage, 'video duration', default=None)) or
|
||||
video_detail.get('length') or
|
||||
get_video_info('duration'))
|
||||
get_video_info('length')
|
||||
or self._html_search_meta(
|
||||
'video:duration', webpage, 'video duration', default=None))
|
||||
or video_detail.get('length')
|
||||
or get_video_info('duration'))
|
||||
|
||||
webpage_url = get_video_info('watch_url') or url
|
||||
|
||||
|
@@ -45,8 +45,8 @@ class NRKBaseIE(InfoExtractor):
|
||||
entries = []
|
||||
|
||||
conviva = data.get('convivaStatistics') or {}
|
||||
live = (data.get('mediaElementType') == 'Live' or
|
||||
data.get('isLive') is True or conviva.get('isLive'))
|
||||
live = (data.get('mediaElementType') == 'Live'
|
||||
or data.get('isLive') is True or conviva.get('isLive'))
|
||||
|
||||
def make_title(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']
|
||||
|
||||
auth_data = self._download_json(
|
||||
self._AUTHORIZATION_URL_TEMPLATE % (pcode, embed_code) +
|
||||
compat_urllib_parse_urlencode({
|
||||
self._AUTHORIZATION_URL_TEMPLATE % (pcode, embed_code)
|
||||
+ compat_urllib_parse_urlencode({
|
||||
'domain': domain,
|
||||
'supportedFormats': supportedformats or 'mp4,rtmp,m3u8,hds,dash,smooth',
|
||||
'embedToken': embed_token,
|
||||
}), video_id)
|
||||
}), video_id, headers=self.geo_verification_headers())
|
||||
|
||||
cur_auth_data = auth_data['authorization_data'][embed_code]
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -50,8 +50,8 @@ class PodomaticIE(InfoExtractor):
|
||||
video_id = mobj.group('id')
|
||||
channel = mobj.group('channel') or mobj.group('channel_2')
|
||||
|
||||
json_url = (('%s://%s.podomatic.com/entry/embed_params/%s' +
|
||||
'?permalink=true&rtmp=0') %
|
||||
json_url = (('%s://%s.podomatic.com/entry/embed_params/%s'
|
||||
+ '?permalink=true&rtmp=0') %
|
||||
(mobj.group('proto'), channel, video_id))
|
||||
data_json = self._download_webpage(
|
||||
json_url, video_id, 'Downloading video info')
|
||||
|
@@ -10,7 +10,7 @@ from ..utils import (
|
||||
|
||||
|
||||
class RedBullTVIE(InfoExtractor):
|
||||
_VALID_URL = r'https?://(?:www\.)?redbull(?:\.tv|\.com/(?:[^/]+/)?tv)/video/(?P<id>AP-\w+)'
|
||||
_VALID_URL = r'https?://(?:www\.)?redbull(?:\.tv|\.com(?:/[^/]+)?(?:/tv)?)(?:/events/[^/]+)?/(?:videos?|live)/(?P<id>AP-\w+)'
|
||||
_TESTS = [{
|
||||
# film
|
||||
'url': 'https://www.redbull.tv/video/AP-1Q6XCDTAN1W11',
|
||||
@@ -38,6 +38,12 @@ class RedBullTVIE(InfoExtractor):
|
||||
}, {
|
||||
'url': 'https://www.redbull.com/int-en/tv/video/AP-1UWHCAR9S1W11/rob-meets-sam-gaze?playlist=playlists::3f81040a-2f31-4832-8e2e-545b1d39d173',
|
||||
'only_matching': True,
|
||||
}, {
|
||||
'url': 'https://www.redbull.com/us-en/videos/AP-1YM9QCYE52111',
|
||||
'only_matching': True,
|
||||
}, {
|
||||
'url': 'https://www.redbull.com/us-en/events/AP-1XV2K61Q51W11/live/AP-1XUJ86FDH1W11',
|
||||
'only_matching': True,
|
||||
}]
|
||||
|
||||
def _real_extract(self, url):
|
||||
|
@@ -7,6 +7,7 @@ from ..utils import (
|
||||
ExtractorError,
|
||||
int_or_none,
|
||||
float_or_none,
|
||||
url_or_none,
|
||||
)
|
||||
|
||||
|
||||
@@ -119,7 +120,7 @@ class RedditRIE(InfoExtractor):
|
||||
'_type': 'url_transparent',
|
||||
'url': video_url,
|
||||
'title': data.get('title'),
|
||||
'thumbnail': data.get('thumbnail'),
|
||||
'thumbnail': url_or_none(data.get('thumbnail')),
|
||||
'timestamp': float_or_none(data.get('created_utc')),
|
||||
'uploader': data.get('author'),
|
||||
'like_count': int_or_none(data.get('ups')),
|
||||
|
@@ -91,8 +91,8 @@ class RuutuIE(InfoExtractor):
|
||||
extract_formats(child)
|
||||
elif child.tag.endswith('File'):
|
||||
video_url = child.text
|
||||
if (not video_url or video_url in processed_urls or
|
||||
any(p in video_url for p in ('NOT_USED', 'NOT-USED'))):
|
||||
if (not video_url or video_url in processed_urls
|
||||
or any(p in video_url for p in ('NOT_USED', 'NOT-USED'))):
|
||||
continue
|
||||
processed_urls.append(video_url)
|
||||
ext = determine_ext(video_url)
|
||||
|
@@ -55,8 +55,8 @@ class SBSIE(InfoExtractor):
|
||||
raise ExtractorError('%s said: %s' % (self.IE_NAME, error_message), expected=True)
|
||||
|
||||
urls = player_params['releaseUrls']
|
||||
theplatform_url = (urls.get('progressive') or urls.get('html') or
|
||||
urls.get('standard') or player_params['relatedItemsURL'])
|
||||
theplatform_url = (urls.get('progressive') or urls.get('html')
|
||||
or urls.get('standard') or player_params['relatedItemsURL'])
|
||||
|
||||
return {
|
||||
'_type': 'url_transparent',
|
||||
|
@@ -65,7 +65,7 @@ class SixPlayIE(InfoExtractor):
|
||||
for asset in assets:
|
||||
asset_url = asset.get('full_physical_path')
|
||||
protocol = asset.get('protocol')
|
||||
if not asset_url or protocol == 'primetime' or asset.get('type') == 'usp_hlsfp_h264' or asset_url in urls:
|
||||
if not asset_url or ((protocol == 'primetime' or asset.get('type') == 'usp_hlsfp_h264') and not ('_drmnp.ism/' in asset_url or '_unpnp.ism/' in asset_url)) or asset_url in urls:
|
||||
continue
|
||||
urls.append(asset_url)
|
||||
container = asset.get('video_container')
|
||||
@@ -82,6 +82,7 @@ class SixPlayIE(InfoExtractor):
|
||||
if not urlh:
|
||||
continue
|
||||
asset_url = urlh.geturl()
|
||||
asset_url = asset_url.replace('_drmnp.ism/', '_unpnp.ism/')
|
||||
for i in range(3, 0, -1):
|
||||
asset_url = asset_url = asset_url.replace('_sd1/', '_sd%d/' % i)
|
||||
m3u8_formats = self._extract_m3u8_formats(
|
||||
|
@@ -10,34 +10,25 @@ from ..utils import (
|
||||
)
|
||||
|
||||
|
||||
class SkySportsIE(InfoExtractor):
|
||||
_VALID_URL = r'https?://(?:www\.)?skysports\.com/watch/video/(?P<id>[0-9]+)'
|
||||
_TEST = {
|
||||
'url': 'http://www.skysports.com/watch/video/10328419/bale-its-our-time-to-shine',
|
||||
'md5': '77d59166cddc8d3cb7b13e35eaf0f5ec',
|
||||
'info_dict': {
|
||||
'id': '10328419',
|
||||
'ext': 'mp4',
|
||||
'title': 'Bale: It\'s our time to shine',
|
||||
'description': 'md5:e88bda94ae15f7720c5cb467e777bb6d',
|
||||
},
|
||||
'add_ie': ['Ooyala'],
|
||||
}
|
||||
|
||||
class SkyBaseIE(InfoExtractor):
|
||||
def _real_extract(self, url):
|
||||
video_id = self._match_id(url)
|
||||
webpage = self._download_webpage(url, video_id)
|
||||
video_data = extract_attributes(self._search_regex(
|
||||
r'(<div.+?class="sdc-article-video__media-ooyala"[^>]+>)', webpage, 'video data'))
|
||||
r'(<div.+?class="[^"]*sdc-article-video__media-ooyala[^"]*"[^>]+>)',
|
||||
webpage, 'video data'))
|
||||
|
||||
video_url = 'ooyala:%s' % video_data['data-video-id']
|
||||
if video_data.get('data-token-required') == 'true':
|
||||
token_fetch_options = self._parse_json(video_data.get('data-token-fetch-options', '{}'), video_id, fatal=False) or {}
|
||||
token_fetch_options = self._parse_json(video_data.get(
|
||||
'data-token-fetch-options', '{}'), video_id, fatal=False) or {}
|
||||
token_fetch_url = token_fetch_options.get('url')
|
||||
if token_fetch_url:
|
||||
embed_token = self._download_webpage(urljoin(url, token_fetch_url), video_id, fatal=False)
|
||||
embed_token = self._download_webpage(urljoin(
|
||||
url, token_fetch_url), video_id, fatal=False)
|
||||
if embed_token:
|
||||
video_url = smuggle_url(video_url, {'embed_token': embed_token.strip('"')})
|
||||
video_url = smuggle_url(
|
||||
video_url, {'embed_token': embed_token.strip('"')})
|
||||
|
||||
return {
|
||||
'_type': 'url_transparent',
|
||||
@@ -47,3 +38,33 @@ class SkySportsIE(InfoExtractor):
|
||||
'description': strip_or_none(self._og_search_description(webpage)),
|
||||
'ie_key': 'Ooyala',
|
||||
}
|
||||
|
||||
|
||||
class SkySportsIE(SkyBaseIE):
|
||||
_VALID_URL = r'https?://(?:www\.)?skysports\.com/watch/video/(?P<id>[0-9]+)'
|
||||
_TEST = {
|
||||
'url': 'http://www.skysports.com/watch/video/10328419/bale-its-our-time-to-shine',
|
||||
'md5': '77d59166cddc8d3cb7b13e35eaf0f5ec',
|
||||
'info_dict': {
|
||||
'id': 'o3eWJnNDE6l7kfNO8BOoBlRxXRQ4ANNQ',
|
||||
'ext': 'mp4',
|
||||
'title': 'Bale: It\'s our time to shine',
|
||||
'description': 'md5:e88bda94ae15f7720c5cb467e777bb6d',
|
||||
},
|
||||
'add_ie': ['Ooyala'],
|
||||
}
|
||||
|
||||
|
||||
class SkyNewsIE(SkyBaseIE):
|
||||
_VALID_URL = r'https?://news\.sky\.com/video/[0-9a-z-]+-(?P<id>[0-9]+)'
|
||||
_TEST = {
|
||||
'url': 'https://news.sky.com/video/russian-plane-inspected-after-deadly-fire-11712962',
|
||||
'md5': 'd6327e581473cea9976a3236ded370cd',
|
||||
'info_dict': {
|
||||
'id': '1ua21xaDE6lCtZDmbYfl8kwsKLooJbNM',
|
||||
'ext': 'mp4',
|
||||
'title': 'Russian plane inspected after deadly fire',
|
||||
'description': 'The Russian Investigative Committee has released video of the wreckage of a passenger plane which caught fire near Moscow.',
|
||||
},
|
||||
'add_ie': ['Ooyala'],
|
||||
}
|
@@ -15,7 +15,12 @@ from ..compat import (
|
||||
)
|
||||
from ..utils import (
|
||||
ExtractorError,
|
||||
float_or_none,
|
||||
int_or_none,
|
||||
KNOWN_EXTENSIONS,
|
||||
merge_dicts,
|
||||
mimetype2ext,
|
||||
str_or_none,
|
||||
try_get,
|
||||
unified_timestamp,
|
||||
update_url_query,
|
||||
@@ -57,7 +62,7 @@ class SoundcloudIE(InfoExtractor):
|
||||
'uploader': 'E.T. ExTerrestrial Music',
|
||||
'timestamp': 1349920598,
|
||||
'upload_date': '20121011',
|
||||
'duration': 143,
|
||||
'duration': 143.216,
|
||||
'license': 'all-rights-reserved',
|
||||
'view_count': int,
|
||||
'like_count': int,
|
||||
@@ -100,7 +105,7 @@ class SoundcloudIE(InfoExtractor):
|
||||
'uploader': 'jaimeMF',
|
||||
'timestamp': 1386604920,
|
||||
'upload_date': '20131209',
|
||||
'duration': 9,
|
||||
'duration': 9.927,
|
||||
'license': 'all-rights-reserved',
|
||||
'view_count': int,
|
||||
'like_count': int,
|
||||
@@ -120,7 +125,7 @@ class SoundcloudIE(InfoExtractor):
|
||||
'uploader': 'jaimeMF',
|
||||
'timestamp': 1386604920,
|
||||
'upload_date': '20131209',
|
||||
'duration': 9,
|
||||
'duration': 9.927,
|
||||
'license': 'all-rights-reserved',
|
||||
'view_count': int,
|
||||
'like_count': int,
|
||||
@@ -140,7 +145,7 @@ class SoundcloudIE(InfoExtractor):
|
||||
'uploader': 'oddsamples',
|
||||
'timestamp': 1389232924,
|
||||
'upload_date': '20140109',
|
||||
'duration': 17,
|
||||
'duration': 17.346,
|
||||
'license': 'cc-by-sa',
|
||||
'view_count': int,
|
||||
'like_count': int,
|
||||
@@ -160,7 +165,7 @@ class SoundcloudIE(InfoExtractor):
|
||||
'uploader': 'Ori Uplift Music',
|
||||
'timestamp': 1504206263,
|
||||
'upload_date': '20170831',
|
||||
'duration': 7449,
|
||||
'duration': 7449.096,
|
||||
'license': 'all-rights-reserved',
|
||||
'view_count': int,
|
||||
'like_count': int,
|
||||
@@ -180,7 +185,7 @@ class SoundcloudIE(InfoExtractor):
|
||||
'uploader': 'garyvee',
|
||||
'timestamp': 1488152409,
|
||||
'upload_date': '20170226',
|
||||
'duration': 207,
|
||||
'duration': 207.012,
|
||||
'thumbnail': r're:https?://.*\.jpg',
|
||||
'license': 'all-rights-reserved',
|
||||
'view_count': int,
|
||||
@@ -192,9 +197,31 @@ class SoundcloudIE(InfoExtractor):
|
||||
'skip_download': True,
|
||||
},
|
||||
},
|
||||
# not avaialble via api.soundcloud.com/i1/tracks/id/streams
|
||||
{
|
||||
'url': 'https://soundcloud.com/giovannisarani/mezzo-valzer',
|
||||
'md5': 'e22aecd2bc88e0e4e432d7dcc0a1abf7',
|
||||
'info_dict': {
|
||||
'id': '583011102',
|
||||
'ext': 'mp3',
|
||||
'title': 'Mezzo Valzer',
|
||||
'description': 'md5:4138d582f81866a530317bae316e8b61',
|
||||
'uploader': 'Giovanni Sarani',
|
||||
'timestamp': 1551394171,
|
||||
'upload_date': '20190228',
|
||||
'duration': 180.157,
|
||||
'thumbnail': r're:https?://.*\.jpg',
|
||||
'license': 'all-rights-reserved',
|
||||
'view_count': int,
|
||||
'like_count': int,
|
||||
'comment_count': int,
|
||||
'repost_count': int,
|
||||
},
|
||||
'expected_warnings': ['Unable to download JSON metadata'],
|
||||
}
|
||||
]
|
||||
|
||||
_CLIENT_ID = 'NmW1FlPaiL94ueEu7oziOWjYEzZzQDcK'
|
||||
_CLIENT_ID = 'FweeGBOOEOYJWLJN3oEyToGLKhmSz0I7'
|
||||
|
||||
@staticmethod
|
||||
def _extract_urls(webpage):
|
||||
@@ -202,10 +229,6 @@ class SoundcloudIE(InfoExtractor):
|
||||
r'<iframe[^>]+src=(["\'])(?P<url>(?:https?://)?(?:w\.)?soundcloud\.com/player.+?)\1',
|
||||
webpage)]
|
||||
|
||||
def report_resolve(self, video_id):
|
||||
"""Report information extraction."""
|
||||
self.to_screen('%s: Resolving id' % video_id)
|
||||
|
||||
@classmethod
|
||||
def _resolv_url(cls, url):
|
||||
return 'https://api.soundcloud.com/resolve.json?url=' + url + '&client_id=' + cls._CLIENT_ID
|
||||
@@ -224,6 +247,10 @@ class SoundcloudIE(InfoExtractor):
|
||||
def extract_count(key):
|
||||
return int_or_none(info.get('%s_count' % key))
|
||||
|
||||
like_count = extract_count('favoritings')
|
||||
if like_count is None:
|
||||
like_count = extract_count('likes')
|
||||
|
||||
result = {
|
||||
'id': track_id,
|
||||
'uploader': username,
|
||||
@@ -231,15 +258,17 @@ class SoundcloudIE(InfoExtractor):
|
||||
'title': title,
|
||||
'description': info.get('description'),
|
||||
'thumbnail': thumbnail,
|
||||
'duration': int_or_none(info.get('duration'), 1000),
|
||||
'duration': float_or_none(info.get('duration'), 1000),
|
||||
'webpage_url': info.get('permalink_url'),
|
||||
'license': info.get('license'),
|
||||
'view_count': extract_count('playback'),
|
||||
'like_count': extract_count('favoritings'),
|
||||
'like_count': like_count,
|
||||
'comment_count': extract_count('comment'),
|
||||
'repost_count': extract_count('reposts'),
|
||||
'genre': info.get('genre'),
|
||||
}
|
||||
|
||||
format_urls = set()
|
||||
formats = []
|
||||
query = {'client_id': self._CLIENT_ID}
|
||||
if secret_token is not None:
|
||||
@@ -248,6 +277,7 @@ class SoundcloudIE(InfoExtractor):
|
||||
# We can build a direct link to the song
|
||||
format_url = update_url_query(
|
||||
'https://api.soundcloud.com/tracks/%s/download' % track_id, query)
|
||||
format_urls.add(format_url)
|
||||
formats.append({
|
||||
'format_id': 'download',
|
||||
'ext': info.get('original_format', 'mp3'),
|
||||
@@ -256,44 +286,91 @@ class SoundcloudIE(InfoExtractor):
|
||||
'preference': 10,
|
||||
})
|
||||
|
||||
# We have to retrieve the url
|
||||
# Old API, does not work for some tracks (e.g.
|
||||
# https://soundcloud.com/giovannisarani/mezzo-valzer)
|
||||
format_dict = self._download_json(
|
||||
'https://api.soundcloud.com/i1/tracks/%s/streams' % track_id,
|
||||
track_id, 'Downloading track url', query=query)
|
||||
track_id, 'Downloading track url', query=query, fatal=False)
|
||||
|
||||
for key, stream_url in format_dict.items():
|
||||
ext, abr = 'mp3', None
|
||||
mobj = re.search(r'_([^_]+)_(\d+)_url', key)
|
||||
if mobj:
|
||||
ext, abr = mobj.groups()
|
||||
abr = int(abr)
|
||||
if key.startswith('http'):
|
||||
stream_formats = [{
|
||||
'format_id': key,
|
||||
'ext': ext,
|
||||
'url': stream_url,
|
||||
}]
|
||||
elif key.startswith('rtmp'):
|
||||
# The url doesn't have an rtmp app, we have to extract the playpath
|
||||
url, path = stream_url.split('mp3:', 1)
|
||||
stream_formats = [{
|
||||
'format_id': key,
|
||||
'url': url,
|
||||
'play_path': 'mp3:' + path,
|
||||
'ext': 'flv',
|
||||
}]
|
||||
elif key.startswith('hls'):
|
||||
stream_formats = self._extract_m3u8_formats(
|
||||
stream_url, track_id, ext, entry_protocol='m3u8_native',
|
||||
m3u8_id=key, fatal=False)
|
||||
else:
|
||||
if format_dict:
|
||||
for key, stream_url in format_dict.items():
|
||||
if stream_url in format_urls:
|
||||
continue
|
||||
format_urls.add(stream_url)
|
||||
ext, abr = 'mp3', None
|
||||
mobj = re.search(r'_([^_]+)_(\d+)_url', key)
|
||||
if mobj:
|
||||
ext, abr = mobj.groups()
|
||||
abr = int(abr)
|
||||
if key.startswith('http'):
|
||||
stream_formats = [{
|
||||
'format_id': key,
|
||||
'ext': ext,
|
||||
'url': stream_url,
|
||||
}]
|
||||
elif key.startswith('rtmp'):
|
||||
# The url doesn't have an rtmp app, we have to extract the playpath
|
||||
url, path = stream_url.split('mp3:', 1)
|
||||
stream_formats = [{
|
||||
'format_id': key,
|
||||
'url': url,
|
||||
'play_path': 'mp3:' + path,
|
||||
'ext': 'flv',
|
||||
}]
|
||||
elif key.startswith('hls'):
|
||||
stream_formats = self._extract_m3u8_formats(
|
||||
stream_url, track_id, ext, entry_protocol='m3u8_native',
|
||||
m3u8_id=key, fatal=False)
|
||||
else:
|
||||
continue
|
||||
|
||||
if abr:
|
||||
for f in stream_formats:
|
||||
f['abr'] = abr
|
||||
|
||||
formats.extend(stream_formats)
|
||||
|
||||
# New API
|
||||
transcodings = try_get(
|
||||
info, lambda x: x['media']['transcodings'], list) or []
|
||||
for t in transcodings:
|
||||
if not isinstance(t, dict):
|
||||
continue
|
||||
|
||||
if abr:
|
||||
for f in stream_formats:
|
||||
f['abr'] = abr
|
||||
|
||||
formats.extend(stream_formats)
|
||||
format_url = url_or_none(t.get('url'))
|
||||
if not format_url:
|
||||
continue
|
||||
stream = self._download_json(
|
||||
update_url_query(format_url, query), track_id, fatal=False)
|
||||
if not isinstance(stream, dict):
|
||||
continue
|
||||
stream_url = url_or_none(stream.get('url'))
|
||||
if not stream_url:
|
||||
continue
|
||||
if stream_url in format_urls:
|
||||
continue
|
||||
format_urls.add(stream_url)
|
||||
protocol = try_get(t, lambda x: x['format']['protocol'], compat_str)
|
||||
if protocol != 'hls' and '/hls' in format_url:
|
||||
protocol = 'hls'
|
||||
ext = None
|
||||
preset = str_or_none(t.get('preset'))
|
||||
if preset:
|
||||
ext = preset.split('_')[0]
|
||||
if ext not in KNOWN_EXTENSIONS:
|
||||
mimetype = try_get(
|
||||
t, lambda x: x['format']['mime_type'], compat_str)
|
||||
ext = mimetype2ext(mimetype) or 'mp3'
|
||||
format_id_list = []
|
||||
if protocol:
|
||||
format_id_list.append(protocol)
|
||||
format_id_list.append(ext)
|
||||
format_id = '_'.join(format_id_list)
|
||||
formats.append({
|
||||
'url': stream_url,
|
||||
'format_id': format_id,
|
||||
'ext': ext,
|
||||
'protocol': 'm3u8_native' if protocol == 'hls' else 'http',
|
||||
})
|
||||
|
||||
if not formats:
|
||||
# We fallback to the stream_url in the original info, this
|
||||
@@ -303,11 +380,11 @@ class SoundcloudIE(InfoExtractor):
|
||||
'url': update_url_query(info['stream_url'], query),
|
||||
'ext': 'mp3',
|
||||
})
|
||||
self._check_formats(formats, track_id)
|
||||
|
||||
for f in formats:
|
||||
f['vcodec'] = 'none'
|
||||
|
||||
self._check_formats(formats, track_id)
|
||||
self._sort_formats(formats)
|
||||
result['formats'] = formats
|
||||
|
||||
@@ -319,6 +396,7 @@ class SoundcloudIE(InfoExtractor):
|
||||
raise ExtractorError('Invalid URL: %s' % url)
|
||||
|
||||
track_id = mobj.group('track_id')
|
||||
new_info = {}
|
||||
|
||||
if track_id is not None:
|
||||
info_json_url = 'https://api.soundcloud.com/tracks/' + track_id + '.json?client_id=' + self._CLIENT_ID
|
||||
@@ -344,13 +422,31 @@ class SoundcloudIE(InfoExtractor):
|
||||
if token:
|
||||
resolve_title += '/%s' % token
|
||||
|
||||
self.report_resolve(full_title)
|
||||
webpage = self._download_webpage(url, full_title, fatal=False)
|
||||
if webpage:
|
||||
entries = self._parse_json(
|
||||
self._search_regex(
|
||||
r'var\s+c\s*=\s*(\[.+?\])\s*,\s*o\s*=Date\b', webpage,
|
||||
'data', default='[]'), full_title, fatal=False)
|
||||
if entries:
|
||||
for e in entries:
|
||||
if not isinstance(e, dict):
|
||||
continue
|
||||
if e.get('id') != 67:
|
||||
continue
|
||||
data = try_get(e, lambda x: x['data'][0], dict)
|
||||
if data:
|
||||
new_info = data
|
||||
break
|
||||
info_json_url = self._resolv_url(
|
||||
'https://soundcloud.com/%s' % resolve_title)
|
||||
|
||||
url = 'https://soundcloud.com/%s' % resolve_title
|
||||
info_json_url = self._resolv_url(url)
|
||||
info = self._download_json(info_json_url, full_title, 'Downloading info JSON')
|
||||
# Contains some additional info missing from new_info
|
||||
info = self._download_json(
|
||||
info_json_url, full_title, 'Downloading info JSON')
|
||||
|
||||
return self._extract_info_dict(info, full_title, secret_token=token)
|
||||
return self._extract_info_dict(
|
||||
merge_dicts(info, new_info), full_title, secret_token=token)
|
||||
|
||||
|
||||
class SoundcloudPlaylistBaseIE(SoundcloudIE):
|
||||
@@ -396,8 +492,6 @@ class SoundcloudSetIE(SoundcloudPlaylistBaseIE):
|
||||
full_title += '/' + token
|
||||
url += '/' + token
|
||||
|
||||
self.report_resolve(full_title)
|
||||
|
||||
resolv_url = self._resolv_url(url)
|
||||
info = self._download_json(resolv_url, full_title)
|
||||
|
||||
|
115
youtube_dl/extractor/sverigesradio.py
Normal file
115
youtube_dl/extractor/sverigesradio.py
Normal file
@@ -0,0 +1,115 @@
|
||||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .common import InfoExtractor
|
||||
from ..utils import (
|
||||
determine_ext,
|
||||
int_or_none,
|
||||
str_or_none,
|
||||
)
|
||||
|
||||
|
||||
class SverigesRadioBaseIE(InfoExtractor):
|
||||
_BASE_URL = 'https://sverigesradio.se/sida/playerajax/'
|
||||
_QUALITIES = ['low', 'medium', 'high']
|
||||
_EXT_TO_CODEC_MAP = {
|
||||
'mp3': 'mp3',
|
||||
'm4a': 'aac',
|
||||
}
|
||||
_CODING_FORMAT_TO_ABR_MAP = {
|
||||
5: 128,
|
||||
11: 192,
|
||||
12: 32,
|
||||
13: 96,
|
||||
}
|
||||
|
||||
def _real_extract(self, url):
|
||||
audio_id = self._match_id(url)
|
||||
query = {
|
||||
'id': audio_id,
|
||||
'type': self._AUDIO_TYPE,
|
||||
}
|
||||
|
||||
item = self._download_json(
|
||||
self._BASE_URL + 'audiometadata', audio_id,
|
||||
'Downloading audio JSON metadata', query=query)['items'][0]
|
||||
title = item['subtitle']
|
||||
|
||||
query['format'] = 'iis'
|
||||
urls = []
|
||||
formats = []
|
||||
for quality in self._QUALITIES:
|
||||
query['quality'] = quality
|
||||
audio_url_data = self._download_json(
|
||||
self._BASE_URL + 'getaudiourl', audio_id,
|
||||
'Downloading %s format JSON metadata' % quality,
|
||||
fatal=False, query=query) or {}
|
||||
audio_url = audio_url_data.get('audioUrl')
|
||||
if not audio_url or audio_url in urls:
|
||||
continue
|
||||
urls.append(audio_url)
|
||||
ext = determine_ext(audio_url)
|
||||
coding_format = audio_url_data.get('codingFormat')
|
||||
abr = int_or_none(self._search_regex(
|
||||
r'_a(\d+)\.m4a', audio_url, 'audio bitrate',
|
||||
default=None)) or self._CODING_FORMAT_TO_ABR_MAP.get(coding_format)
|
||||
formats.append({
|
||||
'abr': abr,
|
||||
'acodec': self._EXT_TO_CODEC_MAP.get(ext),
|
||||
'ext': ext,
|
||||
'format_id': str_or_none(coding_format),
|
||||
'vcodec': 'none',
|
||||
'url': audio_url,
|
||||
})
|
||||
self._sort_formats(formats)
|
||||
|
||||
return {
|
||||
'id': audio_id,
|
||||
'title': title,
|
||||
'formats': formats,
|
||||
'series': item.get('title'),
|
||||
'duration': int_or_none(item.get('duration')),
|
||||
'thumbnail': item.get('displayimageurl'),
|
||||
'description': item.get('description'),
|
||||
}
|
||||
|
||||
|
||||
class SverigesRadioPublicationIE(SverigesRadioBaseIE):
|
||||
IE_NAME = 'sverigesradio:publication'
|
||||
_VALID_URL = r'https?://(?:www\.)?sverigesradio\.se/sida/(?:artikel|gruppsida)\.aspx\?.*?\bartikel=(?P<id>[0-9]+)'
|
||||
_TESTS = [{
|
||||
'url': 'https://sverigesradio.se/sida/artikel.aspx?programid=83&artikel=7038546',
|
||||
'md5': '6a4917e1923fccb080e5a206a5afa542',
|
||||
'info_dict': {
|
||||
'id': '7038546',
|
||||
'ext': 'm4a',
|
||||
'duration': 132,
|
||||
'series': 'Nyheter (Ekot)',
|
||||
'title': 'Esa Teittinen: Sanningen har inte kommit fram',
|
||||
'description': 'md5:daf7ce66a8f0a53d5465a5984d3839df',
|
||||
'thumbnail': r're:^https?://.*\.jpg',
|
||||
},
|
||||
}, {
|
||||
'url': 'https://sverigesradio.se/sida/gruppsida.aspx?programid=3304&grupp=6247&artikel=7146887',
|
||||
'only_matching': True,
|
||||
}]
|
||||
_AUDIO_TYPE = 'publication'
|
||||
|
||||
|
||||
class SverigesRadioEpisodeIE(SverigesRadioBaseIE):
|
||||
IE_NAME = 'sverigesradio:episode'
|
||||
_VALID_URL = r'https?://(?:www\.)?sverigesradio\.se/(?:sida/)?avsnitt/(?P<id>[0-9]+)'
|
||||
_TEST = {
|
||||
'url': 'https://sverigesradio.se/avsnitt/1140922?programid=1300',
|
||||
'md5': '20dc4d8db24228f846be390b0c59a07c',
|
||||
'info_dict': {
|
||||
'id': '1140922',
|
||||
'ext': 'mp3',
|
||||
'duration': 3307,
|
||||
'series': 'Konflikt',
|
||||
'title': 'Metoo och valen',
|
||||
'description': 'md5:fcb5c1f667f00badcc702b196f10a27e',
|
||||
'thumbnail': r're:^https?://.*\.jpg',
|
||||
}
|
||||
}
|
||||
_AUDIO_TYPE = 'episode'
|
@@ -66,7 +66,12 @@ class TouTvIE(RadioCanadaIE):
|
||||
|
||||
def _real_extract(self, url):
|
||||
path = self._match_id(url)
|
||||
metadata = self._download_json('http://ici.tou.tv/presentation/%s' % path, path)
|
||||
metadata = self._download_json(
|
||||
'https://services.radio-canada.ca/toutv/presentation/%s' % path, path, query={
|
||||
'client_key': self._CLIENT_KEY,
|
||||
'device': 'web',
|
||||
'version': 4,
|
||||
})
|
||||
# IsDrm does not necessarily mean the video is DRM protected (see
|
||||
# https://github.com/ytdl-org/youtube-dl/issues/13994).
|
||||
if metadata.get('IsDrm'):
|
||||
@@ -77,6 +82,12 @@ class TouTvIE(RadioCanadaIE):
|
||||
return merge_dicts({
|
||||
'id': video_id,
|
||||
'title': details.get('OriginalTitle'),
|
||||
'description': details.get('Description'),
|
||||
'thumbnail': details.get('ImageUrl'),
|
||||
'duration': int_or_none(details.get('LengthInSeconds')),
|
||||
'series': metadata.get('ProgramTitle'),
|
||||
'season_number': int_or_none(metadata.get('SeasonNumber')),
|
||||
'season': metadata.get('SeasonTitle'),
|
||||
'episode_number': int_or_none(metadata.get('EpisodeNumber')),
|
||||
'episode': metadata.get('EpisodeTitle'),
|
||||
}, self._extract_info(metadata.get('AppCode', 'toutv'), video_id))
|
||||
|
@@ -207,7 +207,7 @@ class TVNowNewBaseIE(InfoExtractor):
|
||||
return result
|
||||
|
||||
|
||||
"""
|
||||
r"""
|
||||
TODO: new apigw.tvnow.de based version of TVNowIE. Replace old TVNowIE with it
|
||||
when api.tvnow.de is shut down. This version can't bypass premium checks though.
|
||||
class TVNowIE(TVNowNewBaseIE):
|
||||
|
@@ -2,19 +2,20 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .common import InfoExtractor
|
||||
from ..utils import urlencode_postdata
|
||||
|
||||
import re
|
||||
|
||||
|
||||
class TwitCastingIE(InfoExtractor):
|
||||
_VALID_URL = r'https?://(?:[^/]+\.)?twitcasting\.tv/(?P<uploader_id>[^/]+)/movie/(?P<id>\d+)'
|
||||
_TEST = {
|
||||
_TESTS = [{
|
||||
'url': 'https://twitcasting.tv/ivetesangalo/movie/2357609',
|
||||
'md5': '745243cad58c4681dc752490f7540d7f',
|
||||
'info_dict': {
|
||||
'id': '2357609',
|
||||
'ext': 'mp4',
|
||||
'title': 'Recorded Live #2357609',
|
||||
'title': 'Live #2357609',
|
||||
'uploader_id': 'ivetesangalo',
|
||||
'description': "Moi! I'm live on TwitCasting from my iPhone.",
|
||||
'thumbnail': r're:^https?://.*\.jpg$',
|
||||
@@ -22,14 +23,34 @@ class TwitCastingIE(InfoExtractor):
|
||||
'params': {
|
||||
'skip_download': True,
|
||||
},
|
||||
}
|
||||
}, {
|
||||
'url': 'https://twitcasting.tv/mttbernardini/movie/3689740',
|
||||
'info_dict': {
|
||||
'id': '3689740',
|
||||
'ext': 'mp4',
|
||||
'title': 'Live playing something #3689740',
|
||||
'uploader_id': 'mttbernardini',
|
||||
'description': "I'm live on TwitCasting from my iPad. password: abc (Santa Marinella/Lazio, Italia)",
|
||||
'thumbnail': r're:^https?://.*\.jpg$',
|
||||
},
|
||||
'params': {
|
||||
'skip_download': True,
|
||||
'videopassword': 'abc',
|
||||
},
|
||||
}]
|
||||
|
||||
def _real_extract(self, url):
|
||||
mobj = re.match(self._VALID_URL, url)
|
||||
video_id = mobj.group('id')
|
||||
uploader_id = mobj.group('uploader_id')
|
||||
|
||||
webpage = self._download_webpage(url, video_id)
|
||||
video_password = self._downloader.params.get('videopassword')
|
||||
request_data = None
|
||||
if video_password:
|
||||
request_data = urlencode_postdata({
|
||||
'password': video_password,
|
||||
})
|
||||
webpage = self._download_webpage(url, video_id, data=request_data)
|
||||
|
||||
title = self._html_search_regex(
|
||||
r'(?s)<[^>]+id=["\']movietitle[^>]+>(.+?)</',
|
||||
|
@@ -134,12 +134,12 @@ class TwitchBaseIE(InfoExtractor):
|
||||
def _prefer_source(self, formats):
|
||||
try:
|
||||
source = next(f for f in formats if f['format_id'] == 'Source')
|
||||
source['preference'] = 10
|
||||
source['quality'] = 10
|
||||
except StopIteration:
|
||||
for f in formats:
|
||||
if '/chunked/' in f['url']:
|
||||
f.update({
|
||||
'source_preference': 10,
|
||||
'quality': 10,
|
||||
'format_note': 'Source',
|
||||
})
|
||||
self._sort_formats(formats)
|
||||
|
@@ -76,7 +76,10 @@ class UdemyIE(InfoExtractor):
|
||||
webpage, 'course', default='{}')),
|
||||
video_id, fatal=False) or {}
|
||||
course_id = course.get('id') or self._search_regex(
|
||||
r'data-course-id=["\'](\d+)', webpage, 'course id')
|
||||
[
|
||||
r'data-course-id=["\'](\d+)',
|
||||
r'"courseId"\s*:\s*(\d+)'
|
||||
], webpage, 'course id')
|
||||
return course_id, course.get('title')
|
||||
|
||||
def _enroll_course(self, base_url, webpage, course_id):
|
||||
|
@@ -275,8 +275,8 @@ class VevoIE(VevoBaseIE):
|
||||
|
||||
genres = video_info.get('genres')
|
||||
genre = (
|
||||
genres[0] if genres and isinstance(genres, list) and
|
||||
isinstance(genres[0], compat_str) else None)
|
||||
genres[0] if genres and isinstance(genres, list)
|
||||
and isinstance(genres[0], compat_str) else None)
|
||||
|
||||
is_explicit = video_info.get('isExplicit')
|
||||
if is_explicit is True:
|
||||
|
@@ -109,23 +109,9 @@ class VimeoBaseInfoExtractor(InfoExtractor):
|
||||
|
||||
def _parse_config(self, config, video_id):
|
||||
video_data = config['video']
|
||||
# Extract title
|
||||
video_title = video_data['title']
|
||||
|
||||
# Extract uploader, uploader_url and uploader_id
|
||||
video_uploader = video_data.get('owner', {}).get('name')
|
||||
video_uploader_url = video_data.get('owner', {}).get('url')
|
||||
video_uploader_id = video_uploader_url.split('/')[-1] if video_uploader_url else None
|
||||
|
||||
# Extract video thumbnail
|
||||
video_thumbnail = video_data.get('thumbnail')
|
||||
if video_thumbnail is None:
|
||||
video_thumbs = video_data.get('thumbs')
|
||||
if video_thumbs and isinstance(video_thumbs, dict):
|
||||
_, video_thumbnail = sorted((int(width if width.isdigit() else 0), t_url) for (width, t_url) in video_thumbs.items())[-1]
|
||||
|
||||
# Extract video duration
|
||||
video_duration = int_or_none(video_data.get('duration'))
|
||||
live_event = video_data.get('live_event') or {}
|
||||
is_live = live_event.get('status') == 'started'
|
||||
|
||||
formats = []
|
||||
config_files = video_data.get('files') or config['request'].get('files', {})
|
||||
@@ -142,6 +128,7 @@ class VimeoBaseInfoExtractor(InfoExtractor):
|
||||
'tbr': int_or_none(f.get('bitrate')),
|
||||
})
|
||||
|
||||
# TODO: fix handling of 308 status code returned for live archive manifest requests
|
||||
for files_type in ('hls', 'dash'):
|
||||
for cdn_name, cdn_data in config_files.get(files_type, {}).get('cdns', {}).items():
|
||||
manifest_url = cdn_data.get('url')
|
||||
@@ -151,7 +138,7 @@ class VimeoBaseInfoExtractor(InfoExtractor):
|
||||
if files_type == 'hls':
|
||||
formats.extend(self._extract_m3u8_formats(
|
||||
manifest_url, video_id, 'mp4',
|
||||
'm3u8_native', m3u8_id=format_id,
|
||||
'm3u8' if is_live else 'm3u8_native', m3u8_id=format_id,
|
||||
note='Downloading %s m3u8 information' % cdn_name,
|
||||
fatal=False))
|
||||
elif files_type == 'dash':
|
||||
@@ -164,6 +151,10 @@ class VimeoBaseInfoExtractor(InfoExtractor):
|
||||
else:
|
||||
mpd_manifest_urls = [(format_id, manifest_url)]
|
||||
for f_id, m_url in mpd_manifest_urls:
|
||||
if 'json=1' in m_url:
|
||||
real_m_url = (self._download_json(m_url, video_id, fatal=False) or {}).get('url')
|
||||
if real_m_url:
|
||||
m_url = real_m_url
|
||||
mpd_formats = self._extract_mpd_formats(
|
||||
m_url.replace('/master.json', '/master.mpd'), video_id, f_id,
|
||||
'Downloading %s MPD information' % cdn_name,
|
||||
@@ -175,6 +166,15 @@ class VimeoBaseInfoExtractor(InfoExtractor):
|
||||
f['preference'] = -40
|
||||
formats.extend(mpd_formats)
|
||||
|
||||
live_archive = live_event.get('archive') or {}
|
||||
live_archive_source_url = live_archive.get('source_url')
|
||||
if live_archive_source_url and live_archive.get('status') == 'done':
|
||||
formats.append({
|
||||
'format_id': 'live-archive-source',
|
||||
'url': live_archive_source_url,
|
||||
'preference': 1,
|
||||
})
|
||||
|
||||
subtitles = {}
|
||||
text_tracks = config['request'].get('text_tracks')
|
||||
if text_tracks:
|
||||
@@ -184,15 +184,33 @@ class VimeoBaseInfoExtractor(InfoExtractor):
|
||||
'url': 'https://vimeo.com' + tt['url'],
|
||||
}]
|
||||
|
||||
thumbnails = []
|
||||
if not is_live:
|
||||
for key, thumb in video_data.get('thumbs', {}).items():
|
||||
thumbnails.append({
|
||||
'id': key,
|
||||
'width': int_or_none(key),
|
||||
'url': thumb,
|
||||
})
|
||||
thumbnail = video_data.get('thumbnail')
|
||||
if thumbnail:
|
||||
thumbnails.append({
|
||||
'url': thumbnail,
|
||||
})
|
||||
|
||||
owner = video_data.get('owner') or {}
|
||||
video_uploader_url = owner.get('url')
|
||||
|
||||
return {
|
||||
'title': video_title,
|
||||
'uploader': video_uploader,
|
||||
'uploader_id': video_uploader_id,
|
||||
'title': self._live_title(video_title) if is_live else video_title,
|
||||
'uploader': owner.get('name'),
|
||||
'uploader_id': video_uploader_url.split('/')[-1] if video_uploader_url else None,
|
||||
'uploader_url': video_uploader_url,
|
||||
'thumbnail': video_thumbnail,
|
||||
'duration': video_duration,
|
||||
'thumbnails': thumbnails,
|
||||
'duration': int_or_none(video_data.get('duration')),
|
||||
'formats': formats,
|
||||
'subtitles': subtitles,
|
||||
'is_live': is_live,
|
||||
}
|
||||
|
||||
def _extract_original_format(self, url, video_id):
|
||||
|
@@ -443,8 +443,8 @@ class VKIE(VKBaseIE):
|
||||
format_url = url_or_none(format_url)
|
||||
if not format_url or not format_url.startswith(('http', '//', 'rtmp')):
|
||||
continue
|
||||
if (format_id.startswith(('url', 'cache')) or
|
||||
format_id in ('extra_data', 'live_mp4', 'postlive_mp4')):
|
||||
if (format_id.startswith(('url', 'cache'))
|
||||
or format_id in ('extra_data', 'live_mp4', 'postlive_mp4')):
|
||||
height = int_or_none(self._search_regex(
|
||||
r'^(?:url|cache)(\d+)', format_id, 'height', default=None))
|
||||
formats.append({
|
||||
|
@@ -102,6 +102,15 @@ class VRVIE(VRVBaseIE):
|
||||
# m3u8 download
|
||||
'skip_download': True,
|
||||
},
|
||||
}, {
|
||||
# movie listing
|
||||
'url': 'https://vrv.co/watch/G6NQXZ1J6/Lily-CAT',
|
||||
'info_dict': {
|
||||
'id': 'G6NQXZ1J6',
|
||||
'title': 'Lily C.A.T',
|
||||
'description': 'md5:988b031e7809a6aeb60968be4af7db07',
|
||||
},
|
||||
'playlist_count': 2,
|
||||
}]
|
||||
_NETRC_MACHINE = 'vrv'
|
||||
|
||||
@@ -123,23 +132,23 @@ class VRVIE(VRVBaseIE):
|
||||
def _extract_vrv_formats(self, url, video_id, stream_format, audio_lang, hardsub_lang):
|
||||
if not url or stream_format not in ('hls', 'dash'):
|
||||
return []
|
||||
assert audio_lang or hardsub_lang
|
||||
stream_id_list = []
|
||||
if audio_lang:
|
||||
stream_id_list.append('audio-%s' % audio_lang)
|
||||
if hardsub_lang:
|
||||
stream_id_list.append('hardsub-%s' % hardsub_lang)
|
||||
stream_id = '-'.join(stream_id_list)
|
||||
format_id = '%s-%s' % (stream_format, stream_id)
|
||||
format_id = stream_format
|
||||
if stream_id_list:
|
||||
format_id += '-' + '-'.join(stream_id_list)
|
||||
if stream_format == 'hls':
|
||||
adaptive_formats = self._extract_m3u8_formats(
|
||||
url, video_id, 'mp4', m3u8_id=format_id,
|
||||
note='Downloading %s m3u8 information' % stream_id,
|
||||
note='Downloading %s information' % format_id,
|
||||
fatal=False)
|
||||
elif stream_format == 'dash':
|
||||
adaptive_formats = self._extract_mpd_formats(
|
||||
url, video_id, mpd_id=format_id,
|
||||
note='Downloading %s MPD information' % stream_id,
|
||||
note='Downloading %s information' % format_id,
|
||||
fatal=False)
|
||||
if audio_lang:
|
||||
for f in adaptive_formats:
|
||||
@@ -155,6 +164,23 @@ class VRVIE(VRVBaseIE):
|
||||
resource_path = object_data['__links__']['resource']['href']
|
||||
video_data = self._call_cms(resource_path, video_id, 'video')
|
||||
title = video_data['title']
|
||||
description = video_data.get('description')
|
||||
|
||||
if video_data.get('__class__') == 'movie_listing':
|
||||
items = self._call_cms(
|
||||
video_data['__links__']['movie_listing/movies']['href'],
|
||||
video_id, 'movie listing').get('items') or []
|
||||
if len(items) != 1:
|
||||
entries = []
|
||||
for item in items:
|
||||
item_id = item.get('id')
|
||||
if not item_id:
|
||||
continue
|
||||
entries.append(self.url_result(
|
||||
'https://vrv.co/watch/' + item_id,
|
||||
self.ie_key(), item_id, item.get('title')))
|
||||
return self.playlist_result(entries, video_id, title, description)
|
||||
video_data = items[0]
|
||||
|
||||
streams_path = video_data['__links__'].get('streams', {}).get('href')
|
||||
if not streams_path:
|
||||
@@ -198,7 +224,7 @@ class VRVIE(VRVBaseIE):
|
||||
'formats': formats,
|
||||
'subtitles': subtitles,
|
||||
'thumbnails': thumbnails,
|
||||
'description': video_data.get('description'),
|
||||
'description': description,
|
||||
'duration': float_or_none(video_data.get('duration_ms'), 1000),
|
||||
'uploader_id': video_data.get('channel_id'),
|
||||
'series': video_data.get('series_title'),
|
||||
|
@@ -1,158 +0,0 @@
|
||||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
|
||||
from .common import InfoExtractor
|
||||
from ..utils import (
|
||||
ExtractorError,
|
||||
int_or_none,
|
||||
qualities,
|
||||
remove_start,
|
||||
)
|
||||
|
||||
|
||||
class WrzutaIE(InfoExtractor):
|
||||
IE_NAME = 'wrzuta.pl'
|
||||
|
||||
_VALID_URL = r'https?://(?P<uploader>[0-9a-zA-Z]+)\.wrzuta\.pl/(?P<typ>film|audio)/(?P<id>[0-9a-zA-Z]+)'
|
||||
|
||||
_TESTS = [{
|
||||
'url': 'http://laboratoriumdextera.wrzuta.pl/film/aq4hIZWrkBu/nike_football_the_last_game',
|
||||
'md5': '9e67e05bed7c03b82488d87233a9efe7',
|
||||
'info_dict': {
|
||||
'id': 'aq4hIZWrkBu',
|
||||
'ext': 'mp4',
|
||||
'title': 'Nike Football: The Last Game',
|
||||
'duration': 307,
|
||||
'uploader_id': 'laboratoriumdextera',
|
||||
'description': 'md5:7fb5ef3c21c5893375fda51d9b15d9cd',
|
||||
},
|
||||
'skip': 'Redirected to wrzuta.pl',
|
||||
}, {
|
||||
'url': 'http://vexling.wrzuta.pl/audio/01xBFabGXu6/james_horner_-_into_the_na_39_vi_world_bonus',
|
||||
'md5': 'f80564fb5a2ec6ec59705ae2bf2ba56d',
|
||||
'info_dict': {
|
||||
'id': '01xBFabGXu6',
|
||||
'ext': 'mp3',
|
||||
'title': 'James Horner - Into The Na\'vi World [Bonus]',
|
||||
'description': 'md5:30a70718b2cd9df3120fce4445b0263b',
|
||||
'duration': 95,
|
||||
'uploader_id': 'vexling',
|
||||
},
|
||||
}]
|
||||
|
||||
def _real_extract(self, url):
|
||||
mobj = re.match(self._VALID_URL, url)
|
||||
video_id = mobj.group('id')
|
||||
typ = mobj.group('typ')
|
||||
uploader = mobj.group('uploader')
|
||||
|
||||
webpage, urlh = self._download_webpage_handle(url, video_id)
|
||||
|
||||
if urlh.geturl() == 'http://www.wrzuta.pl/':
|
||||
raise ExtractorError('Video removed', expected=True)
|
||||
|
||||
quality = qualities(['SD', 'MQ', 'HQ', 'HD'])
|
||||
|
||||
audio_table = {'flv': 'mp3', 'webm': 'ogg', '???': 'mp3'}
|
||||
|
||||
embedpage = self._download_json('http://www.wrzuta.pl/npp/embed/%s/%s' % (uploader, video_id), video_id)
|
||||
|
||||
formats = []
|
||||
for media in embedpage['url']:
|
||||
fmt = media['type'].split('@')[0]
|
||||
if typ == 'audio':
|
||||
ext = audio_table.get(fmt, fmt)
|
||||
else:
|
||||
ext = fmt
|
||||
|
||||
formats.append({
|
||||
'format_id': '%s_%s' % (ext, media['quality'].lower()),
|
||||
'url': media['url'],
|
||||
'ext': ext,
|
||||
'quality': quality(media['quality']),
|
||||
})
|
||||
|
||||
self._sort_formats(formats)
|
||||
|
||||
return {
|
||||
'id': video_id,
|
||||
'title': self._og_search_title(webpage),
|
||||
'thumbnail': self._og_search_thumbnail(webpage),
|
||||
'formats': formats,
|
||||
'duration': int_or_none(embedpage['duration']),
|
||||
'uploader_id': uploader,
|
||||
'description': self._og_search_description(webpage),
|
||||
'age_limit': embedpage.get('minimalAge', 0),
|
||||
}
|
||||
|
||||
|
||||
class WrzutaPlaylistIE(InfoExtractor):
|
||||
"""
|
||||
this class covers extraction of wrzuta playlist entries
|
||||
the extraction process bases on following steps:
|
||||
* collect information of playlist size
|
||||
* download all entries provided on
|
||||
the playlist webpage (the playlist is split
|
||||
on two pages: first directly reached from webpage
|
||||
second: downloaded on demand by ajax call and rendered
|
||||
using the ajax call response)
|
||||
* in case size of extracted entries not reached total number of entries
|
||||
use the ajax call to collect the remaining entries
|
||||
"""
|
||||
|
||||
IE_NAME = 'wrzuta.pl:playlist'
|
||||
_VALID_URL = r'https?://(?P<uploader>[0-9a-zA-Z]+)\.wrzuta\.pl/playlista/(?P<id>[0-9a-zA-Z]+)'
|
||||
_TESTS = [{
|
||||
'url': 'http://miromak71.wrzuta.pl/playlista/7XfO4vE84iR/moja_muza',
|
||||
'playlist_mincount': 14,
|
||||
'info_dict': {
|
||||
'id': '7XfO4vE84iR',
|
||||
'title': 'Moja muza',
|
||||
},
|
||||
}, {
|
||||
'url': 'http://heroesf70.wrzuta.pl/playlista/6Nj3wQHx756/lipiec_-_lato_2015_muzyka_swiata',
|
||||
'playlist_mincount': 144,
|
||||
'info_dict': {
|
||||
'id': '6Nj3wQHx756',
|
||||
'title': 'Lipiec - Lato 2015 Muzyka Świata',
|
||||
},
|
||||
}, {
|
||||
'url': 'http://miromak71.wrzuta.pl/playlista/7XfO4vE84iR',
|
||||
'only_matching': True,
|
||||
}]
|
||||
|
||||
def _real_extract(self, url):
|
||||
mobj = re.match(self._VALID_URL, url)
|
||||
playlist_id = mobj.group('id')
|
||||
uploader = mobj.group('uploader')
|
||||
|
||||
webpage = self._download_webpage(url, playlist_id)
|
||||
|
||||
playlist_size = int_or_none(self._html_search_regex(
|
||||
(r'<div[^>]+class=["\']playlist-counter["\'][^>]*>\d+/(\d+)',
|
||||
r'<div[^>]+class=["\']all-counter["\'][^>]*>(.+?)</div>'),
|
||||
webpage, 'playlist size', default=None))
|
||||
|
||||
playlist_title = remove_start(
|
||||
self._og_search_title(webpage), 'Playlista: ')
|
||||
|
||||
entries = []
|
||||
if playlist_size:
|
||||
entries = [
|
||||
self.url_result(entry_url)
|
||||
for _, entry_url in re.findall(
|
||||
r'<a[^>]+href=(["\'])(http.+?)\1[^>]+class=["\']playlist-file-page',
|
||||
webpage)]
|
||||
if playlist_size > len(entries):
|
||||
playlist_content = self._download_json(
|
||||
'http://%s.wrzuta.pl/xhr/get_playlist_offset/%s' % (uploader, playlist_id),
|
||||
playlist_id,
|
||||
'Downloading playlist JSON',
|
||||
'Unable to download playlist JSON')
|
||||
entries.extend([
|
||||
self.url_result(entry['filelink'])
|
||||
for entry in playlist_content.get('files', []) if entry.get('filelink')])
|
||||
|
||||
return self.playlist_result(entries, playlist_id, playlist_title)
|
@@ -526,7 +526,7 @@ class YahooGyaOPlayerIE(InfoExtractor):
|
||||
|
||||
class YahooGyaOIE(InfoExtractor):
|
||||
IE_NAME = 'yahoo:gyao'
|
||||
_VALID_URL = r'https?://(?:gyao\.yahoo\.co\.jp/p|streaming\.yahoo\.co\.jp/p/y)/(?P<id>\d+/v\d+)'
|
||||
_VALID_URL = r'https?://(?:gyao\.yahoo\.co\.jp/(?:p|title/[^/]+)|streaming\.yahoo\.co\.jp/p/y)/(?P<id>\d+/v\d+|[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12})'
|
||||
_TESTS = [{
|
||||
'url': 'https://gyao.yahoo.co.jp/p/00449/v03102/',
|
||||
'info_dict': {
|
||||
@@ -536,6 +536,9 @@ class YahooGyaOIE(InfoExtractor):
|
||||
}, {
|
||||
'url': 'https://streaming.yahoo.co.jp/p/y/01034/v00133/',
|
||||
'only_matching': True,
|
||||
}, {
|
||||
'url': 'https://gyao.yahoo.co.jp/title/%E3%81%97%E3%82%83%E3%81%B9%E3%81%8F%E3%82%8A007/5b025a49-b2e5-4dc7-945c-09c6634afacf',
|
||||
'only_matching': True,
|
||||
}]
|
||||
|
||||
def _real_extract(self, url):
|
||||
|
@@ -69,25 +69,28 @@ class YandexMusicTrackIE(YandexMusicBaseIE):
|
||||
'skip': 'Travis CI servers blocked by YandexMusic',
|
||||
}
|
||||
|
||||
def _get_track_url(self, storage_dir, track_id):
|
||||
data = self._download_json(
|
||||
'http://music.yandex.ru/api/v1.5/handlers/api-jsonp.jsx?action=getTrackSrc&p=download-info/%s'
|
||||
% storage_dir,
|
||||
track_id, 'Downloading track location JSON')
|
||||
def _real_extract(self, url):
|
||||
mobj = re.match(self._VALID_URL, url)
|
||||
album_id, track_id = mobj.group('album_id'), mobj.group('id')
|
||||
|
||||
# Each string is now wrapped in a list, this is probably only temporarily thus
|
||||
# supporting both scenarios (see https://github.com/ytdl-org/youtube-dl/issues/10193)
|
||||
for k, v in data.items():
|
||||
if v and isinstance(v, list):
|
||||
data[k] = v[0]
|
||||
track = self._download_json(
|
||||
'http://music.yandex.ru/handlers/track.jsx?track=%s:%s' % (track_id, album_id),
|
||||
track_id, 'Downloading track JSON')['track']
|
||||
track_title = track['title']
|
||||
|
||||
key = hashlib.md5(('XGRlBW9FXlekgbPrRHuSiA' + data['path'][1:] + data['s']).encode('utf-8')).hexdigest()
|
||||
storage = storage_dir.split('.')
|
||||
download_data = self._download_json(
|
||||
'https://music.yandex.ru/api/v2.1/handlers/track/%s:%s/web-album_track-track-track-main/download/m' % (track_id, album_id),
|
||||
track_id, 'Downloading track location url JSON',
|
||||
headers={'X-Retpath-Y': url})
|
||||
|
||||
return ('http://%s/get-mp3/%s/%s?track-id=%s&from=service-10-track&similarities-experiment=default'
|
||||
% (data['host'], key, data['ts'] + data['path'], storage[1]))
|
||||
fd_data = self._download_json(
|
||||
download_data['src'], track_id,
|
||||
'Downloading track location JSON',
|
||||
query={'format': 'json'})
|
||||
key = hashlib.md5(('XGRlBW9FXlekgbPrRHuSiA' + fd_data['path'][1:] + fd_data['s']).encode('utf-8')).hexdigest()
|
||||
storage = track['storageDir'].split('.')
|
||||
f_url = 'http://%s/get-mp3/%s/%s?track-id=%s ' % (fd_data['host'], key, fd_data['ts'] + fd_data['path'], storage[1])
|
||||
|
||||
def _get_track_info(self, track):
|
||||
thumbnail = None
|
||||
cover_uri = track.get('albums', [{}])[0].get('coverUri')
|
||||
if cover_uri:
|
||||
@@ -95,15 +98,16 @@ class YandexMusicTrackIE(YandexMusicBaseIE):
|
||||
if not thumbnail.startswith('http'):
|
||||
thumbnail = 'http://' + thumbnail
|
||||
|
||||
track_title = track['title']
|
||||
track_info = {
|
||||
'id': track['id'],
|
||||
'id': track_id,
|
||||
'ext': 'mp3',
|
||||
'url': self._get_track_url(track['storageDir'], track['id']),
|
||||
'url': f_url,
|
||||
'filesize': int_or_none(track.get('fileSize')),
|
||||
'duration': float_or_none(track.get('durationMs'), 1000),
|
||||
'thumbnail': thumbnail,
|
||||
'track': track_title,
|
||||
'acodec': download_data.get('codec'),
|
||||
'abr': int_or_none(download_data.get('bitrate')),
|
||||
}
|
||||
|
||||
def extract_artist(artist_list):
|
||||
@@ -131,18 +135,9 @@ class YandexMusicTrackIE(YandexMusicBaseIE):
|
||||
})
|
||||
else:
|
||||
track_info['title'] = track_title
|
||||
|
||||
return track_info
|
||||
|
||||
def _real_extract(self, url):
|
||||
mobj = re.match(self._VALID_URL, url)
|
||||
album_id, track_id = mobj.group('album_id'), mobj.group('id')
|
||||
|
||||
track = self._download_json(
|
||||
'http://music.yandex.ru/handlers/track.jsx?track=%s:%s' % (track_id, album_id),
|
||||
track_id, 'Downloading track JSON')['track']
|
||||
|
||||
return self._get_track_info(track)
|
||||
|
||||
|
||||
class YandexMusicPlaylistBaseIE(YandexMusicBaseIE):
|
||||
def _build_playlist(self, tracks):
|
||||
|
@@ -70,9 +70,9 @@ class YandexVideoIE(InfoExtractor):
|
||||
|
||||
description = content.get('description')
|
||||
thumbnail = content.get('thumbnail')
|
||||
timestamp = (int_or_none(content.get('release_date')) or
|
||||
int_or_none(content.get('release_date_ut')) or
|
||||
int_or_none(content.get('start_time')))
|
||||
timestamp = (int_or_none(content.get('release_date'))
|
||||
or int_or_none(content.get('release_date_ut'))
|
||||
or int_or_none(content.get('start_time')))
|
||||
duration = int_or_none(content.get('duration'))
|
||||
series = content.get('program_title')
|
||||
age_limit = int_or_none(content.get('restriction_age'))
|
||||
|
@@ -258,8 +258,8 @@ class YoukuShowIE(InfoExtractor):
|
||||
transform_source=lambda s: js_to_json(strip_jsonp(s))).get('html')
|
||||
if playlist_data is None:
|
||||
return [None, None]
|
||||
drama_list = (get_element_by_class('p-drama-grid', playlist_data) or
|
||||
get_element_by_class('p-drama-half-row', playlist_data))
|
||||
drama_list = (get_element_by_class('p-drama-grid', playlist_data)
|
||||
or get_element_by_class('p-drama-half-row', playlist_data))
|
||||
if drama_list is None:
|
||||
raise ExtractorError('No episodes found')
|
||||
video_urls = re.findall(r'<a[^>]+href="([^"]+)"', drama_list)
|
||||
|
@@ -16,6 +16,7 @@ from ..jsinterp import JSInterpreter
|
||||
from ..swfinterp import SWFInterpreter
|
||||
from ..compat import (
|
||||
compat_chr,
|
||||
compat_HTTPError,
|
||||
compat_kwargs,
|
||||
compat_parse_qs,
|
||||
compat_urllib_parse_unquote,
|
||||
@@ -27,6 +28,7 @@ from ..compat import (
|
||||
)
|
||||
from ..utils import (
|
||||
clean_html,
|
||||
dict_get,
|
||||
error_to_compat_str,
|
||||
ExtractorError,
|
||||
float_or_none,
|
||||
@@ -287,10 +289,25 @@ class YoutubeEntryListBaseInfoExtractor(YoutubeBaseInfoExtractor):
|
||||
if not mobj:
|
||||
break
|
||||
|
||||
more = self._download_json(
|
||||
'https://youtube.com/%s' % mobj.group('more'), playlist_id,
|
||||
'Downloading page #%s' % page_num,
|
||||
transform_source=uppercase_escape)
|
||||
count = 0
|
||||
retries = 3
|
||||
while count <= retries:
|
||||
try:
|
||||
# Downloading page may result in intermittent 5xx HTTP error
|
||||
# that is usually worked around with a retry
|
||||
more = self._download_json(
|
||||
'https://youtube.com/%s' % mobj.group('more'), playlist_id,
|
||||
'Downloading page #%s%s'
|
||||
% (page_num, ' (retry #%d)' % count if count else ''),
|
||||
transform_source=uppercase_escape)
|
||||
break
|
||||
except ExtractorError as e:
|
||||
if isinstance(e.cause, compat_HTTPError) and e.cause.code in (500, 503):
|
||||
count += 1
|
||||
if count <= retries:
|
||||
continue
|
||||
raise
|
||||
|
||||
content_html = more['content_html']
|
||||
if not content_html.strip():
|
||||
# Some webpages show a "Load more" button but they don't
|
||||
@@ -908,6 +925,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
'creator': 'Todd Haberman, Daniel Law Heath and Aaron Kaplan',
|
||||
'track': 'Dark Walk - Position Music',
|
||||
'artist': 'Todd Haberman, Daniel Law Heath and Aaron Kaplan',
|
||||
'album': 'Position Music - Production Music Vol. 143 - Dark Walk',
|
||||
},
|
||||
'params': {
|
||||
'skip_download': True,
|
||||
@@ -1086,7 +1104,95 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
'skip_download': True,
|
||||
'youtube_include_dash_manifest': False,
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
# Youtube Music Auto-generated description
|
||||
'url': 'https://music.youtube.com/watch?v=MgNrAu2pzNs',
|
||||
'info_dict': {
|
||||
'id': 'MgNrAu2pzNs',
|
||||
'ext': 'mp4',
|
||||
'title': 'Voyeur Girl',
|
||||
'description': 'md5:7ae382a65843d6df2685993e90a8628f',
|
||||
'upload_date': '20190312',
|
||||
'uploader': 'Various Artists - Topic',
|
||||
'uploader_id': 'UCVWKBi1ELZn0QX2CBLSkiyw',
|
||||
'artist': 'Stephen',
|
||||
'track': 'Voyeur Girl',
|
||||
'album': 'it\'s too much love to know my dear',
|
||||
'release_date': '20190313',
|
||||
'release_year': 2019,
|
||||
},
|
||||
'params': {
|
||||
'skip_download': True,
|
||||
},
|
||||
},
|
||||
{
|
||||
# Youtube Music Auto-generated description
|
||||
# Retrieve 'artist' field from 'Artist:' in video description
|
||||
# when it is present on youtube music video
|
||||
'url': 'https://www.youtube.com/watch?v=k0jLE7tTwjY',
|
||||
'info_dict': {
|
||||
'id': 'k0jLE7tTwjY',
|
||||
'ext': 'mp4',
|
||||
'title': 'Latch Feat. Sam Smith',
|
||||
'description': 'md5:3cb1e8101a7c85fcba9b4fb41b951335',
|
||||
'upload_date': '20150110',
|
||||
'uploader': 'Various Artists - Topic',
|
||||
'uploader_id': 'UCNkEcmYdjrH4RqtNgh7BZ9w',
|
||||
'artist': 'Disclosure',
|
||||
'track': 'Latch Feat. Sam Smith',
|
||||
'album': 'Latch Featuring Sam Smith',
|
||||
'release_date': '20121008',
|
||||
'release_year': 2012,
|
||||
},
|
||||
'params': {
|
||||
'skip_download': True,
|
||||
},
|
||||
},
|
||||
{
|
||||
# Youtube Music Auto-generated description
|
||||
# handle multiple artists on youtube music video
|
||||
'url': 'https://www.youtube.com/watch?v=74qn0eJSjpA',
|
||||
'info_dict': {
|
||||
'id': '74qn0eJSjpA',
|
||||
'ext': 'mp4',
|
||||
'title': 'Eastside',
|
||||
'description': 'md5:290516bb73dcbfab0dcc4efe6c3de5f2',
|
||||
'upload_date': '20180710',
|
||||
'uploader': 'Benny Blanco - Topic',
|
||||
'uploader_id': 'UCzqz_ksRu_WkIzmivMdIS7A',
|
||||
'artist': 'benny blanco, Halsey, Khalid',
|
||||
'track': 'Eastside',
|
||||
'album': 'Eastside',
|
||||
'release_date': '20180713',
|
||||
'release_year': 2018,
|
||||
},
|
||||
'params': {
|
||||
'skip_download': True,
|
||||
},
|
||||
},
|
||||
{
|
||||
# Youtube Music Auto-generated description
|
||||
# handle youtube music video with release_year and no release_date
|
||||
'url': 'https://www.youtube.com/watch?v=-hcAI0g-f5M',
|
||||
'info_dict': {
|
||||
'id': '-hcAI0g-f5M',
|
||||
'ext': 'mp4',
|
||||
'title': 'Put It On Me',
|
||||
'description': 'md5:93c55acc682ae7b0c668f2e34e1c069e',
|
||||
'upload_date': '20180426',
|
||||
'uploader': 'Matt Maeson - Topic',
|
||||
'uploader_id': 'UCnEkIGqtGcQMLk73Kp-Q5LQ',
|
||||
'artist': 'Matt Maeson',
|
||||
'track': 'Put It On Me',
|
||||
'album': 'The Hearse',
|
||||
'release_date': None,
|
||||
'release_year': 2018,
|
||||
},
|
||||
'params': {
|
||||
'skip_download': True,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -1563,6 +1669,9 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
def extract_view_count(v_info):
|
||||
return int_or_none(try_get(v_info, lambda x: x['view_count'][0]))
|
||||
|
||||
def extract_token(v_info):
|
||||
return dict_get(v_info, ('account_playback_token', 'accountPlaybackToken', 'token'))
|
||||
|
||||
player_response = {}
|
||||
|
||||
# Get video info
|
||||
@@ -1622,7 +1731,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
# The general idea is to take a union of itags of both DASH manifests (for example
|
||||
# video with such 'manifest behavior' see https://github.com/ytdl-org/youtube-dl/issues/6093)
|
||||
self.report_video_info_webpage_download(video_id)
|
||||
for el in ('info', 'embedded', 'detailpage', 'vevo', ''):
|
||||
for el in ('embedded', 'detailpage', 'vevo', ''):
|
||||
query = {
|
||||
'video_id': video_id,
|
||||
'ps': 'default',
|
||||
@@ -1652,7 +1761,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
view_count = extract_view_count(get_video_info)
|
||||
if not video_info:
|
||||
video_info = get_video_info
|
||||
if 'token' in get_video_info:
|
||||
get_token = extract_token(get_video_info)
|
||||
if get_token:
|
||||
# Different get_video_info requests may report different results, e.g.
|
||||
# some may report video unavailability, but some may serve it without
|
||||
# any complaint (see https://github.com/ytdl-org/youtube-dl/issues/7362,
|
||||
@@ -1662,7 +1772,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
# due to YouTube measures against IP ranges of hosting providers.
|
||||
# Working around by preferring the first succeeded video_info containing
|
||||
# the token if no such video_info yet was found.
|
||||
if 'token' not in video_info:
|
||||
token = extract_token(video_info)
|
||||
if not token:
|
||||
video_info = get_video_info
|
||||
break
|
||||
|
||||
@@ -1671,26 +1782,12 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
r'(?s)<h1[^>]+id="unavailable-message"[^>]*>(.+?)</h1>',
|
||||
video_webpage, 'unavailable message', default=None)
|
||||
|
||||
if 'token' not in video_info:
|
||||
if 'reason' in video_info:
|
||||
if 'The uploader has not made this video available in your country.' in video_info['reason']:
|
||||
regions_allowed = self._html_search_meta(
|
||||
'regionsAllowed', video_webpage, default=None)
|
||||
countries = regions_allowed.split(',') if regions_allowed else None
|
||||
self.raise_geo_restricted(
|
||||
msg=video_info['reason'][0], countries=countries)
|
||||
reason = video_info['reason'][0]
|
||||
if 'Invalid parameters' in reason:
|
||||
unavailable_message = extract_unavailable_message()
|
||||
if unavailable_message:
|
||||
reason = unavailable_message
|
||||
raise ExtractorError(
|
||||
'YouTube said: %s' % reason,
|
||||
expected=True, video_id=video_id)
|
||||
else:
|
||||
raise ExtractorError(
|
||||
'"token" parameter not in video info for unknown reason',
|
||||
video_id=video_id)
|
||||
if not video_info:
|
||||
unavailable_message = extract_unavailable_message()
|
||||
if not unavailable_message:
|
||||
unavailable_message = 'Unable to extract video data'
|
||||
raise ExtractorError(
|
||||
'YouTube said: %s' % unavailable_message, expected=True, video_id=video_id)
|
||||
|
||||
if video_info.get('license_info'):
|
||||
raise ExtractorError('This video is DRM protected.', expected=True)
|
||||
@@ -1890,7 +1987,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
|
||||
signature = self._decrypt_signature(
|
||||
encrypted_sig, video_id, player_url, age_gate)
|
||||
url += '&signature=' + signature
|
||||
sp = try_get(url_data, lambda x: x['sp'][0], compat_str) or 'signature'
|
||||
url += '&%s=%s' % (sp, signature)
|
||||
if 'ratebypass' not in url:
|
||||
url += '&ratebypass=yes'
|
||||
|
||||
@@ -1954,8 +2052,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
url_or_none(try_get(
|
||||
player_response,
|
||||
lambda x: x['streamingData']['hlsManifestUrl'],
|
||||
compat_str)) or
|
||||
url_or_none(try_get(
|
||||
compat_str))
|
||||
or url_or_none(try_get(
|
||||
video_info, lambda x: x['hlsvp'][0], compat_str)))
|
||||
if manifest_url:
|
||||
formats = []
|
||||
@@ -2003,8 +2101,13 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
else:
|
||||
self._downloader.report_warning('unable to extract uploader nickname')
|
||||
|
||||
channel_id = self._html_search_meta(
|
||||
'channelId', video_webpage, 'channel id')
|
||||
channel_id = (
|
||||
str_or_none(video_details.get('channelId'))
|
||||
or self._html_search_meta(
|
||||
'channelId', video_webpage, 'channel id', default=None)
|
||||
or self._search_regex(
|
||||
r'data-channel-external-id=(["\'])(?P<id>(?:(?!\1).)+)\1',
|
||||
video_webpage, 'channel id', default=None, group='id'))
|
||||
channel_url = 'http://www.youtube.com/channel/%s' % channel_id if channel_id else None
|
||||
|
||||
# thumbnail image
|
||||
@@ -2063,6 +2166,27 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
|
||||
track = extract_meta('Song')
|
||||
artist = extract_meta('Artist')
|
||||
album = extract_meta('Album')
|
||||
|
||||
# Youtube Music Auto-generated description
|
||||
release_date = release_year = None
|
||||
if video_description:
|
||||
mobj = re.search(r'(?s)Provided to YouTube by [^\n]+\n+(?P<track>[^·]+)·(?P<artist>[^\n]+)\n+(?P<album>[^\n]+)(?:.+?℗\s*(?P<release_year>\d{4})(?!\d))?(?:.+?Released on\s*:\s*(?P<release_date>\d{4}-\d{2}-\d{2}))?(.+?\nArtist\s*:\s*(?P<clean_artist>[^\n]+))?', video_description)
|
||||
if mobj:
|
||||
if not track:
|
||||
track = mobj.group('track').strip()
|
||||
if not artist:
|
||||
artist = mobj.group('clean_artist') or ', '.join(a.strip() for a in mobj.group('artist').split('·'))
|
||||
if not album:
|
||||
album = mobj.group('album'.strip())
|
||||
release_year = mobj.group('release_year')
|
||||
release_date = mobj.group('release_date')
|
||||
if release_date:
|
||||
release_date = release_date.replace('-', '')
|
||||
if not release_year:
|
||||
release_year = int(release_date[:4])
|
||||
if release_year:
|
||||
release_year = int(release_year)
|
||||
|
||||
m_episode = re.search(
|
||||
r'<div[^>]+id="watch7-headline"[^>]*>\s*<span[^>]*>.*?>(?P<series>[^<]+)</a></b>\s*S(?P<season>\d+)\s*•\s*E(?P<episode>\d+)</span>',
|
||||
@@ -2176,6 +2300,29 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
if f.get('vcodec') != 'none':
|
||||
f['stretched_ratio'] = ratio
|
||||
|
||||
if not formats:
|
||||
token = extract_token(video_info)
|
||||
if not token:
|
||||
if 'reason' in video_info:
|
||||
if 'The uploader has not made this video available in your country.' in video_info['reason']:
|
||||
regions_allowed = self._html_search_meta(
|
||||
'regionsAllowed', video_webpage, default=None)
|
||||
countries = regions_allowed.split(',') if regions_allowed else None
|
||||
self.raise_geo_restricted(
|
||||
msg=video_info['reason'][0], countries=countries)
|
||||
reason = video_info['reason'][0]
|
||||
if 'Invalid parameters' in reason:
|
||||
unavailable_message = extract_unavailable_message()
|
||||
if unavailable_message:
|
||||
reason = unavailable_message
|
||||
raise ExtractorError(
|
||||
'YouTube said: %s' % reason,
|
||||
expected=True, video_id=video_id)
|
||||
else:
|
||||
raise ExtractorError(
|
||||
'"token" parameter not in video info for unknown reason',
|
||||
video_id=video_id)
|
||||
|
||||
self._sort_formats(formats)
|
||||
|
||||
self.mark_watched(video_id, video_info, player_response)
|
||||
@@ -2216,6 +2363,9 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
'episode_number': episode_number,
|
||||
'track': track,
|
||||
'artist': artist,
|
||||
'album': album,
|
||||
'release_date': release_date,
|
||||
'release_year': release_year,
|
||||
}
|
||||
|
||||
|
||||
@@ -2414,9 +2564,9 @@ class YoutubePlaylistIE(YoutubePlaylistBaseInfoExtractor):
|
||||
|
||||
search_title = lambda class_name: get_element_by_attribute('class', class_name, webpage)
|
||||
title_span = (
|
||||
search_title('playlist-title') or
|
||||
search_title('title long-title') or
|
||||
search_title('title'))
|
||||
search_title('playlist-title')
|
||||
or search_title('title long-title')
|
||||
or search_title('title'))
|
||||
title = clean_html(title_span)
|
||||
|
||||
return self.playlist_result(url_results, playlist_id, title)
|
||||
|
@@ -86,8 +86,8 @@ class ZattooPlatformBaseIE(InfoExtractor):
|
||||
return next(
|
||||
chan['cid'] for chan in channel_list
|
||||
if chan.get('cid') and (
|
||||
chan.get('display_alias') == channel_name or
|
||||
chan.get('cid') == channel_name))
|
||||
chan.get('display_alias') == channel_name
|
||||
or chan.get('cid') == channel_name))
|
||||
except StopIteration:
|
||||
raise ExtractorError('Could not extract channel id')
|
||||
|
||||
|
@@ -221,9 +221,9 @@ class FFmpegPostProcessor(PostProcessor):
|
||||
# avconv does not have repeat option
|
||||
if self.basename == 'ffmpeg':
|
||||
cmd += [encodeArgument('-loglevel'), encodeArgument('repeat+info')]
|
||||
cmd += (files_cmd +
|
||||
[encodeArgument(o) for o in opts] +
|
||||
[encodeFilename(self._ffmpeg_filename_argument(out_path), True)])
|
||||
cmd += (files_cmd
|
||||
+ [encodeArgument(o) for o in opts]
|
||||
+ [encodeFilename(self._ffmpeg_filename_argument(out_path), True)])
|
||||
|
||||
if self._downloader.params.get('verbose', False):
|
||||
self._downloader.to_screen('[debug] ffmpeg command line: %s' % shell_quote(cmd))
|
||||
@@ -326,8 +326,8 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):
|
||||
information['ext'] = extension
|
||||
|
||||
# If we download foo.mp3 and convert it to... foo.mp3, then don't delete foo.mp3, silly.
|
||||
if (new_path == path or
|
||||
(self._nopostoverwrites and os.path.exists(encodeFilename(new_path)))):
|
||||
if (new_path == path
|
||||
or (self._nopostoverwrites and os.path.exists(encodeFilename(new_path)))):
|
||||
self._downloader.to_screen('[ffmpeg] Post-process file %s exists, skipping' % new_path)
|
||||
return [], information
|
||||
|
||||
|
@@ -64,8 +64,8 @@ class XAttrMetadataPP(PostProcessor):
|
||||
except XAttrMetadataError as e:
|
||||
if e.reason == 'NO_SPACE':
|
||||
self._downloader.report_warning(
|
||||
'There\'s no disk space left, disk quota exceeded or filesystem xattr limit exceeded. ' +
|
||||
(('Some ' if num_written else '') + 'extended attributes are not written.').capitalize())
|
||||
'There\'s no disk space left, disk quota exceeded or filesystem xattr limit exceeded. '
|
||||
+ (('Some ' if num_written else '') + 'extended attributes are not written.').capitalize())
|
||||
elif e.reason == 'VALUE_TOO_LONG':
|
||||
self._downloader.report_warning(
|
||||
'Unable to write extended attributes due to too long values.')
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user