Compare commits
19 Commits
2017.12.31
...
totalwebca
Author | SHA1 | Date | |
---|---|---|---|
![]() |
97bc05116e | ||
![]() |
0a5b1295b7 | ||
![]() |
a133eb7764 | ||
![]() |
f12628f934 | ||
![]() |
45283afdec | ||
![]() |
b7c74c0403 | ||
![]() |
0b0870f9d0 | ||
![]() |
c2f18e1c49 | ||
![]() |
da35331c6c | ||
![]() |
de329f64ab | ||
![]() |
75ba0efb52 | ||
![]() |
f0c6c2bce2 | ||
![]() |
9650c3e91d | ||
![]() |
b5e531f31a | ||
![]() |
7a6c204fcb | ||
![]() |
d7cd9a9e84 | ||
![]() |
54009c246e | ||
![]() |
b300cda476 | ||
![]() |
7608a91ee7 |
13
.travis.yml
13
.travis.yml
@@ -7,16 +7,21 @@ python:
|
|||||||
- "3.4"
|
- "3.4"
|
||||||
- "3.5"
|
- "3.5"
|
||||||
- "3.6"
|
- "3.6"
|
||||||
|
- "pypy"
|
||||||
|
- "pypy3"
|
||||||
sudo: false
|
sudo: false
|
||||||
env:
|
env:
|
||||||
- YTDL_TEST_SET=core
|
- YTDL_TEST_SET=core
|
||||||
- YTDL_TEST_SET=download
|
- YTDL_TEST_SET=download
|
||||||
matrix:
|
matrix:
|
||||||
|
include:
|
||||||
|
- env: JYTHON=true; YTDL_TEST_SET=core
|
||||||
|
- env: JYTHON=true; YTDL_TEST_SET=download
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- env: YTDL_TEST_SET=download
|
- env: YTDL_TEST_SET=download
|
||||||
|
- env: JYTHON=true; YTDL_TEST_SET=core
|
||||||
|
- env: JYTHON=true; YTDL_TEST_SET=download
|
||||||
|
before_install:
|
||||||
|
- if [ "$JYTHON" == "true" ]; then ./devscripts/install_jython.sh; export PATH="$HOME/jython/bin:$PATH"; fi
|
||||||
script: ./devscripts/run_tests.sh
|
script: ./devscripts/run_tests.sh
|
||||||
notifications:
|
|
||||||
email:
|
|
||||||
- filippo.valsorda@gmail.com
|
|
||||||
- yasoob.khld@gmail.com
|
|
||||||
|
@@ -1,3 +1,11 @@
|
|||||||
|
version <unreleased>
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [youku] Fix list extraction (#15135)
|
||||||
|
* [openload] Fix extraction (#15166)
|
||||||
|
* [rtve.es:alacarta] Fix extraction of some new URLs
|
||||||
|
|
||||||
|
|
||||||
version 2017.12.31
|
version 2017.12.31
|
||||||
|
|
||||||
Core
|
Core
|
||||||
|
5
devscripts/install_jython.sh
Executable file
5
devscripts/install_jython.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
wget http://central.maven.org/maven2/org/python/jython-installer/2.7.1/jython-installer-2.7.1.jar
|
||||||
|
java -jar jython-installer-2.7.1.jar -s -d "$HOME/jython"
|
||||||
|
$HOME/jython/bin/jython -m pip install nose
|
@@ -2233,8 +2233,16 @@ class YoutubeDL(object):
|
|||||||
sys.exc_clear()
|
sys.exc_clear()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
self._write_string('[debug] Python version %s - %s\n' % (
|
|
||||||
platform.python_version(), platform_name()))
|
def python_implementation():
|
||||||
|
impl_name = platform.python_implementation()
|
||||||
|
if impl_name == 'PyPy' and hasattr(sys, 'pypy_version_info'):
|
||||||
|
return impl_name + ' version %d.%d.%d' % sys.pypy_version_info[:3]
|
||||||
|
return impl_name
|
||||||
|
|
||||||
|
self._write_string('[debug] Python version %s (%s) - %s\n' % (
|
||||||
|
platform.python_version(), python_implementation(),
|
||||||
|
platform_name()))
|
||||||
|
|
||||||
exe_versions = FFmpegPostProcessor.get_versions(self)
|
exe_versions = FFmpegPostProcessor.get_versions(self)
|
||||||
exe_versions['rtmpdump'] = rtmpdump_version()
|
exe_versions['rtmpdump'] = rtmpdump_version()
|
||||||
|
@@ -3,12 +3,14 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import binascii
|
import binascii
|
||||||
import collections
|
import collections
|
||||||
|
import ctypes
|
||||||
import email
|
import email
|
||||||
import getpass
|
import getpass
|
||||||
import io
|
import io
|
||||||
import itertools
|
import itertools
|
||||||
import optparse
|
import optparse
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import shutil
|
import shutil
|
||||||
@@ -2906,6 +2908,24 @@ except ImportError: # not 2.6+ or is 3.x
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
compat_zip = zip
|
compat_zip = zip
|
||||||
|
|
||||||
|
if platform.python_implementation() == 'PyPy' and sys.pypy_version_info < (5, 4, 0):
|
||||||
|
# PyPy2 prior to version 5.4.0 expects byte strings as Windows function
|
||||||
|
# names, see the original PyPy issue [1] and the youtube-dl one [2].
|
||||||
|
# 1. https://bitbucket.org/pypy/pypy/issues/2360/windows-ctypescdll-typeerror-function-name
|
||||||
|
# 2. https://github.com/rg3/youtube-dl/pull/4392
|
||||||
|
def compat_ctypes_WINFUNCTYPE(*args, **kwargs):
|
||||||
|
real = ctypes.WINFUNCTYPE(*args, **kwargs)
|
||||||
|
|
||||||
|
def resf(tpl, *args, **kwargs):
|
||||||
|
funcname, dll = tpl
|
||||||
|
return real((str(funcname), dll), *args, **kwargs)
|
||||||
|
|
||||||
|
return resf
|
||||||
|
else:
|
||||||
|
def compat_ctypes_WINFUNCTYPE(*args, **kwargs):
|
||||||
|
return ctypes.WINFUNCTYPE(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'compat_HTMLParseError',
|
'compat_HTMLParseError',
|
||||||
'compat_HTMLParser',
|
'compat_HTMLParser',
|
||||||
@@ -2914,6 +2934,7 @@ __all__ = [
|
|||||||
'compat_chr',
|
'compat_chr',
|
||||||
'compat_cookiejar',
|
'compat_cookiejar',
|
||||||
'compat_cookies',
|
'compat_cookies',
|
||||||
|
'compat_ctypes_WINFUNCTYPE',
|
||||||
'compat_etree_fromstring',
|
'compat_etree_fromstring',
|
||||||
'compat_etree_register_namespace',
|
'compat_etree_register_namespace',
|
||||||
'compat_expanduser',
|
'compat_expanduser',
|
||||||
|
@@ -8,7 +8,7 @@ from .common import InfoExtractor
|
|||||||
from ..compat import compat_str
|
from ..compat import compat_str
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_iso8601,
|
unified_timestamp,
|
||||||
OnDemandPagedList,
|
OnDemandPagedList,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ class ACastIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
# test with multiple blings
|
# test with multiple blings
|
||||||
'url': 'https://www.acast.com/sparpodcast/2.raggarmordet-rosterurdetforflutna',
|
'url': 'https://www.acast.com/sparpodcast/2.raggarmordet-rosterurdetforflutna',
|
||||||
'md5': '55c0097badd7095f494c99a172f86501',
|
'md5': 'e87d5b8516cd04c0d81b6ee1caca28d0',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '2a92b283-1a75-4ad8-8396-499c641de0d9',
|
'id': '2a92b283-1a75-4ad8-8396-499c641de0d9',
|
||||||
'ext': 'mp3',
|
'ext': 'mp3',
|
||||||
@@ -40,23 +40,24 @@ class ACastIE(InfoExtractor):
|
|||||||
'timestamp': 1477346700,
|
'timestamp': 1477346700,
|
||||||
'upload_date': '20161024',
|
'upload_date': '20161024',
|
||||||
'description': 'md5:4f81f6d8cf2e12ee21a321d8bca32db4',
|
'description': 'md5:4f81f6d8cf2e12ee21a321d8bca32db4',
|
||||||
'duration': 2797,
|
'duration': 2766,
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
channel, display_id = re.match(self._VALID_URL, url).groups()
|
channel, display_id = re.match(self._VALID_URL, url).groups()
|
||||||
cast_data = self._download_json(
|
cast_data = self._download_json(
|
||||||
'https://embed.acast.com/api/acasts/%s/%s' % (channel, display_id), display_id)
|
'https://play-api.acast.com/splash/%s/%s' % (channel, display_id), display_id)
|
||||||
|
e = cast_data['result']['episode']
|
||||||
return {
|
return {
|
||||||
'id': compat_str(cast_data['id']),
|
'id': compat_str(e['id']),
|
||||||
'display_id': display_id,
|
'display_id': display_id,
|
||||||
'url': [b['audio'] for b in cast_data['blings'] if b['type'] == 'BlingAudio'][0],
|
'url': e['mediaUrl'],
|
||||||
'title': cast_data['name'],
|
'title': e['name'],
|
||||||
'description': cast_data.get('description'),
|
'description': e.get('description'),
|
||||||
'thumbnail': cast_data.get('image'),
|
'thumbnail': e.get('image'),
|
||||||
'timestamp': parse_iso8601(cast_data.get('publishingDate')),
|
'timestamp': unified_timestamp(e.get('publishingDate')),
|
||||||
'duration': int_or_none(cast_data.get('duration')),
|
'duration': int_or_none(e.get('duration')),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -609,7 +609,10 @@ from .mofosex import MofosexIE
|
|||||||
from .mojvideo import MojvideoIE
|
from .mojvideo import MojvideoIE
|
||||||
from .moniker import MonikerIE
|
from .moniker import MonikerIE
|
||||||
from .morningstar import MorningstarIE
|
from .morningstar import MorningstarIE
|
||||||
from .motherless import MotherlessIE
|
from .motherless import (
|
||||||
|
MotherlessIE,
|
||||||
|
MotherlessGroupIE
|
||||||
|
)
|
||||||
from .motorsport import MotorsportIE
|
from .motorsport import MotorsportIE
|
||||||
from .movieclips import MovieClipsIE
|
from .movieclips import MovieClipsIE
|
||||||
from .moviezine import MoviezineIE
|
from .moviezine import MoviezineIE
|
||||||
@@ -1065,6 +1068,7 @@ from .tnaflix import (
|
|||||||
from .toggle import ToggleIE
|
from .toggle import ToggleIE
|
||||||
from .tonline import TOnlineIE
|
from .tonline import TOnlineIE
|
||||||
from .toongoggles import ToonGogglesIE
|
from .toongoggles import ToonGogglesIE
|
||||||
|
from .totalwebcasting import TotalWebCastingIE
|
||||||
from .toutv import TouTvIE
|
from .toutv import TouTvIE
|
||||||
from .toypics import ToypicsUserIE, ToypicsIE
|
from .toypics import ToypicsUserIE, ToypicsIE
|
||||||
from .traileraddict import TrailerAddictIE
|
from .traileraddict import TrailerAddictIE
|
||||||
|
@@ -94,7 +94,15 @@ class LyndaBaseIE(InfoExtractor):
|
|||||||
class LyndaIE(LyndaBaseIE):
|
class LyndaIE(LyndaBaseIE):
|
||||||
IE_NAME = 'lynda'
|
IE_NAME = 'lynda'
|
||||||
IE_DESC = 'lynda.com videos'
|
IE_DESC = 'lynda.com videos'
|
||||||
_VALID_URL = r'https?://(?:www\.)?(?:lynda\.com|educourse\.ga)/(?:[^/]+/[^/]+/(?P<course_id>\d+)|player/embed)/(?P<id>\d+)'
|
_VALID_URL = r'''(?x)
|
||||||
|
https?://
|
||||||
|
(?:www\.)?(?:lynda\.com|educourse\.ga)/
|
||||||
|
(?:
|
||||||
|
(?:[^/]+/){2,3}(?P<course_id>\d+)|
|
||||||
|
player/embed
|
||||||
|
)/
|
||||||
|
(?P<id>\d+)
|
||||||
|
'''
|
||||||
|
|
||||||
_TIMECODE_REGEX = r'\[(?P<timecode>\d+:\d+:\d+[\.,]\d+)\]'
|
_TIMECODE_REGEX = r'\[(?P<timecode>\d+:\d+:\d+[\.,]\d+)\]'
|
||||||
|
|
||||||
@@ -113,6 +121,9 @@ class LyndaIE(LyndaBaseIE):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'https://educourse.ga/Bootstrap-tutorials/Using-exercise-files/110885/114408-4.html',
|
'url': 'https://educourse.ga/Bootstrap-tutorials/Using-exercise-files/110885/114408-4.html',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.lynda.com/de/Graphic-Design-tutorials/Willkommen-Grundlagen-guten-Gestaltung/393570/393572-4.html',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _raise_unavailable(self, video_id):
|
def _raise_unavailable(self, video_id):
|
||||||
@@ -244,8 +255,9 @@ class LyndaIE(LyndaBaseIE):
|
|||||||
def _get_subtitles(self, video_id):
|
def _get_subtitles(self, video_id):
|
||||||
url = 'https://www.lynda.com/ajax/player?videoId=%s&type=transcript' % video_id
|
url = 'https://www.lynda.com/ajax/player?videoId=%s&type=transcript' % video_id
|
||||||
subs = self._download_json(url, None, False)
|
subs = self._download_json(url, None, False)
|
||||||
if subs:
|
fixed_subs = self._fix_subtitles(subs)
|
||||||
return {'en': [{'ext': 'srt', 'data': self._fix_subtitles(subs)}]}
|
if fixed_subs:
|
||||||
|
return {'en': [{'ext': 'srt', 'data': fixed_subs}]}
|
||||||
else:
|
else:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@@ -256,7 +268,15 @@ class LyndaCourseIE(LyndaBaseIE):
|
|||||||
|
|
||||||
# Course link equals to welcome/introduction video link of same course
|
# Course link equals to welcome/introduction video link of same course
|
||||||
# We will recognize it as course link
|
# We will recognize it as course link
|
||||||
_VALID_URL = r'https?://(?:www|m)\.(?:lynda\.com|educourse\.ga)/(?P<coursepath>[^/]+/[^/]+/(?P<courseid>\d+))-\d\.html'
|
_VALID_URL = r'https?://(?:www|m)\.(?:lynda\.com|educourse\.ga)/(?P<coursepath>(?:[^/]+/){2,3}(?P<courseid>\d+))-2\.html'
|
||||||
|
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://www.lynda.com/Graphic-Design-tutorials/Grundlagen-guten-Gestaltung/393570-2.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.lynda.com/de/Graphic-Design-tutorials/Grundlagen-guten-Gestaltung/393570-2.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from .ooyala import OoyalaIE
|
from .ooyala import OoyalaIE
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_str,
|
compat_str,
|
||||||
compat_urllib_parse_urlencode,
|
|
||||||
compat_urlparse,
|
compat_urlparse,
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
@@ -42,31 +42,33 @@ class MiTeleBaseIE(InfoExtractor):
|
|||||||
duration = int_or_none(mmc.get('duration'))
|
duration = int_or_none(mmc.get('duration'))
|
||||||
for location in mmc['locations']:
|
for location in mmc['locations']:
|
||||||
gat = self._proto_relative_url(location.get('gat'), 'http:')
|
gat = self._proto_relative_url(location.get('gat'), 'http:')
|
||||||
bas = location.get('bas')
|
gcp = location.get('gcp')
|
||||||
loc = location.get('loc')
|
|
||||||
ogn = location.get('ogn')
|
ogn = location.get('ogn')
|
||||||
if None in (gat, bas, loc, ogn):
|
if None in (gat, gcp, ogn):
|
||||||
continue
|
continue
|
||||||
token_data = {
|
token_data = {
|
||||||
'bas': bas,
|
'gcp': gcp,
|
||||||
'icd': loc,
|
|
||||||
'ogn': ogn,
|
'ogn': ogn,
|
||||||
'sta': '0',
|
'sta': 0,
|
||||||
}
|
}
|
||||||
media = self._download_json(
|
media = self._download_json(
|
||||||
'%s/?%s' % (gat, compat_urllib_parse_urlencode(token_data)),
|
gat, video_id, data=json.dumps(token_data).encode('utf-8'),
|
||||||
video_id, 'Downloading %s JSON' % location['loc'])
|
headers={
|
||||||
file_ = media.get('file')
|
'Content-Type': 'application/json;charset=utf-8',
|
||||||
if not file_:
|
'Referer': url,
|
||||||
|
})
|
||||||
|
stream = media.get('stream') or media.get('file')
|
||||||
|
if not stream:
|
||||||
continue
|
continue
|
||||||
ext = determine_ext(file_)
|
ext = determine_ext(stream)
|
||||||
if ext == 'f4m':
|
if ext == 'f4m':
|
||||||
formats.extend(self._extract_f4m_formats(
|
formats.extend(self._extract_f4m_formats(
|
||||||
file_ + '&hdcore=3.2.0&plugin=aasp-3.2.0.77.18',
|
stream + '&hdcore=3.2.0&plugin=aasp-3.2.0.77.18',
|
||||||
video_id, f4m_id='hds', fatal=False))
|
video_id, f4m_id='hds', fatal=False))
|
||||||
elif ext == 'm3u8':
|
elif ext == 'm3u8':
|
||||||
formats.extend(self._extract_m3u8_formats(
|
formats.extend(self._extract_m3u8_formats(
|
||||||
file_, video_id, 'mp4', 'm3u8_native', m3u8_id='hls', fatal=False))
|
stream, video_id, 'mp4', 'm3u8_native',
|
||||||
|
m3u8_id='hls', fatal=False))
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -4,8 +4,11 @@ import datetime
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..compat import compat_urlparse
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
|
InAdvancePagedList,
|
||||||
|
orderedSet,
|
||||||
str_to_int,
|
str_to_int,
|
||||||
unified_strdate,
|
unified_strdate,
|
||||||
)
|
)
|
||||||
@@ -114,3 +117,86 @@ class MotherlessIE(InfoExtractor):
|
|||||||
'age_limit': age_limit,
|
'age_limit': age_limit,
|
||||||
'url': video_url,
|
'url': video_url,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MotherlessGroupIE(InfoExtractor):
|
||||||
|
_VALID_URL = 'https?://(?:www\.)?motherless\.com/gv?/(?P<id>[a-z0-9_]+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'http://motherless.com/g/movie_scenes',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'movie_scenes',
|
||||||
|
'title': 'Movie Scenes',
|
||||||
|
'description': 'Hot and sexy scenes from "regular" movies... '
|
||||||
|
'Beautiful actresses fully nude... A looot of '
|
||||||
|
'skin! :)Enjoy!',
|
||||||
|
},
|
||||||
|
'playlist_mincount': 662,
|
||||||
|
}, {
|
||||||
|
'url': 'http://motherless.com/gv/sex_must_be_funny',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'sex_must_be_funny',
|
||||||
|
'title': 'Sex must be funny',
|
||||||
|
'description': 'Sex can be funny. Wide smiles,laugh, games, fun of '
|
||||||
|
'any kind!'
|
||||||
|
},
|
||||||
|
'playlist_mincount': 9,
|
||||||
|
}]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def suitable(cls, url):
|
||||||
|
return (False if MotherlessIE.suitable(url)
|
||||||
|
else super(MotherlessGroupIE, cls).suitable(url))
|
||||||
|
|
||||||
|
def _extract_entries(self, webpage, base):
|
||||||
|
entries = []
|
||||||
|
for mobj in re.finditer(
|
||||||
|
r'href="(?P<href>/[^"]+)"[^>]*>(?:\s*<img[^>]+alt="[^-]+-\s(?P<title>[^"]+)")?',
|
||||||
|
webpage):
|
||||||
|
video_url = compat_urlparse.urljoin(base, mobj.group('href'))
|
||||||
|
if not MotherlessIE.suitable(video_url):
|
||||||
|
continue
|
||||||
|
video_id = MotherlessIE._match_id(video_url)
|
||||||
|
title = mobj.group('title')
|
||||||
|
entries.append(self.url_result(
|
||||||
|
video_url, ie=MotherlessIE.ie_key(), video_id=video_id,
|
||||||
|
video_title=title))
|
||||||
|
# Alternative fallback
|
||||||
|
if not entries:
|
||||||
|
entries = [
|
||||||
|
self.url_result(
|
||||||
|
compat_urlparse.urljoin(base, '/' + video_id),
|
||||||
|
ie=MotherlessIE.ie_key(), video_id=video_id)
|
||||||
|
for video_id in orderedSet(re.findall(
|
||||||
|
r'data-codename=["\']([A-Z0-9]+)', webpage))]
|
||||||
|
return entries
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
group_id = self._match_id(url)
|
||||||
|
page_url = compat_urlparse.urljoin(url, '/gv/%s' % group_id)
|
||||||
|
webpage = self._download_webpage(page_url, group_id)
|
||||||
|
title = self._search_regex(
|
||||||
|
r'<title>([\w\s]+\w)\s+-', webpage, 'title', fatal=False)
|
||||||
|
description = self._html_search_meta(
|
||||||
|
'description', webpage, fatal=False)
|
||||||
|
page_count = self._int(self._search_regex(
|
||||||
|
r'(\d+)</(?:a|span)><(?:a|span)[^>]+>\s*NEXT',
|
||||||
|
webpage, 'page_count'), 'page_count')
|
||||||
|
PAGE_SIZE = 80
|
||||||
|
|
||||||
|
def _get_page(idx):
|
||||||
|
webpage = self._download_webpage(
|
||||||
|
page_url, group_id, query={'page': idx + 1},
|
||||||
|
note='Downloading page %d/%d' % (idx + 1, page_count)
|
||||||
|
)
|
||||||
|
for entry in self._extract_entries(webpage, url):
|
||||||
|
yield entry
|
||||||
|
|
||||||
|
playlist = InAdvancePagedList(_get_page, page_count, PAGE_SIZE)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'_type': 'playlist',
|
||||||
|
'id': group_id,
|
||||||
|
'title': title,
|
||||||
|
'description': description,
|
||||||
|
'entries': playlist
|
||||||
|
}
|
||||||
|
@@ -333,7 +333,11 @@ class OpenloadIE(InfoExtractor):
|
|||||||
webpage, _ = phantom.get(page_url, html=webpage, video_id=video_id, headers=headers)
|
webpage, _ = phantom.get(page_url, html=webpage, video_id=video_id, headers=headers)
|
||||||
|
|
||||||
decoded_id = (get_element_by_id('streamurl', webpage) or
|
decoded_id = (get_element_by_id('streamurl', webpage) or
|
||||||
get_element_by_id('streamuri', webpage))
|
get_element_by_id('streamuri', webpage) or
|
||||||
|
get_element_by_id('streamurj', webpage))
|
||||||
|
|
||||||
|
if not decoded_id:
|
||||||
|
raise ExtractorError('Can\'t find stream URL', video_id=video_id)
|
||||||
|
|
||||||
video_url = 'https://openload.co/stream/%s?mime=true' % decoded_id
|
video_url = 'https://openload.co/stream/%s?mime=true' % decoded_id
|
||||||
|
|
||||||
|
@@ -31,6 +31,9 @@ def _decrypt_url(png):
|
|||||||
hash_index = data.index('#')
|
hash_index = data.index('#')
|
||||||
alphabet_data = data[:hash_index]
|
alphabet_data = data[:hash_index]
|
||||||
url_data = data[hash_index + 1:]
|
url_data = data[hash_index + 1:]
|
||||||
|
if url_data[0] == 'H' and url_data[3] == '%':
|
||||||
|
# remove useless HQ%% at the start
|
||||||
|
url_data = url_data[4:]
|
||||||
|
|
||||||
alphabet = []
|
alphabet = []
|
||||||
e = 0
|
e = 0
|
||||||
|
@@ -136,6 +136,25 @@ class SoundcloudIE(InfoExtractor):
|
|||||||
'license': 'all-rights-reserved',
|
'license': 'all-rights-reserved',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
# no album art, use avatar pic for thumbnail
|
||||||
|
{
|
||||||
|
'url': 'https://soundcloud.com/garyvee/sideways-prod-mad-real',
|
||||||
|
'md5': '59c7872bc44e5d99b7211891664760c2',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '309699954',
|
||||||
|
'ext': 'mp3',
|
||||||
|
'title': 'Sideways (Prod. Mad Real)',
|
||||||
|
'description': 'md5:d41d8cd98f00b204e9800998ecf8427e',
|
||||||
|
'uploader': 'garyvee',
|
||||||
|
'upload_date': '20170226',
|
||||||
|
'duration': 207,
|
||||||
|
'thumbnail': r're:https?://.*\.jpg',
|
||||||
|
'license': 'all-rights-reserved',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
_CLIENT_ID = 'c6CU49JDMapyrQo06UxU9xouB9ZVzqCn'
|
_CLIENT_ID = 'c6CU49JDMapyrQo06UxU9xouB9ZVzqCn'
|
||||||
@@ -160,7 +179,7 @@ class SoundcloudIE(InfoExtractor):
|
|||||||
name = full_title or track_id
|
name = full_title or track_id
|
||||||
if quiet:
|
if quiet:
|
||||||
self.report_extraction(name)
|
self.report_extraction(name)
|
||||||
thumbnail = info.get('artwork_url')
|
thumbnail = info.get('artwork_url') or info.get('user', {}).get('avatar_url')
|
||||||
if isinstance(thumbnail, compat_str):
|
if isinstance(thumbnail, compat_str):
|
||||||
thumbnail = thumbnail.replace('-large', '-t500x500')
|
thumbnail = thumbnail.replace('-large', '-t500x500')
|
||||||
ext = 'mp3'
|
ext = 'mp3'
|
||||||
|
50
youtube_dl/extractor/totalwebcasting.py
Normal file
50
youtube_dl/extractor/totalwebcasting.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
|
||||||
|
|
||||||
|
class TotalWebCastingIE(InfoExtractor):
|
||||||
|
IE_NAME = 'totalwebcasting.com'
|
||||||
|
_VALID_URL = r'https?://www\.totalwebcasting\.com/view/\?func=VOFF.*'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'https://www.totalwebcasting.com/view/?func=VOFF&id=columbia&date=2017-01-04&seq=1',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '270e1c415d443924485f547403180906731570466a42740764673853041316737548',
|
||||||
|
'title': 'Real World Cryptography Conference 2017',
|
||||||
|
'description': 'md5:47a31e91ed537a2bb0d3a091659dc80c',
|
||||||
|
},
|
||||||
|
'playlist_count': 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
params = url.split('?', 1)[1]
|
||||||
|
webpage = self._download_webpage(url, params)
|
||||||
|
aprm = self._search_regex(r"startVideo\('(\w+)'", webpage, 'aprm')
|
||||||
|
VLEV = self._download_json("https://www.totalwebcasting.com/view/?func=VLEV&aprm=%s&style=G" % aprm, aprm)
|
||||||
|
parts = []
|
||||||
|
for s in VLEV["aiTimes"].values():
|
||||||
|
n = int(s[:-5])
|
||||||
|
if n == 99:
|
||||||
|
continue
|
||||||
|
if n not in parts:
|
||||||
|
parts.append(n)
|
||||||
|
parts.sort()
|
||||||
|
title = VLEV["title"]
|
||||||
|
entries = []
|
||||||
|
for p in parts:
|
||||||
|
VLEV = self._download_json("https://www.totalwebcasting.com/view/?func=VLEV&aprm=%s&style=G&refP=1&nf=%d&time=1&cs=1&ns=1" % (aprm, p), aprm)
|
||||||
|
for s in VLEV["playerObj"]["clip"]["sources"]:
|
||||||
|
if s["type"] != "video/mp4":
|
||||||
|
continue
|
||||||
|
entries.append({
|
||||||
|
"id": "%s_part%d" % (aprm, p),
|
||||||
|
"url": "https:" + s["src"],
|
||||||
|
"title": title,
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
'_type': 'multi_video',
|
||||||
|
'id': aprm,
|
||||||
|
'entries': entries,
|
||||||
|
'title': title,
|
||||||
|
'description': VLEV.get("desc"),
|
||||||
|
}
|
@@ -358,9 +358,16 @@ class TwitchPlaylistBaseIE(TwitchBaseIE):
|
|||||||
break
|
break
|
||||||
offset += limit
|
offset += limit
|
||||||
return self.playlist_result(
|
return self.playlist_result(
|
||||||
[self.url_result(entry) for entry in orderedSet(entries)],
|
[self._make_url_result(entry) for entry in orderedSet(entries)],
|
||||||
channel_id, channel_name)
|
channel_id, channel_name)
|
||||||
|
|
||||||
|
def _make_url_result(self, url):
|
||||||
|
try:
|
||||||
|
video_id = 'v%s' % TwitchVodIE._match_id(url)
|
||||||
|
return self.url_result(url, TwitchVodIE.ie_key(), video_id=video_id)
|
||||||
|
except AssertionError:
|
||||||
|
return self.url_result(url)
|
||||||
|
|
||||||
def _extract_playlist_page(self, response):
|
def _extract_playlist_page(self, response):
|
||||||
videos = response.get('videos')
|
videos = response.get('videos')
|
||||||
return [video['url'] for video in videos] if videos else []
|
return [video['url'] for video in videos] if videos else []
|
||||||
|
@@ -245,13 +245,19 @@ class YoukuShowIE(InfoExtractor):
|
|||||||
# No data-id value.
|
# No data-id value.
|
||||||
'url': 'http://list.youku.com/show/id_zefbfbd61237fefbfbdef.html',
|
'url': 'http://list.youku.com/show/id_zefbfbd61237fefbfbdef.html',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# Wrong number of reload_id.
|
||||||
|
'url': 'http://list.youku.com/show/id_z20eb4acaf5c211e3b2ad.html',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _extract_entries(self, playlist_data_url, show_id, note, query):
|
def _extract_entries(self, playlist_data_url, show_id, note, query):
|
||||||
query['callback'] = 'cb'
|
query['callback'] = 'cb'
|
||||||
playlist_data = self._download_json(
|
playlist_data = self._download_json(
|
||||||
playlist_data_url, show_id, query=query, note=note,
|
playlist_data_url, show_id, query=query, note=note,
|
||||||
transform_source=lambda s: js_to_json(strip_jsonp(s)))['html']
|
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
|
drama_list = (get_element_by_class('p-drama-grid', playlist_data) or
|
||||||
get_element_by_class('p-drama-half-row', playlist_data))
|
get_element_by_class('p-drama-half-row', playlist_data))
|
||||||
if drama_list is None:
|
if drama_list is None:
|
||||||
@@ -291,8 +297,8 @@ class YoukuShowIE(InfoExtractor):
|
|||||||
'id': page_config['showid'],
|
'id': page_config['showid'],
|
||||||
'stage': reload_id,
|
'stage': reload_id,
|
||||||
})
|
})
|
||||||
entries.extend(new_entries)
|
if new_entries is not None:
|
||||||
|
entries.extend(new_entries)
|
||||||
desc = self._html_search_meta('description', webpage, fatal=False)
|
desc = self._html_search_meta('description', webpage, fatal=False)
|
||||||
playlist_title = desc.split(',')[0] if desc else None
|
playlist_title = desc.split(',')[0] if desc else None
|
||||||
detail_li = get_element_by_class('p-intro', webpage)
|
detail_li = get_element_by_class('p-intro', webpage)
|
||||||
|
@@ -39,6 +39,7 @@ from .compat import (
|
|||||||
compat_HTMLParser,
|
compat_HTMLParser,
|
||||||
compat_basestring,
|
compat_basestring,
|
||||||
compat_chr,
|
compat_chr,
|
||||||
|
compat_ctypes_WINFUNCTYPE,
|
||||||
compat_etree_fromstring,
|
compat_etree_fromstring,
|
||||||
compat_expanduser,
|
compat_expanduser,
|
||||||
compat_html_entities,
|
compat_html_entities,
|
||||||
@@ -1330,24 +1331,24 @@ def _windows_write_string(s, out):
|
|||||||
if fileno not in WIN_OUTPUT_IDS:
|
if fileno not in WIN_OUTPUT_IDS:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
GetStdHandle = ctypes.WINFUNCTYPE(
|
GetStdHandle = compat_ctypes_WINFUNCTYPE(
|
||||||
ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD)(
|
ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD)(
|
||||||
(b'GetStdHandle', ctypes.windll.kernel32))
|
('GetStdHandle', ctypes.windll.kernel32))
|
||||||
h = GetStdHandle(WIN_OUTPUT_IDS[fileno])
|
h = GetStdHandle(WIN_OUTPUT_IDS[fileno])
|
||||||
|
|
||||||
WriteConsoleW = ctypes.WINFUNCTYPE(
|
WriteConsoleW = compat_ctypes_WINFUNCTYPE(
|
||||||
ctypes.wintypes.BOOL, ctypes.wintypes.HANDLE, ctypes.wintypes.LPWSTR,
|
ctypes.wintypes.BOOL, ctypes.wintypes.HANDLE, ctypes.wintypes.LPWSTR,
|
||||||
ctypes.wintypes.DWORD, ctypes.POINTER(ctypes.wintypes.DWORD),
|
ctypes.wintypes.DWORD, ctypes.POINTER(ctypes.wintypes.DWORD),
|
||||||
ctypes.wintypes.LPVOID)((b'WriteConsoleW', ctypes.windll.kernel32))
|
ctypes.wintypes.LPVOID)(('WriteConsoleW', ctypes.windll.kernel32))
|
||||||
written = ctypes.wintypes.DWORD(0)
|
written = ctypes.wintypes.DWORD(0)
|
||||||
|
|
||||||
GetFileType = ctypes.WINFUNCTYPE(ctypes.wintypes.DWORD, ctypes.wintypes.DWORD)((b'GetFileType', ctypes.windll.kernel32))
|
GetFileType = compat_ctypes_WINFUNCTYPE(ctypes.wintypes.DWORD, ctypes.wintypes.DWORD)(('GetFileType', ctypes.windll.kernel32))
|
||||||
FILE_TYPE_CHAR = 0x0002
|
FILE_TYPE_CHAR = 0x0002
|
||||||
FILE_TYPE_REMOTE = 0x8000
|
FILE_TYPE_REMOTE = 0x8000
|
||||||
GetConsoleMode = ctypes.WINFUNCTYPE(
|
GetConsoleMode = compat_ctypes_WINFUNCTYPE(
|
||||||
ctypes.wintypes.BOOL, ctypes.wintypes.HANDLE,
|
ctypes.wintypes.BOOL, ctypes.wintypes.HANDLE,
|
||||||
ctypes.POINTER(ctypes.wintypes.DWORD))(
|
ctypes.POINTER(ctypes.wintypes.DWORD))(
|
||||||
(b'GetConsoleMode', ctypes.windll.kernel32))
|
('GetConsoleMode', ctypes.windll.kernel32))
|
||||||
INVALID_HANDLE_VALUE = ctypes.wintypes.DWORD(-1).value
|
INVALID_HANDLE_VALUE = ctypes.wintypes.DWORD(-1).value
|
||||||
|
|
||||||
def not_a_console(handle):
|
def not_a_console(handle):
|
||||||
|
Reference in New Issue
Block a user