Merge branch 'master' of https://github.com/rg3/youtube-dl
This commit is contained in:
commit
98f7f3c252
6
.github/ISSUE_TEMPLATE/1_broken_site.md
vendored
6
.github/ISSUE_TEMPLATE/1_broken_site.md
vendored
@ -18,7 +18,7 @@ title: ''
|
|||||||
|
|
||||||
<!--
|
<!--
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.07.30. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.08.13. 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 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.
|
- 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.
|
- Search the bugtracker for similar issues: http://yt-dl.org/search-issues. DO NOT post duplicates.
|
||||||
@ -26,7 +26,7 @@ Carefully read and work through this check list in order to prevent the most com
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
- [ ] I'm reporting a broken site support
|
- [ ] I'm reporting a broken site support
|
||||||
- [ ] I've verified that I'm running youtube-dl version **2019.07.30**
|
- [ ] I've verified that I'm running youtube-dl version **2019.08.13**
|
||||||
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
- [ ] 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 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
|
- [ ] I've searched the bugtracker for similar issues including closed ones
|
||||||
@ -41,7 +41,7 @@ Add the `-v` flag to your command line you run youtube-dl with (`youtube-dl -v <
|
|||||||
[debug] User config: []
|
[debug] User config: []
|
||||||
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
||||||
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
||||||
[debug] youtube-dl version 2019.07.30
|
[debug] youtube-dl version 2019.08.13
|
||||||
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
||||||
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
||||||
[debug] Proxy map: {}
|
[debug] Proxy map: {}
|
||||||
|
@ -19,7 +19,7 @@ labels: 'site-support-request'
|
|||||||
|
|
||||||
<!--
|
<!--
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.07.30. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.08.13. 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 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.
|
- 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.
|
- Search the bugtracker for similar site support requests: http://yt-dl.org/search-issues. DO NOT post duplicates.
|
||||||
@ -27,7 +27,7 @@ Carefully read and work through this check list in order to prevent the most com
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
- [ ] I'm reporting a new site support request
|
- [ ] I'm reporting a new site support request
|
||||||
- [ ] I've verified that I'm running youtube-dl version **2019.07.30**
|
- [ ] I've verified that I'm running youtube-dl version **2019.08.13**
|
||||||
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
- [ ] 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 checked that none of provided URLs violate any copyrights
|
||||||
- [ ] I've searched the bugtracker for similar site support requests including closed ones
|
- [ ] I've searched the bugtracker for similar site support requests including closed ones
|
||||||
|
@ -18,13 +18,13 @@ title: ''
|
|||||||
|
|
||||||
<!--
|
<!--
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.07.30. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.08.13. 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.
|
- 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])
|
- Finally, put x into all relevant boxes (like this [x])
|
||||||
-->
|
-->
|
||||||
|
|
||||||
- [ ] I'm reporting a site feature request
|
- [ ] I'm reporting a site feature request
|
||||||
- [ ] I've verified that I'm running youtube-dl version **2019.07.30**
|
- [ ] I've verified that I'm running youtube-dl version **2019.08.13**
|
||||||
- [ ] I've searched the bugtracker for similar site feature requests including closed ones
|
- [ ] I've searched the bugtracker for similar site feature requests including closed ones
|
||||||
|
|
||||||
|
|
||||||
|
6
.github/ISSUE_TEMPLATE/4_bug_report.md
vendored
6
.github/ISSUE_TEMPLATE/4_bug_report.md
vendored
@ -18,7 +18,7 @@ title: ''
|
|||||||
|
|
||||||
<!--
|
<!--
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.07.30. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.08.13. 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 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.
|
- 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.
|
- Search the bugtracker for similar issues: http://yt-dl.org/search-issues. DO NOT post duplicates.
|
||||||
@ -27,7 +27,7 @@ Carefully read and work through this check list in order to prevent the most com
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
- [ ] I'm reporting a broken site support issue
|
- [ ] I'm reporting a broken site support issue
|
||||||
- [ ] I've verified that I'm running youtube-dl version **2019.07.30**
|
- [ ] I've verified that I'm running youtube-dl version **2019.08.13**
|
||||||
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
- [ ] 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 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 searched the bugtracker for similar bug reports including closed ones
|
||||||
@ -43,7 +43,7 @@ Add the `-v` flag to your command line you run youtube-dl with (`youtube-dl -v <
|
|||||||
[debug] User config: []
|
[debug] User config: []
|
||||||
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
||||||
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
||||||
[debug] youtube-dl version 2019.07.30
|
[debug] youtube-dl version 2019.08.13
|
||||||
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
||||||
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
||||||
[debug] Proxy map: {}
|
[debug] Proxy map: {}
|
||||||
|
4
.github/ISSUE_TEMPLATE/5_feature_request.md
vendored
4
.github/ISSUE_TEMPLATE/5_feature_request.md
vendored
@ -19,13 +19,13 @@ labels: 'request'
|
|||||||
|
|
||||||
<!--
|
<!--
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
|
||||||
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.07.30. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
|
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2019.08.13. 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.
|
- 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])
|
- Finally, put x into all relevant boxes (like this [x])
|
||||||
-->
|
-->
|
||||||
|
|
||||||
- [ ] I'm reporting a feature request
|
- [ ] I'm reporting a feature request
|
||||||
- [ ] I've verified that I'm running youtube-dl version **2019.07.30**
|
- [ ] I've verified that I'm running youtube-dl version **2019.08.13**
|
||||||
- [ ] I've searched the bugtracker for similar feature requests including closed ones
|
- [ ] I've searched the bugtracker for similar feature requests including closed ones
|
||||||
|
|
||||||
|
|
||||||
|
@ -339,6 +339,72 @@ Incorrect:
|
|||||||
'PLMYEtVRpaqY00V9W81Cwmzp6N6vZqfUKD4'
|
'PLMYEtVRpaqY00V9W81Cwmzp6N6vZqfUKD4'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Inline values
|
||||||
|
|
||||||
|
Extracting variables is acceptable for reducing code duplication and improving readability of complex expressions. However, you should avoid extracting variables used only once and moving them to opposite parts of the extractor file, which makes reading the linear flow difficult.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
Correct:
|
||||||
|
|
||||||
|
```python
|
||||||
|
title = self._html_search_regex(r'<title>([^<]+)</title>', webpage, 'title')
|
||||||
|
```
|
||||||
|
|
||||||
|
Incorrect:
|
||||||
|
|
||||||
|
```python
|
||||||
|
TITLE_RE = r'<title>([^<]+)</title>'
|
||||||
|
# ...some lines of code...
|
||||||
|
title = self._html_search_regex(TITLE_RE, webpage, 'title')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Collapse fallbacks
|
||||||
|
|
||||||
|
Multiple fallback values can quickly become unwieldy. Collapse multiple fallback values into a single expression via a list of patterns.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
Good:
|
||||||
|
|
||||||
|
```python
|
||||||
|
description = self._html_search_meta(
|
||||||
|
['og:description', 'description', 'twitter:description'],
|
||||||
|
webpage, 'description', default=None)
|
||||||
|
```
|
||||||
|
|
||||||
|
Unwieldy:
|
||||||
|
|
||||||
|
```python
|
||||||
|
description = (
|
||||||
|
self._og_search_description(webpage, default=None)
|
||||||
|
or self._html_search_meta('description', webpage, default=None)
|
||||||
|
or self._html_search_meta('twitter:description', webpage, default=None))
|
||||||
|
```
|
||||||
|
|
||||||
|
Methods supporting list of patterns are: `_search_regex`, `_html_search_regex`, `_og_search_property`, `_html_search_meta`.
|
||||||
|
|
||||||
|
### Trailing parentheses
|
||||||
|
|
||||||
|
Always move trailing parentheses after the last argument.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
Correct:
|
||||||
|
|
||||||
|
```python
|
||||||
|
lambda x: x['ResultSet']['Result'][0]['VideoUrlSet']['VideoUrl'],
|
||||||
|
list)
|
||||||
|
```
|
||||||
|
|
||||||
|
Incorrect:
|
||||||
|
|
||||||
|
```python
|
||||||
|
lambda x: x['ResultSet']['Result'][0]['VideoUrlSet']['VideoUrl'],
|
||||||
|
list,
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
### Use convenience conversion and parsing functions
|
### Use convenience conversion and parsing functions
|
||||||
|
|
||||||
Wrap all extracted numeric data into safe functions from [`youtube_dl/utils.py`](https://github.com/ytdl-org/youtube-dl/blob/master/youtube_dl/utils.py): `int_or_none`, `float_or_none`. Use them for string to number conversions as well.
|
Wrap all extracted numeric data into safe functions from [`youtube_dl/utils.py`](https://github.com/ytdl-org/youtube-dl/blob/master/youtube_dl/utils.py): `int_or_none`, `float_or_none`. Use them for string to number conversions as well.
|
||||||
|
30
ChangeLog
30
ChangeLog
@ -1,3 +1,33 @@
|
|||||||
|
version 2019.08.13
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [downloader/fragment] Fix ETA calculation of resumed download (#21992)
|
||||||
|
* [YoutubeDL] Check annotations availability (#18582)
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [youtube:playlist] Improve flat extraction (#21927)
|
||||||
|
* [youtube] Fix annotations extraction (#22045)
|
||||||
|
+ [discovery] Extract series meta field (#21808)
|
||||||
|
* [youtube] Improve error detection (#16445)
|
||||||
|
* [vimeo] Fix album extraction (#1933, #15704, #15855, #18967, #21986)
|
||||||
|
+ [roosterteeth] Add support for watch URLs
|
||||||
|
* [discovery] Limit video data by show slug (#21980)
|
||||||
|
|
||||||
|
|
||||||
|
version 2019.08.02
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [tvigle] Add support for HLS and DASH formats (#21967)
|
||||||
|
* [tvigle] Fix extraction (#21967)
|
||||||
|
+ [yandexvideo] Add support for DASH formats (#21971)
|
||||||
|
* [discovery] Use API call for video data extraction (#21808)
|
||||||
|
+ [mgtv] Extract format_note (#21881)
|
||||||
|
* [tvn24] Fix metadata extraction (#21833, #21834)
|
||||||
|
* [dlive] Relax URL regular expression (#21909)
|
||||||
|
+ [openload] Add support for oload.best (#21913)
|
||||||
|
* [youtube] Improve metadata extraction for age gate content (#21943)
|
||||||
|
|
||||||
|
|
||||||
version 2019.07.30
|
version 2019.07.30
|
||||||
|
|
||||||
Extractors
|
Extractors
|
||||||
|
66
README.md
66
README.md
@ -1216,6 +1216,72 @@ Incorrect:
|
|||||||
'PLMYEtVRpaqY00V9W81Cwmzp6N6vZqfUKD4'
|
'PLMYEtVRpaqY00V9W81Cwmzp6N6vZqfUKD4'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Inline values
|
||||||
|
|
||||||
|
Extracting variables is acceptable for reducing code duplication and improving readability of complex expressions. However, you should avoid extracting variables used only once and moving them to opposite parts of the extractor file, which makes reading the linear flow difficult.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
Correct:
|
||||||
|
|
||||||
|
```python
|
||||||
|
title = self._html_search_regex(r'<title>([^<]+)</title>', webpage, 'title')
|
||||||
|
```
|
||||||
|
|
||||||
|
Incorrect:
|
||||||
|
|
||||||
|
```python
|
||||||
|
TITLE_RE = r'<title>([^<]+)</title>'
|
||||||
|
# ...some lines of code...
|
||||||
|
title = self._html_search_regex(TITLE_RE, webpage, 'title')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Collapse fallbacks
|
||||||
|
|
||||||
|
Multiple fallback values can quickly become unwieldy. Collapse multiple fallback values into a single expression via a list of patterns.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
Good:
|
||||||
|
|
||||||
|
```python
|
||||||
|
description = self._html_search_meta(
|
||||||
|
['og:description', 'description', 'twitter:description'],
|
||||||
|
webpage, 'description', default=None)
|
||||||
|
```
|
||||||
|
|
||||||
|
Unwieldy:
|
||||||
|
|
||||||
|
```python
|
||||||
|
description = (
|
||||||
|
self._og_search_description(webpage, default=None)
|
||||||
|
or self._html_search_meta('description', webpage, default=None)
|
||||||
|
or self._html_search_meta('twitter:description', webpage, default=None))
|
||||||
|
```
|
||||||
|
|
||||||
|
Methods supporting list of patterns are: `_search_regex`, `_html_search_regex`, `_og_search_property`, `_html_search_meta`.
|
||||||
|
|
||||||
|
### Trailing parentheses
|
||||||
|
|
||||||
|
Always move trailing parentheses after the last argument.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
Correct:
|
||||||
|
|
||||||
|
```python
|
||||||
|
lambda x: x['ResultSet']['Result'][0]['VideoUrlSet']['VideoUrl'],
|
||||||
|
list)
|
||||||
|
```
|
||||||
|
|
||||||
|
Incorrect:
|
||||||
|
|
||||||
|
```python
|
||||||
|
lambda x: x['ResultSet']['Result'][0]['VideoUrlSet']['VideoUrl'],
|
||||||
|
list,
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
### Use convenience conversion and parsing functions
|
### Use convenience conversion and parsing functions
|
||||||
|
|
||||||
Wrap all extracted numeric data into safe functions from [`youtube_dl/utils.py`](https://github.com/ytdl-org/youtube-dl/blob/master/youtube_dl/utils.py): `int_or_none`, `float_or_none`. Use them for string to number conversions as well.
|
Wrap all extracted numeric data into safe functions from [`youtube_dl/utils.py`](https://github.com/ytdl-org/youtube-dl/blob/master/youtube_dl/utils.py): `int_or_none`, `float_or_none`. Use them for string to number conversions as well.
|
||||||
|
@ -1783,6 +1783,8 @@ class YoutubeDL(object):
|
|||||||
annofn = replace_extension(filename, 'annotations.xml', info_dict.get('ext'))
|
annofn = replace_extension(filename, 'annotations.xml', info_dict.get('ext'))
|
||||||
if self.params.get('nooverwrites', False) and os.path.exists(encodeFilename(annofn)):
|
if self.params.get('nooverwrites', False) and os.path.exists(encodeFilename(annofn)):
|
||||||
self.to_screen('[info] Video annotations are already present')
|
self.to_screen('[info] Video annotations are already present')
|
||||||
|
elif not info_dict.get('annotations'):
|
||||||
|
self.report_warning('There are no annotations to write.')
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
self.to_screen('[info] Writing video annotations to: ' + annofn)
|
self.to_screen('[info] Writing video annotations to: ' + annofn)
|
||||||
|
@ -94,7 +94,7 @@ def _real_main(argv=None):
|
|||||||
if opts.verbose:
|
if opts.verbose:
|
||||||
write_string('[debug] Batch file urls: ' + repr(batch_urls) + '\n')
|
write_string('[debug] Batch file urls: ' + repr(batch_urls) + '\n')
|
||||||
except IOError:
|
except IOError:
|
||||||
sys.exit('ERROR: batch file could not be read')
|
sys.exit('ERROR: batch file %s could not be read' % opts.batchfile)
|
||||||
all_urls = batch_urls + [url.strip() for url in args] # batch_urls are already striped in read_batch_urls
|
all_urls = batch_urls + [url.strip() for url in args] # batch_urls are already striped in read_batch_urls
|
||||||
_enc = preferredencoding()
|
_enc = preferredencoding()
|
||||||
all_urls = [url.decode(_enc, 'ignore') if isinstance(url, bytes) else url for url in all_urls]
|
all_urls = [url.decode(_enc, 'ignore') if isinstance(url, bytes) else url for url in all_urls]
|
||||||
|
@ -190,12 +190,13 @@ class FragmentFD(FileDownloader):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def _start_frag_download(self, ctx):
|
def _start_frag_download(self, ctx):
|
||||||
|
resume_len = ctx['complete_frags_downloaded_bytes']
|
||||||
total_frags = ctx['total_frags']
|
total_frags = ctx['total_frags']
|
||||||
# This dict stores the download progress, it's updated by the progress
|
# This dict stores the download progress, it's updated by the progress
|
||||||
# hook
|
# hook
|
||||||
state = {
|
state = {
|
||||||
'status': 'downloading',
|
'status': 'downloading',
|
||||||
'downloaded_bytes': ctx['complete_frags_downloaded_bytes'],
|
'downloaded_bytes': resume_len,
|
||||||
'fragment_index': ctx['fragment_index'],
|
'fragment_index': ctx['fragment_index'],
|
||||||
'fragment_count': total_frags,
|
'fragment_count': total_frags,
|
||||||
'filename': ctx['filename'],
|
'filename': ctx['filename'],
|
||||||
@ -234,8 +235,8 @@ class FragmentFD(FileDownloader):
|
|||||||
state['downloaded_bytes'] += frag_downloaded_bytes - ctx['prev_frag_downloaded_bytes']
|
state['downloaded_bytes'] += frag_downloaded_bytes - ctx['prev_frag_downloaded_bytes']
|
||||||
if not ctx['live']:
|
if not ctx['live']:
|
||||||
state['eta'] = self.calc_eta(
|
state['eta'] = self.calc_eta(
|
||||||
start, time_now, estimated_size,
|
start, time_now, estimated_size - resume_len,
|
||||||
state['downloaded_bytes'])
|
state['downloaded_bytes'] - resume_len)
|
||||||
state['speed'] = s.get('speed') or ctx.get('speed')
|
state['speed'] = s.get('speed') or ctx.get('speed')
|
||||||
ctx['speed'] = state['speed']
|
ctx['speed'] = state['speed']
|
||||||
ctx['prev_frag_downloaded_bytes'] = frag_downloaded_bytes
|
ctx['prev_frag_downloaded_bytes'] = frag_downloaded_bytes
|
||||||
|
@ -5,14 +5,8 @@ import re
|
|||||||
import string
|
import string
|
||||||
|
|
||||||
from .discoverygo import DiscoveryGoBaseIE
|
from .discoverygo import DiscoveryGoBaseIE
|
||||||
from ..compat import (
|
from ..compat import compat_urllib_parse_unquote
|
||||||
compat_str,
|
from ..utils import ExtractorError
|
||||||
compat_urllib_parse_unquote,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
|
||||||
ExtractorError,
|
|
||||||
try_get,
|
|
||||||
)
|
|
||||||
from ..compat import compat_HTTPError
|
from ..compat import compat_HTTPError
|
||||||
|
|
||||||
|
|
||||||
@ -40,15 +34,15 @@ class DiscoveryIE(DiscoveryGoBaseIE):
|
|||||||
cookingchanneltv|
|
cookingchanneltv|
|
||||||
motortrend
|
motortrend
|
||||||
)
|
)
|
||||||
)\.com(?P<path>/tv-shows/[^/]+/(?:video|full-episode)s/(?P<id>[^./?#]+))'''
|
)\.com/tv-shows/(?P<show_slug>[^/]+)/(?:video|full-episode)s/(?P<id>[^./?#]+)'''
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'https://www.discovery.com/tv-shows/cash-cab/videos/dave-foley',
|
'url': 'https://go.discovery.com/tv-shows/cash-cab/videos/riding-with-matthew-perry',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '5a2d9b4d6b66d17a5026e1fd',
|
'id': '5a2f35ce6b66d17a5026e29e',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Dave Foley',
|
'title': 'Riding with Matthew Perry',
|
||||||
'description': 'md5:4b39bcafccf9167ca42810eb5f28b01f',
|
'description': 'md5:a34333153e79bc4526019a5129e7f878',
|
||||||
'duration': 608,
|
'duration': 84,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
'skip_download': True, # requires ffmpeg
|
'skip_download': True, # requires ffmpeg
|
||||||
@ -59,20 +53,17 @@ class DiscoveryIE(DiscoveryGoBaseIE):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'https://go.discovery.com/tv-shows/alaskan-bush-people/videos/follow-your-own-road',
|
'url': 'https://go.discovery.com/tv-shows/alaskan-bush-people/videos/follow-your-own-road',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# using `show_slug` is important to get the correct video data
|
||||||
|
'url': 'https://www.sciencechannel.com/tv-shows/mythbusters-on-science/full-episodes/christmas-special',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
_GEO_COUNTRIES = ['US']
|
_GEO_COUNTRIES = ['US']
|
||||||
_GEO_BYPASS = False
|
_GEO_BYPASS = False
|
||||||
|
_API_BASE_URL = 'https://api.discovery.com/v1/'
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
site, path, display_id = re.match(self._VALID_URL, url).groups()
|
site, show_slug, display_id = re.match(self._VALID_URL, url).groups()
|
||||||
webpage = self._download_webpage(url, display_id)
|
|
||||||
|
|
||||||
react_data = self._parse_json(self._search_regex(
|
|
||||||
r'window\.__reactTransmitPacket\s*=\s*({.+?});',
|
|
||||||
webpage, 'react data'), display_id)
|
|
||||||
content_blocks = react_data['layout'][path]['contentBlocks']
|
|
||||||
video = next(cb for cb in content_blocks if cb.get('type') == 'video')['content']['items'][0]
|
|
||||||
video_id = video['id']
|
|
||||||
|
|
||||||
access_token = None
|
access_token = None
|
||||||
cookies = self._get_cookies(url)
|
cookies = self._get_cookies(url)
|
||||||
@ -82,27 +73,36 @@ class DiscoveryIE(DiscoveryGoBaseIE):
|
|||||||
if auth_storage_cookie and auth_storage_cookie.value:
|
if auth_storage_cookie and auth_storage_cookie.value:
|
||||||
auth_storage = self._parse_json(compat_urllib_parse_unquote(
|
auth_storage = self._parse_json(compat_urllib_parse_unquote(
|
||||||
compat_urllib_parse_unquote(auth_storage_cookie.value)),
|
compat_urllib_parse_unquote(auth_storage_cookie.value)),
|
||||||
video_id, fatal=False) or {}
|
display_id, fatal=False) or {}
|
||||||
access_token = auth_storage.get('a') or auth_storage.get('access_token')
|
access_token = auth_storage.get('a') or auth_storage.get('access_token')
|
||||||
|
|
||||||
if not access_token:
|
if not access_token:
|
||||||
access_token = self._download_json(
|
access_token = self._download_json(
|
||||||
'https://%s.com/anonymous' % site, display_id, query={
|
'https://%s.com/anonymous' % site, display_id,
|
||||||
|
'Downloading token JSON metadata', query={
|
||||||
'authRel': 'authorization',
|
'authRel': 'authorization',
|
||||||
'client_id': try_get(
|
'client_id': '3020a40c2356a645b4b4',
|
||||||
react_data, lambda x: x['application']['apiClientId'],
|
|
||||||
compat_str) or '3020a40c2356a645b4b4',
|
|
||||||
'nonce': ''.join([random.choice(string.ascii_letters) for _ in range(32)]),
|
'nonce': ''.join([random.choice(string.ascii_letters) for _ in range(32)]),
|
||||||
'redirectUri': 'https://fusion.ddmcdn.com/app/mercury-sdk/180/redirectHandler.html?https://www.%s.com' % site,
|
'redirectUri': 'https://fusion.ddmcdn.com/app/mercury-sdk/180/redirectHandler.html?https://www.%s.com' % site,
|
||||||
})['access_token']
|
})['access_token']
|
||||||
|
|
||||||
try:
|
headers = self.geo_verification_headers()
|
||||||
headers = self.geo_verification_headers()
|
headers['Authorization'] = 'Bearer ' + access_token
|
||||||
headers['Authorization'] = 'Bearer ' + access_token
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
video = self._download_json(
|
||||||
|
self._API_BASE_URL + 'content/videos',
|
||||||
|
display_id, 'Downloading content JSON metadata',
|
||||||
|
headers=headers, query={
|
||||||
|
'embed': 'show.name',
|
||||||
|
'fields': 'authenticated,description.detailed,duration,episodeNumber,id,name,parental.rating,season.number,show,tags',
|
||||||
|
'slug': display_id,
|
||||||
|
'show_slug': show_slug,
|
||||||
|
})[0]
|
||||||
|
video_id = video['id']
|
||||||
stream = self._download_json(
|
stream = self._download_json(
|
||||||
'https://api.discovery.com/v1/streaming/video/' + video_id,
|
self._API_BASE_URL + 'streaming/video/' + video_id,
|
||||||
display_id, headers=headers)
|
display_id, 'Downloading streaming JSON metadata', headers=headers)
|
||||||
except ExtractorError as e:
|
except ExtractorError as e:
|
||||||
if isinstance(e.cause, compat_HTTPError) and e.cause.code in (401, 403):
|
if isinstance(e.cause, compat_HTTPError) and e.cause.code in (401, 403):
|
||||||
e_description = self._parse_json(
|
e_description = self._parse_json(
|
||||||
|
@ -9,8 +9,8 @@ from ..utils import int_or_none
|
|||||||
|
|
||||||
class DLiveVODIE(InfoExtractor):
|
class DLiveVODIE(InfoExtractor):
|
||||||
IE_NAME = 'dlive:vod'
|
IE_NAME = 'dlive:vod'
|
||||||
_VALID_URL = r'https?://(?:www\.)?dlive\.tv/p/(?P<uploader_id>.+?)\+(?P<id>[a-zA-Z0-9]+)'
|
_VALID_URL = r'https?://(?:www\.)?dlive\.tv/p/(?P<uploader_id>.+?)\+(?P<id>[^/?#&]+)'
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
'url': 'https://dlive.tv/p/pdp+3mTzOl4WR',
|
'url': 'https://dlive.tv/p/pdp+3mTzOl4WR',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '3mTzOl4WR',
|
'id': '3mTzOl4WR',
|
||||||
@ -20,7 +20,10 @@ class DLiveVODIE(InfoExtractor):
|
|||||||
'timestamp': 1562011015,
|
'timestamp': 1562011015,
|
||||||
'uploader_id': 'pdp',
|
'uploader_id': 'pdp',
|
||||||
}
|
}
|
||||||
}
|
}, {
|
||||||
|
'url': 'https://dlive.tv/p/pdpreplay+D-RD-xSZg',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
uploader_id, vod_id = re.match(self._VALID_URL, url).groups()
|
uploader_id, vod_id = re.match(self._VALID_URL, url).groups()
|
||||||
|
@ -82,6 +82,7 @@ class MGTVIE(InfoExtractor):
|
|||||||
'http_headers': {
|
'http_headers': {
|
||||||
'Referer': url,
|
'Referer': url,
|
||||||
},
|
},
|
||||||
|
'format_note': stream.get('name'),
|
||||||
})
|
})
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@ class PhantomJSwrapper(object):
|
|||||||
|
|
||||||
|
|
||||||
class OpenloadIE(InfoExtractor):
|
class OpenloadIE(InfoExtractor):
|
||||||
_DOMAINS = r'(?:openload\.(?:co|io|link|pw)|oload\.(?:tv|biz|stream|site|xyz|win|download|cloud|cc|icu|fun|club|info|press|pw|life|live|space|services|website)|oladblock\.(?:services|xyz|me)|openloed\.co)'
|
_DOMAINS = r'(?:openload\.(?:co|io|link|pw)|oload\.(?:tv|best|biz|stream|site|xyz|win|download|cloud|cc|icu|fun|club|info|press|pw|life|live|space|services|website)|oladblock\.(?:services|xyz|me)|openloed\.co)'
|
||||||
_VALID_URL = r'''(?x)
|
_VALID_URL = r'''(?x)
|
||||||
https?://
|
https?://
|
||||||
(?P<host>
|
(?P<host>
|
||||||
@ -368,6 +368,9 @@ class OpenloadIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'https://oload.biz/f/bEk3Gp8ARr4/',
|
'url': 'https://oload.biz/f/bEk3Gp8ARr4/',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://oload.best/embed/kkz9JgVZeWc/',
|
||||||
|
'only_matching': True,
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://oladblock.services/f/b8NWEgkqNLI/',
|
'url': 'https://oladblock.services/f/b8NWEgkqNLI/',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
@ -18,15 +18,14 @@ class PikselIE(InfoExtractor):
|
|||||||
_VALID_URL = r'https?://player\.piksel\.com/v/(?P<id>[a-z0-9]+)'
|
_VALID_URL = r'https?://player\.piksel\.com/v/(?P<id>[a-z0-9]+)'
|
||||||
_TESTS = [
|
_TESTS = [
|
||||||
{
|
{
|
||||||
'url': 'http://player.piksel.com/v/nv60p12f',
|
'url': 'http://player.piksel.com/v/ums2867l',
|
||||||
'md5': 'd9c17bbe9c3386344f9cfd32fad8d235',
|
'md5': '34e34c8d89dc2559976a6079db531e85',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'nv60p12f',
|
'id': 'ums2867l',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'فن الحياة - الحلقة 1',
|
'title': 'GX-005 with Caption',
|
||||||
'description': 'احدث برامج الداعية الاسلامي " مصطفي حسني " فى رمضان 2016علي النهار نور',
|
'timestamp': 1481335659,
|
||||||
'timestamp': 1465231790,
|
'upload_date': '20161210'
|
||||||
'upload_date': '20160606',
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -39,7 +38,7 @@ class PikselIE(InfoExtractor):
|
|||||||
'title': 'WAW- State of Washington vs. Donald J. Trump, et al',
|
'title': 'WAW- State of Washington vs. Donald J. Trump, et al',
|
||||||
'description': 'State of Washington vs. Donald J. Trump, et al, Case Number 17-CV-00141-JLR, TRO Hearing, Civil Rights Case, 02/3/2017, 1:00 PM (PST), Seattle Federal Courthouse, Seattle, WA, Judge James L. Robart presiding.',
|
'description': 'State of Washington vs. Donald J. Trump, et al, Case Number 17-CV-00141-JLR, TRO Hearing, Civil Rights Case, 02/3/2017, 1:00 PM (PST), Seattle Federal Courthouse, Seattle, WA, Judge James L. Robart presiding.',
|
||||||
'timestamp': 1486171129,
|
'timestamp': 1486171129,
|
||||||
'upload_date': '20170204',
|
'upload_date': '20170204'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -113,6 +112,13 @@ class PikselIE(InfoExtractor):
|
|||||||
})
|
})
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
subtitles = {}
|
||||||
|
for caption in video_data.get('captions', []):
|
||||||
|
caption_url = caption.get('url')
|
||||||
|
if caption_url:
|
||||||
|
subtitles.setdefault(caption.get('locale', 'en'), []).append({
|
||||||
|
'url': caption_url})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': title,
|
'title': title,
|
||||||
@ -120,4 +126,5 @@ class PikselIE(InfoExtractor):
|
|||||||
'thumbnail': video_data.get('thumbnailUrl'),
|
'thumbnail': video_data.get('thumbnailUrl'),
|
||||||
'timestamp': parse_iso8601(video_data.get('dateadd')),
|
'timestamp': parse_iso8601(video_data.get('dateadd')),
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
|
'subtitles': subtitles,
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class RoosterTeethIE(InfoExtractor):
|
class RoosterTeethIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:.+?\.)?roosterteeth\.com/episode/(?P<id>[^/?#&]+)'
|
_VALID_URL = r'https?://(?:.+?\.)?roosterteeth\.com/(?:episode|watch)/(?P<id>[^/?#&]+)'
|
||||||
_LOGIN_URL = 'https://roosterteeth.com/login'
|
_LOGIN_URL = 'https://roosterteeth.com/login'
|
||||||
_NETRC_MACHINE = 'roosterteeth'
|
_NETRC_MACHINE = 'roosterteeth'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
@ -49,6 +49,9 @@ class RoosterTeethIE(InfoExtractor):
|
|||||||
# only available for FIRST members
|
# only available for FIRST members
|
||||||
'url': 'http://roosterteeth.com/episode/rt-docs-the-world-s-greatest-head-massage-the-world-s-greatest-head-massage-an-asmr-journey-part-one',
|
'url': 'http://roosterteeth.com/episode/rt-docs-the-world-s-greatest-head-massage-the-world-s-greatest-head-massage-an-asmr-journey-part-one',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://roosterteeth.com/watch/million-dollars-but-season-2-million-dollars-but-the-game-announcement',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _login(self):
|
def _login(self):
|
||||||
|
@ -9,6 +9,8 @@ from ..utils import (
|
|||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_age_limit,
|
parse_age_limit,
|
||||||
|
try_get,
|
||||||
|
url_or_none,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -23,11 +25,10 @@ class TvigleIE(InfoExtractor):
|
|||||||
_TESTS = [
|
_TESTS = [
|
||||||
{
|
{
|
||||||
'url': 'http://www.tvigle.ru/video/sokrat/',
|
'url': 'http://www.tvigle.ru/video/sokrat/',
|
||||||
'md5': '36514aed3657d4f70b4b2cef8eb520cd',
|
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '1848932',
|
'id': '1848932',
|
||||||
'display_id': 'sokrat',
|
'display_id': 'sokrat',
|
||||||
'ext': 'flv',
|
'ext': 'mp4',
|
||||||
'title': 'Сократ',
|
'title': 'Сократ',
|
||||||
'description': 'md5:d6b92ffb7217b4b8ebad2e7665253c17',
|
'description': 'md5:d6b92ffb7217b4b8ebad2e7665253c17',
|
||||||
'duration': 6586,
|
'duration': 6586,
|
||||||
@ -37,7 +38,6 @@ class TvigleIE(InfoExtractor):
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': 'http://www.tvigle.ru/video/vladimir-vysotskii/vedushchii-teleprogrammy-60-minut-ssha-o-vladimire-vysotskom/',
|
'url': 'http://www.tvigle.ru/video/vladimir-vysotskii/vedushchii-teleprogrammy-60-minut-ssha-o-vladimire-vysotskom/',
|
||||||
'md5': 'e7efe5350dd5011d0de6550b53c3ba7b',
|
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '5142516',
|
'id': '5142516',
|
||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
@ -62,7 +62,7 @@ class TvigleIE(InfoExtractor):
|
|||||||
webpage = self._download_webpage(url, display_id)
|
webpage = self._download_webpage(url, display_id)
|
||||||
video_id = self._html_search_regex(
|
video_id = self._html_search_regex(
|
||||||
(r'<div[^>]+class=["\']player["\'][^>]+id=["\'](\d+)',
|
(r'<div[^>]+class=["\']player["\'][^>]+id=["\'](\d+)',
|
||||||
r'var\s+cloudId\s*=\s*["\'](\d+)',
|
r'cloudId\s*=\s*["\'](\d+)',
|
||||||
r'class="video-preview current_playing" id="(\d+)"'),
|
r'class="video-preview current_playing" id="(\d+)"'),
|
||||||
webpage, 'video id')
|
webpage, 'video id')
|
||||||
|
|
||||||
@ -90,21 +90,40 @@ class TvigleIE(InfoExtractor):
|
|||||||
age_limit = parse_age_limit(item.get('ageRestrictions'))
|
age_limit = parse_age_limit(item.get('ageRestrictions'))
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for vcodec, fmts in item['videos'].items():
|
for vcodec, url_or_fmts in item['videos'].items():
|
||||||
if vcodec == 'hls':
|
if vcodec == 'hls':
|
||||||
continue
|
m3u8_url = url_or_none(url_or_fmts)
|
||||||
for format_id, video_url in fmts.items():
|
if not m3u8_url:
|
||||||
if format_id == 'm3u8':
|
|
||||||
continue
|
continue
|
||||||
height = self._search_regex(
|
formats.extend(self._extract_m3u8_formats(
|
||||||
r'^(\d+)[pP]$', format_id, 'height', default=None)
|
m3u8_url, video_id, ext='mp4', entry_protocol='m3u8_native',
|
||||||
formats.append({
|
m3u8_id='hls', fatal=False))
|
||||||
'url': video_url,
|
elif vcodec == 'dash':
|
||||||
'format_id': '%s-%s' % (vcodec, format_id),
|
mpd_url = url_or_none(url_or_fmts)
|
||||||
'vcodec': vcodec,
|
if not mpd_url:
|
||||||
'height': int_or_none(height),
|
continue
|
||||||
'filesize': int_or_none(item.get('video_files_size', {}).get(vcodec, {}).get(format_id)),
|
formats.extend(self._extract_mpd_formats(
|
||||||
})
|
mpd_url, video_id, mpd_id='dash', fatal=False))
|
||||||
|
else:
|
||||||
|
if not isinstance(url_or_fmts, dict):
|
||||||
|
continue
|
||||||
|
for format_id, video_url in url_or_fmts.items():
|
||||||
|
if format_id == 'm3u8':
|
||||||
|
continue
|
||||||
|
video_url = url_or_none(video_url)
|
||||||
|
if not video_url:
|
||||||
|
continue
|
||||||
|
height = self._search_regex(
|
||||||
|
r'^(\d+)[pP]$', format_id, 'height', default=None)
|
||||||
|
filesize = int_or_none(try_get(
|
||||||
|
item, lambda x: x['video_files_size'][vcodec][format_id]))
|
||||||
|
formats.append({
|
||||||
|
'url': video_url,
|
||||||
|
'format_id': '%s-%s' % (vcodec, format_id),
|
||||||
|
'vcodec': vcodec,
|
||||||
|
'height': int_or_none(height),
|
||||||
|
'filesize': filesize,
|
||||||
|
})
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -4,6 +4,7 @@ from __future__ import unicode_literals
|
|||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
NO_DEFAULT,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,9 +18,21 @@ class TVN24IE(InfoExtractor):
|
|||||||
'id': '1584444',
|
'id': '1584444',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': '"Święta mają być wesołe, dlatego, ludziska, wszyscy pod jemiołę"',
|
'title': '"Święta mają być wesołe, dlatego, ludziska, wszyscy pod jemiołę"',
|
||||||
'description': 'Wyjątkowe orędzie Artura Andrusa, jednego z gości "Szkła kontaktowego".',
|
'description': 'Wyjątkowe orędzie Artura Andrusa, jednego z gości Szkła kontaktowego.',
|
||||||
'thumbnail': 're:https?://.*[.]jpeg',
|
'thumbnail': 're:https?://.*[.]jpeg',
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
# different layout
|
||||||
|
'url': 'https://tvnmeteo.tvn24.pl/magazyny/maja-w-ogrodzie,13/odcinki-online,1,4,1,0/pnacza-ptaki-i-iglaki-odc-691-hgtv-odc-29,1771763.html',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1771763',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Pnącza, ptaki i iglaki (odc. 691 /HGTV odc. 29)',
|
||||||
|
'thumbnail': 're:https?://.*',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://fakty.tvn24.pl/ogladaj-online,60/53-konferencja-bezpieczenstwa-w-monachium,716431.html',
|
'url': 'http://fakty.tvn24.pl/ogladaj-online,60/53-konferencja-bezpieczenstwa-w-monachium,716431.html',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
@ -35,18 +48,21 @@ class TVN24IE(InfoExtractor):
|
|||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
display_id = self._match_id(url)
|
||||||
|
|
||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
|
||||||
title = self._og_search_title(webpage)
|
title = self._og_search_title(
|
||||||
|
webpage, default=None) or self._search_regex(
|
||||||
|
r'<h\d+[^>]+class=["\']magazineItemHeader[^>]+>(.+?)</h',
|
||||||
|
webpage, 'title')
|
||||||
|
|
||||||
def extract_json(attr, name, fatal=True):
|
def extract_json(attr, name, default=NO_DEFAULT, fatal=True):
|
||||||
return self._parse_json(
|
return self._parse_json(
|
||||||
self._search_regex(
|
self._search_regex(
|
||||||
r'\b%s=(["\'])(?P<json>(?!\1).+?)\1' % attr, webpage,
|
r'\b%s=(["\'])(?P<json>(?!\1).+?)\1' % attr, webpage,
|
||||||
name, group='json', fatal=fatal) or '{}',
|
name, group='json', default=default, fatal=fatal) or '{}',
|
||||||
video_id, transform_source=unescapeHTML, fatal=fatal)
|
display_id, transform_source=unescapeHTML, fatal=fatal)
|
||||||
|
|
||||||
quality_data = extract_json('data-quality', 'formats')
|
quality_data = extract_json('data-quality', 'formats')
|
||||||
|
|
||||||
@ -59,16 +75,24 @@ class TVN24IE(InfoExtractor):
|
|||||||
})
|
})
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
description = self._og_search_description(webpage)
|
description = self._og_search_description(webpage, default=None)
|
||||||
thumbnail = self._og_search_thumbnail(
|
thumbnail = self._og_search_thumbnail(
|
||||||
webpage, default=None) or self._html_search_regex(
|
webpage, default=None) or self._html_search_regex(
|
||||||
r'\bdata-poster=(["\'])(?P<url>(?!\1).+?)\1', webpage,
|
r'\bdata-poster=(["\'])(?P<url>(?!\1).+?)\1', webpage,
|
||||||
'thumbnail', group='url')
|
'thumbnail', group='url')
|
||||||
|
|
||||||
|
video_id = None
|
||||||
|
|
||||||
share_params = extract_json(
|
share_params = extract_json(
|
||||||
'data-share-params', 'share params', fatal=False)
|
'data-share-params', 'share params', default=None)
|
||||||
if isinstance(share_params, dict):
|
if isinstance(share_params, dict):
|
||||||
video_id = share_params.get('id') or video_id
|
video_id = share_params.get('id')
|
||||||
|
|
||||||
|
if not video_id:
|
||||||
|
video_id = self._search_regex(
|
||||||
|
r'data-vid-id=["\'](\d+)', webpage, 'video id',
|
||||||
|
default=None) or self._search_regex(
|
||||||
|
r',(\d+)\.html', url, 'video id', default=display_id)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
|
@ -2,12 +2,14 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
import functools
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
|
compat_kwargs,
|
||||||
compat_HTTPError,
|
compat_HTTPError,
|
||||||
compat_str,
|
compat_str,
|
||||||
compat_urlparse,
|
compat_urlparse,
|
||||||
@ -19,6 +21,7 @@ from ..utils import (
|
|||||||
int_or_none,
|
int_or_none,
|
||||||
merge_dicts,
|
merge_dicts,
|
||||||
NO_DEFAULT,
|
NO_DEFAULT,
|
||||||
|
OnDemandPagedList,
|
||||||
parse_filesize,
|
parse_filesize,
|
||||||
qualities,
|
qualities,
|
||||||
RegexNotFoundError,
|
RegexNotFoundError,
|
||||||
@ -98,6 +101,13 @@ class VimeoBaseInfoExtractor(InfoExtractor):
|
|||||||
webpage, 'vuid', group='vuid')
|
webpage, 'vuid', group='vuid')
|
||||||
return xsrft, vuid
|
return xsrft, vuid
|
||||||
|
|
||||||
|
def _extract_vimeo_config(self, webpage, video_id, *args, **kwargs):
|
||||||
|
vimeo_config = self._search_regex(
|
||||||
|
r'vimeo\.config\s*=\s*(?:({.+?})|_extend\([^,]+,\s+({.+?})\));',
|
||||||
|
webpage, 'vimeo config', *args, **compat_kwargs(kwargs))
|
||||||
|
if vimeo_config:
|
||||||
|
return self._parse_json(vimeo_config, video_id)
|
||||||
|
|
||||||
def _set_vimeo_cookie(self, name, value):
|
def _set_vimeo_cookie(self, name, value):
|
||||||
self._set_cookie('vimeo.com', name, value)
|
self._set_cookie('vimeo.com', name, value)
|
||||||
|
|
||||||
@ -253,7 +263,7 @@ class VimeoIE(VimeoBaseInfoExtractor):
|
|||||||
\.
|
\.
|
||||||
)?
|
)?
|
||||||
vimeo(?P<pro>pro)?\.com/
|
vimeo(?P<pro>pro)?\.com/
|
||||||
(?!(?:channels|album)/[^/?#]+/?(?:$|[?#])|[^/]+/review/|ondemand/)
|
(?!(?:channels|album|showcase)/[^/?#]+/?(?:$|[?#])|[^/]+/review/|ondemand/)
|
||||||
(?:.*?/)?
|
(?:.*?/)?
|
||||||
(?:
|
(?:
|
||||||
(?:
|
(?:
|
||||||
@ -580,11 +590,9 @@ class VimeoIE(VimeoBaseInfoExtractor):
|
|||||||
# and latter we extract those that are Vimeo specific.
|
# and latter we extract those that are Vimeo specific.
|
||||||
self.report_extraction(video_id)
|
self.report_extraction(video_id)
|
||||||
|
|
||||||
vimeo_config = self._search_regex(
|
vimeo_config = self._extract_vimeo_config(webpage, video_id, default=None)
|
||||||
r'vimeo\.config\s*=\s*(?:({.+?})|_extend\([^,]+,\s+({.+?})\));', webpage,
|
|
||||||
'vimeo config', default=None)
|
|
||||||
if vimeo_config:
|
if vimeo_config:
|
||||||
seed_status = self._parse_json(vimeo_config, video_id).get('seed_status', {})
|
seed_status = vimeo_config.get('seed_status', {})
|
||||||
if seed_status.get('state') == 'failed':
|
if seed_status.get('state') == 'failed':
|
||||||
raise ExtractorError(
|
raise ExtractorError(
|
||||||
'%s said: %s' % (self.IE_NAME, seed_status['title']),
|
'%s said: %s' % (self.IE_NAME, seed_status['title']),
|
||||||
@ -905,7 +913,7 @@ class VimeoUserIE(VimeoChannelIE):
|
|||||||
|
|
||||||
class VimeoAlbumIE(VimeoChannelIE):
|
class VimeoAlbumIE(VimeoChannelIE):
|
||||||
IE_NAME = 'vimeo:album'
|
IE_NAME = 'vimeo:album'
|
||||||
_VALID_URL = r'https://vimeo\.com/album/(?P<id>\d+)(?:$|[?#]|/(?!video))'
|
_VALID_URL = r'https://vimeo\.com/(?:album|showcase)/(?P<id>\d+)(?:$|[?#]|/(?!video))'
|
||||||
_TITLE_RE = r'<header id="page_header">\n\s*<h1>(.*?)</h1>'
|
_TITLE_RE = r'<header id="page_header">\n\s*<h1>(.*?)</h1>'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'https://vimeo.com/album/2632481',
|
'url': 'https://vimeo.com/album/2632481',
|
||||||
@ -925,21 +933,39 @@ class VimeoAlbumIE(VimeoChannelIE):
|
|||||||
'params': {
|
'params': {
|
||||||
'videopassword': 'youtube-dl',
|
'videopassword': 'youtube-dl',
|
||||||
}
|
}
|
||||||
}, {
|
|
||||||
'url': 'https://vimeo.com/album/2632481/sort:plays/format:thumbnail',
|
|
||||||
'only_matching': True,
|
|
||||||
}, {
|
|
||||||
# TODO: respect page number
|
|
||||||
'url': 'https://vimeo.com/album/2632481/page:2/sort:plays/format:thumbnail',
|
|
||||||
'only_matching': True,
|
|
||||||
}]
|
}]
|
||||||
|
_PAGE_SIZE = 100
|
||||||
|
|
||||||
def _page_url(self, base_url, pagenum):
|
def _fetch_page(self, album_id, authorizaion, hashed_pass, page):
|
||||||
return '%s/page:%d/' % (base_url, pagenum)
|
api_page = page + 1
|
||||||
|
query = {
|
||||||
|
'fields': 'link',
|
||||||
|
'page': api_page,
|
||||||
|
'per_page': self._PAGE_SIZE,
|
||||||
|
}
|
||||||
|
if hashed_pass:
|
||||||
|
query['_hashed_pass'] = hashed_pass
|
||||||
|
videos = self._download_json(
|
||||||
|
'https://api.vimeo.com/albums/%s/videos' % album_id,
|
||||||
|
album_id, 'Downloading page %d' % api_page, query=query, headers={
|
||||||
|
'Authorization': 'jwt ' + authorizaion,
|
||||||
|
})['data']
|
||||||
|
for video in videos:
|
||||||
|
link = video.get('link')
|
||||||
|
if not link:
|
||||||
|
continue
|
||||||
|
yield self.url_result(link, VimeoIE.ie_key(), VimeoIE._match_id(link))
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
album_id = self._match_id(url)
|
album_id = self._match_id(url)
|
||||||
return self._extract_videos(album_id, 'https://vimeo.com/album/%s' % album_id)
|
webpage = self._download_webpage(url, album_id)
|
||||||
|
webpage = self._login_list_password(url, album_id, webpage)
|
||||||
|
api_config = self._extract_vimeo_config(webpage, album_id)['api']
|
||||||
|
entries = OnDemandPagedList(functools.partial(
|
||||||
|
self._fetch_page, album_id, api_config['jwt'],
|
||||||
|
api_config.get('hashed_pass')), self._PAGE_SIZE)
|
||||||
|
return self.playlist_result(entries, album_id, self._html_search_regex(
|
||||||
|
r'<title>\s*(.+?)(?:\s+on Vimeo)?</title>', webpage, 'title', fatal=False))
|
||||||
|
|
||||||
|
|
||||||
class VimeoGroupsIE(VimeoAlbumIE):
|
class VimeoGroupsIE(VimeoAlbumIE):
|
||||||
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
determine_ext,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
url_or_none,
|
url_or_none,
|
||||||
)
|
)
|
||||||
@ -47,6 +48,10 @@ class YandexVideoIE(InfoExtractor):
|
|||||||
# episode, sports
|
# episode, sports
|
||||||
'url': 'https://yandex.ru/?stream_channel=1538487871&stream_id=4132a07f71fb0396be93d74b3477131d',
|
'url': 'https://yandex.ru/?stream_channel=1538487871&stream_id=4132a07f71fb0396be93d74b3477131d',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# DASH with DRM
|
||||||
|
'url': 'https://yandex.ru/portal/video?from=morda&stream_id=485a92d94518d73a9d0ff778e13505f8',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
@ -59,13 +64,22 @@ class YandexVideoIE(InfoExtractor):
|
|||||||
'disable_trackings': 1,
|
'disable_trackings': 1,
|
||||||
})['content']
|
})['content']
|
||||||
|
|
||||||
m3u8_url = url_or_none(content.get('content_url')) or url_or_none(
|
content_url = url_or_none(content.get('content_url')) or url_or_none(
|
||||||
content['streams'][0]['url'])
|
content['streams'][0]['url'])
|
||||||
title = content.get('title') or content.get('computed_title')
|
title = content.get('title') or content.get('computed_title')
|
||||||
|
|
||||||
formats = self._extract_m3u8_formats(
|
ext = determine_ext(content_url)
|
||||||
m3u8_url, video_id, 'mp4', entry_protocol='m3u8_native',
|
|
||||||
m3u8_id='hls')
|
if ext == 'm3u8':
|
||||||
|
formats = self._extract_m3u8_formats(
|
||||||
|
content_url, video_id, 'mp4', entry_protocol='m3u8_native',
|
||||||
|
m3u8_id='hls')
|
||||||
|
elif ext == 'mpd':
|
||||||
|
formats = self._extract_mpd_formats(
|
||||||
|
content_url, video_id, mpd_id='dash')
|
||||||
|
else:
|
||||||
|
formats = [{'url': content_url}]
|
||||||
|
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
description = content.get('description')
|
description = content.get('description')
|
||||||
|
@ -31,6 +31,7 @@ from ..utils import (
|
|||||||
clean_html,
|
clean_html,
|
||||||
dict_get,
|
dict_get,
|
||||||
error_to_compat_str,
|
error_to_compat_str,
|
||||||
|
extract_attributes,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
get_element_by_attribute,
|
get_element_by_attribute,
|
||||||
@ -324,17 +325,18 @@ class YoutubePlaylistBaseInfoExtractor(YoutubeEntryListBaseInfoExtractor):
|
|||||||
for video_id, video_title in self.extract_videos_from_page(content):
|
for video_id, video_title in self.extract_videos_from_page(content):
|
||||||
yield self.url_result(video_id, 'Youtube', video_id, video_title)
|
yield self.url_result(video_id, 'Youtube', video_id, video_title)
|
||||||
|
|
||||||
def extract_videos_from_page(self, page):
|
def extract_videos_from_page_impl(self, video_re, page, ids_in_page, titles_in_page):
|
||||||
ids_in_page = []
|
for mobj in re.finditer(video_re, page):
|
||||||
titles_in_page = []
|
|
||||||
for mobj in re.finditer(self._VIDEO_RE, page):
|
|
||||||
# The link with index 0 is not the first video of the playlist (not sure if still actual)
|
# The link with index 0 is not the first video of the playlist (not sure if still actual)
|
||||||
if 'index' in mobj.groupdict() and mobj.group('id') == '0':
|
if 'index' in mobj.groupdict() and mobj.group('id') == '0':
|
||||||
continue
|
continue
|
||||||
video_id = mobj.group('id')
|
video_id = mobj.group('id')
|
||||||
video_title = unescapeHTML(mobj.group('title'))
|
video_title = unescapeHTML(
|
||||||
|
mobj.group('title')) if 'title' in mobj.groupdict() else None
|
||||||
if video_title:
|
if video_title:
|
||||||
video_title = video_title.strip()
|
video_title = video_title.strip()
|
||||||
|
if video_title == '► Play all':
|
||||||
|
video_title = None
|
||||||
try:
|
try:
|
||||||
idx = ids_in_page.index(video_id)
|
idx = ids_in_page.index(video_id)
|
||||||
if video_title and not titles_in_page[idx]:
|
if video_title and not titles_in_page[idx]:
|
||||||
@ -342,6 +344,12 @@ class YoutubePlaylistBaseInfoExtractor(YoutubeEntryListBaseInfoExtractor):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
ids_in_page.append(video_id)
|
ids_in_page.append(video_id)
|
||||||
titles_in_page.append(video_title)
|
titles_in_page.append(video_title)
|
||||||
|
|
||||||
|
def extract_videos_from_page(self, page):
|
||||||
|
ids_in_page = []
|
||||||
|
titles_in_page = []
|
||||||
|
self.extract_videos_from_page_impl(
|
||||||
|
self._VIDEO_RE, page, ids_in_page, titles_in_page)
|
||||||
return zip(ids_in_page, titles_in_page)
|
return zip(ids_in_page, titles_in_page)
|
||||||
|
|
||||||
|
|
||||||
@ -1595,17 +1603,6 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
video_id = mobj.group(2)
|
video_id = mobj.group(2)
|
||||||
return video_id
|
return video_id
|
||||||
|
|
||||||
def _extract_annotations(self, video_id):
|
|
||||||
return self._download_webpage(
|
|
||||||
'https://www.youtube.com/annotations_invideo', video_id,
|
|
||||||
note='Downloading annotations',
|
|
||||||
errnote='Unable to download video annotations', fatal=False,
|
|
||||||
query={
|
|
||||||
'features': 1,
|
|
||||||
'legacy': 1,
|
|
||||||
'video_id': video_id,
|
|
||||||
})
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _extract_chapters(description, duration):
|
def _extract_chapters(description, duration):
|
||||||
if not description:
|
if not description:
|
||||||
@ -1700,6 +1697,15 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
def extract_token(v_info):
|
def extract_token(v_info):
|
||||||
return dict_get(v_info, ('account_playback_token', 'accountPlaybackToken', 'token'))
|
return dict_get(v_info, ('account_playback_token', 'accountPlaybackToken', 'token'))
|
||||||
|
|
||||||
|
def extract_player_response(player_response, video_id):
|
||||||
|
pl_response = str_or_none(player_response)
|
||||||
|
if not pl_response:
|
||||||
|
return
|
||||||
|
pl_response = self._parse_json(pl_response, video_id, fatal=False)
|
||||||
|
if isinstance(pl_response, dict):
|
||||||
|
add_dash_mpd_pr(pl_response)
|
||||||
|
return pl_response
|
||||||
|
|
||||||
player_response = {}
|
player_response = {}
|
||||||
|
|
||||||
# Get video info
|
# Get video info
|
||||||
@ -1722,7 +1728,10 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
note='Refetching age-gated info webpage',
|
note='Refetching age-gated info webpage',
|
||||||
errnote='unable to download video info webpage')
|
errnote='unable to download video info webpage')
|
||||||
video_info = compat_parse_qs(video_info_webpage)
|
video_info = compat_parse_qs(video_info_webpage)
|
||||||
|
pl_response = video_info.get('player_response', [None])[0]
|
||||||
|
player_response = extract_player_response(pl_response, video_id)
|
||||||
add_dash_mpd(video_info)
|
add_dash_mpd(video_info)
|
||||||
|
view_count = extract_view_count(video_info)
|
||||||
else:
|
else:
|
||||||
age_gate = False
|
age_gate = False
|
||||||
video_info = None
|
video_info = None
|
||||||
@ -1745,11 +1754,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
is_live = True
|
is_live = True
|
||||||
sts = ytplayer_config.get('sts')
|
sts = ytplayer_config.get('sts')
|
||||||
if not player_response:
|
if not player_response:
|
||||||
pl_response = str_or_none(args.get('player_response'))
|
player_response = extract_player_response(args.get('player_response'), video_id)
|
||||||
if pl_response:
|
|
||||||
pl_response = self._parse_json(pl_response, video_id, fatal=False)
|
|
||||||
if isinstance(pl_response, dict):
|
|
||||||
player_response = pl_response
|
|
||||||
if not video_info or self._downloader.params.get('youtube_include_dash_manifest', True):
|
if not video_info or self._downloader.params.get('youtube_include_dash_manifest', True):
|
||||||
add_dash_mpd_pr(player_response)
|
add_dash_mpd_pr(player_response)
|
||||||
# We also try looking in get_video_info since it may contain different dashmpd
|
# We also try looking in get_video_info since it may contain different dashmpd
|
||||||
@ -1781,9 +1786,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
get_video_info = compat_parse_qs(video_info_webpage)
|
get_video_info = compat_parse_qs(video_info_webpage)
|
||||||
if not player_response:
|
if not player_response:
|
||||||
pl_response = get_video_info.get('player_response', [None])[0]
|
pl_response = get_video_info.get('player_response', [None])[0]
|
||||||
if isinstance(pl_response, dict):
|
player_response = extract_player_response(pl_response, video_id)
|
||||||
player_response = pl_response
|
|
||||||
add_dash_mpd_pr(player_response)
|
|
||||||
add_dash_mpd(get_video_info)
|
add_dash_mpd(get_video_info)
|
||||||
if view_count is None:
|
if view_count is None:
|
||||||
view_count = extract_view_count(get_video_info)
|
view_count = extract_view_count(get_video_info)
|
||||||
@ -1806,9 +1809,15 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
break
|
break
|
||||||
|
|
||||||
def extract_unavailable_message():
|
def extract_unavailable_message():
|
||||||
return self._html_search_regex(
|
messages = []
|
||||||
r'(?s)<h1[^>]+id="unavailable-message"[^>]*>(.+?)</h1>',
|
for tag, kind in (('h1', 'message'), ('div', 'submessage')):
|
||||||
video_webpage, 'unavailable message', default=None)
|
msg = self._html_search_regex(
|
||||||
|
r'(?s)<{tag}[^>]+id=["\']unavailable-{kind}["\'][^>]*>(.+?)</{tag}>'.format(tag=tag, kind=kind),
|
||||||
|
video_webpage, 'unavailable %s' % kind, default=None)
|
||||||
|
if msg:
|
||||||
|
messages.append(msg)
|
||||||
|
if messages:
|
||||||
|
return '\n'.join(messages)
|
||||||
|
|
||||||
if not video_info:
|
if not video_info:
|
||||||
unavailable_message = extract_unavailable_message()
|
unavailable_message = extract_unavailable_message()
|
||||||
@ -2092,9 +2101,14 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
a_format.setdefault('http_headers', {})['Youtubedl-no-compression'] = 'True'
|
a_format.setdefault('http_headers', {})['Youtubedl-no-compression'] = 'True'
|
||||||
formats.append(a_format)
|
formats.append(a_format)
|
||||||
else:
|
else:
|
||||||
error_message = clean_html(video_info.get('reason', [None])[0])
|
error_message = extract_unavailable_message()
|
||||||
if not error_message:
|
if not error_message:
|
||||||
error_message = extract_unavailable_message()
|
error_message = clean_html(try_get(
|
||||||
|
player_response, lambda x: x['playabilityStatus']['reason'],
|
||||||
|
compat_str))
|
||||||
|
if not error_message:
|
||||||
|
error_message = clean_html(
|
||||||
|
try_get(video_info, lambda x: x['reason'][0], compat_str))
|
||||||
if error_message:
|
if error_message:
|
||||||
raise ExtractorError(error_message, expected=True)
|
raise ExtractorError(error_message, expected=True)
|
||||||
raise ExtractorError('no conn, hlsvp, hlsManifestUrl or url_encoded_fmt_stream_map information found in video info')
|
raise ExtractorError('no conn, hlsvp, hlsManifestUrl or url_encoded_fmt_stream_map information found in video info')
|
||||||
@ -2265,7 +2279,21 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
# annotations
|
# annotations
|
||||||
video_annotations = None
|
video_annotations = None
|
||||||
if self._downloader.params.get('writeannotations', False):
|
if self._downloader.params.get('writeannotations', False):
|
||||||
video_annotations = self._extract_annotations(video_id)
|
xsrf_token = self._search_regex(
|
||||||
|
r'([\'"])XSRF_TOKEN\1\s*:\s*([\'"])(?P<xsrf_token>[A-Za-z0-9+/=]+)\2',
|
||||||
|
video_webpage, 'xsrf token', group='xsrf_token', fatal=False)
|
||||||
|
invideo_url = try_get(
|
||||||
|
player_response, lambda x: x['annotations'][0]['playerAnnotationsUrlsRenderer']['invideoUrl'], compat_str)
|
||||||
|
if xsrf_token and invideo_url:
|
||||||
|
xsrf_field_name = self._search_regex(
|
||||||
|
r'([\'"])XSRF_FIELD_NAME\1\s*:\s*([\'"])(?P<xsrf_field_name>\w+)\2',
|
||||||
|
video_webpage, 'xsrf field name',
|
||||||
|
group='xsrf_field_name', default='session_token')
|
||||||
|
video_annotations = self._download_webpage(
|
||||||
|
self._proto_relative_url(invideo_url),
|
||||||
|
video_id, note='Downloading annotations',
|
||||||
|
errnote='Unable to download video annotations', fatal=False,
|
||||||
|
data=urlencode_postdata({xsrf_field_name: xsrf_token}))
|
||||||
|
|
||||||
chapters = self._extract_chapters(description_original, video_duration)
|
chapters = self._extract_chapters(description_original, video_duration)
|
||||||
|
|
||||||
@ -2423,7 +2451,8 @@ class YoutubePlaylistIE(YoutubePlaylistBaseInfoExtractor):
|
|||||||
(%(playlist_id)s)
|
(%(playlist_id)s)
|
||||||
)""" % {'playlist_id': YoutubeBaseInfoExtractor._PLAYLIST_ID_RE}
|
)""" % {'playlist_id': YoutubeBaseInfoExtractor._PLAYLIST_ID_RE}
|
||||||
_TEMPLATE_URL = 'https://www.youtube.com/playlist?list=%s'
|
_TEMPLATE_URL = 'https://www.youtube.com/playlist?list=%s'
|
||||||
_VIDEO_RE = r'href="\s*/watch\?v=(?P<id>[0-9A-Za-z_-]{11})(?:&(?:[^"]*?index=(?P<index>\d+))?(?:[^>]+>(?P<title>[^<]+))?)?'
|
_VIDEO_RE_TPL = r'href="\s*/watch\?v=%s(?:&(?:[^"]*?index=(?P<index>\d+))?(?:[^>]+>(?P<title>[^<]+))?)?'
|
||||||
|
_VIDEO_RE = _VIDEO_RE_TPL % r'(?P<id>[0-9A-Za-z_-]{11})'
|
||||||
IE_NAME = 'youtube:playlist'
|
IE_NAME = 'youtube:playlist'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'https://www.youtube.com/playlist?list=PLwiyx1dc3P2JR9N8gQaQN_BCvlSlap7re',
|
'url': 'https://www.youtube.com/playlist?list=PLwiyx1dc3P2JR9N8gQaQN_BCvlSlap7re',
|
||||||
@ -2588,6 +2617,34 @@ class YoutubePlaylistIE(YoutubePlaylistBaseInfoExtractor):
|
|||||||
def _real_initialize(self):
|
def _real_initialize(self):
|
||||||
self._login()
|
self._login()
|
||||||
|
|
||||||
|
def extract_videos_from_page(self, page):
|
||||||
|
ids_in_page = []
|
||||||
|
titles_in_page = []
|
||||||
|
|
||||||
|
for item in re.findall(
|
||||||
|
r'(<[^>]*\bdata-video-id\s*=\s*["\'][0-9A-Za-z_-]{11}[^>]+>)', page):
|
||||||
|
attrs = extract_attributes(item)
|
||||||
|
video_id = attrs['data-video-id']
|
||||||
|
video_title = unescapeHTML(attrs.get('data-title'))
|
||||||
|
if video_title:
|
||||||
|
video_title = video_title.strip()
|
||||||
|
ids_in_page.append(video_id)
|
||||||
|
titles_in_page.append(video_title)
|
||||||
|
|
||||||
|
# Fallback with old _VIDEO_RE
|
||||||
|
self.extract_videos_from_page_impl(
|
||||||
|
self._VIDEO_RE, page, ids_in_page, titles_in_page)
|
||||||
|
|
||||||
|
# Relaxed fallbacks
|
||||||
|
self.extract_videos_from_page_impl(
|
||||||
|
r'href="\s*/watch\?v\s*=\s*(?P<id>[0-9A-Za-z_-]{11})', page,
|
||||||
|
ids_in_page, titles_in_page)
|
||||||
|
self.extract_videos_from_page_impl(
|
||||||
|
r'data-video-ids\s*=\s*["\'](?P<id>[0-9A-Za-z_-]{11})', page,
|
||||||
|
ids_in_page, titles_in_page)
|
||||||
|
|
||||||
|
return zip(ids_in_page, titles_in_page)
|
||||||
|
|
||||||
def _extract_mix(self, playlist_id):
|
def _extract_mix(self, playlist_id):
|
||||||
# The mixes are generated from a single video
|
# The mixes are generated from a single video
|
||||||
# the id of the playlist is just 'RD' + video_id
|
# the id of the playlist is just 'RD' + video_id
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
__version__ = '2019.07.30'
|
__version__ = '2019.08.13'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user