Compare commits

...

8 Commits

Author SHA1 Message Date
Iaiao
fba797449a
Merge 19ac5b7656f04e8ba8d1ec136d1791236aa2f052 into 3eb8d22ddb8982ca4fb56bb7a8d6517538bf14c6 2025-04-01 10:58:47 +02:00
dirkf
3eb8d22ddb
[JSInterp] Temporary fix for #33102 2025-03-31 04:21:09 +01:00
dirkf
4e714f9df1 [Misc] Correct [_]IE_DESC/NAME in a few IEs
* thx seproDev, yt-dlp/yt-dlp/pull/12694/commits/ae69e3c
* also add documenting comment in `InfoExtractor`
2025-03-26 12:47:19 +00:00
dirkf
c1ea7f5a24 [ITV] Mark ITVX not working
* update old shim
* correct [_]IE_DESC
2025-03-26 12:17:49 +00:00
Iaiao
19ac5b7656
[goodgame] test view_count with int keyword 2021-01-16 22:36:05 +02:00
Iaiao
556d03d450
[goodgame] Don't capture unused group 2021-01-16 22:31:30 +02:00
Iaiao
1b733c1e2c
[goodgame] Get title like dict[key] 2021-01-16 22:23:05 +02:00
Iaiao
49e0682d1b
[goodgame] Add new extractor 2021-01-16 21:04:31 +02:00
8 changed files with 256 additions and 15 deletions

View File

@ -32,7 +32,7 @@ class BokeCCBaseIE(InfoExtractor):
class BokeCCIE(BokeCCBaseIE): class BokeCCIE(BokeCCBaseIE):
_IE_DESC = 'CC视频' IE_DESC = 'CC视频'
_VALID_URL = r'https?://union\.bokecc\.com/playvideo\.bo\?(?P<query>.*)' _VALID_URL = r'https?://union\.bokecc\.com/playvideo\.bo\?(?P<query>.*)'
_TESTS = [{ _TESTS = [{

View File

@ -9,7 +9,7 @@ from ..utils import (
class CloudyIE(InfoExtractor): class CloudyIE(InfoExtractor):
_IE_DESC = 'cloudy.ec' IE_DESC = 'cloudy.ec'
_VALID_URL = r'https?://(?:www\.)?cloudy\.ec/(?:v/|embed\.php\?.*?\bid=)(?P<id>[A-Za-z0-9]+)' _VALID_URL = r'https?://(?:www\.)?cloudy\.ec/(?:v/|embed\.php\?.*?\bid=)(?P<id>[A-Za-z0-9]+)'
_TESTS = [{ _TESTS = [{
'url': 'https://www.cloudy.ec/v/af511e2527aac', 'url': 'https://www.cloudy.ec/v/af511e2527aac',

View File

@ -422,6 +422,8 @@ class InfoExtractor(object):
_GEO_COUNTRIES = None _GEO_COUNTRIES = None
_GEO_IP_BLOCKS = None _GEO_IP_BLOCKS = None
_WORKING = True _WORKING = True
# supply this in public subclasses: used in supported sites list, etc
# IE_DESC = 'short description of IE'
def __init__(self, downloader=None): def __init__(self, downloader=None):
"""Constructor. Receives an optional downloader.""" """Constructor. Receives an optional downloader."""

View File

@ -465,6 +465,11 @@ from .globo import (
from .go import GoIE from .go import GoIE
from .godtube import GodTubeIE from .godtube import GodTubeIE
from .golem import GolemIE from .golem import GolemIE
from .goodgame import (
GoodgameStreamIE,
GoodgameVideoIE,
GoodgameClipIE,
)
from .googledrive import GoogleDriveIE from .googledrive import GoogleDriveIE
from .googlepodcasts import ( from .googlepodcasts import (
GooglePodcastsIE, GooglePodcastsIE,

View File

@ -0,0 +1,239 @@
# coding=utf-8
from __future__ import unicode_literals
import re
from .common import InfoExtractor
from .youtube import YoutubeIE
from ..utils import (
ExtractorError,
int_or_none
)
class GoodgameBaseIE(InfoExtractor):
_RTMP_SERVER = 'rtmp://46.61.227.158:1940/vod//'
_API_BASE = 'https://goodgame.ru/api'
_HLS_BASE = 'https://hls.goodgame.ru/hls'
_QUALITIES = {
'Source': '',
'240p': '_240',
'480p': '_480',
'720p': '_720',
}
_RE_UPLOADER = r'''(?x)
<a[^>]+
href=\"https?://(?:www\.)?goodgame\.ru/user/(?P<uploader_id>\d+)/\"
[^>]*>
(?P<uploader>[^<]+)
'''
_RE_TIMESTAMP = r'utc-timestamp=\"(?P<timestamp>\d+)\"'
def _extract_uploader(self, webpage):
uploader_match = re.search(self._RE_UPLOADER, webpage)
if uploader_match:
uploader = uploader_match.group('uploader')
uploader_id = uploader_match.group('uploader_id')
else:
uploader, uploader_id = None, None
return uploader, uploader_id
class GoodgameStreamIE(GoodgameBaseIE):
IE_NAME = 'goodgame:stream'
_VALID_URL = r'https?://(?:www\.)?goodgame\.ru/(?:channel/|player\?)(?P<id>[^/#?]+)'
_TESTS = [{
'url': 'https://goodgame.ru/channel/rutony',
'info_dict': {
'id': 'rutony',
'title': r're:^.*',
'thumbnail': r're:^https?://.*\.jpg$',
'ext': 'mp4',
'is_live': True,
},
'params': {
'skip_download': True,
}
}, {
'url': 'https://goodgame.ru/player?9418',
'info_dict': {
'id': 'Artist.the',
'title': r're:^.*',
'thumbnail': r're:^https?://.*\.jpg$',
'ext': 'mp4',
'is_live': True,
},
'params': {
'skip_download': True,
}
}, {
'url': 'https://goodgame.ru/channel/BRAT_OK/#autoplay',
'only_matching': True,
}]
def _real_extract(self, url):
channel_id = self._match_id(url)
stream_info = next(
_ for _ in self._download_json('%s/getchannelstatus?id=%s&fmt=json' % (self._API_BASE, channel_id),
channel_id,
note='Downloading stream JSON').values())
if stream_info.get('status') == 'Dead':
raise ExtractorError('%s is offline' % channel_id, expected=True)
# url with player and stream_id
if stream_info.get('key') == stream_info.get('stream_id'):
channel_id = self._download_json('%s/player?src=%s' % (self._API_BASE, channel_id),
channel_id,
note='Downloading streamer info JSON').get('streamer_name')
_id = self._search_regex(r'src=\"https://goodgame.ru/player\?(?P<id>.+)\"', stream_info.get('embed'), 'id')
thumbnail = stream_info.get('thumb')
# goodgame.ru host thumbnail image
if thumbnail.startswith('//'):
thumbnail = 'https:%s' % thumbnail
else:
thumbnail = None
formats = []
for quality, suffix in self._QUALITIES.items():
formats.append({
'format_id': quality,
'url': '%s/%s%s.m3u8' % (self._HLS_BASE, _id, suffix),
'ext': 'mp4',
'protocol': 'm3u8'
})
self._prefer_source(formats)
return {
'id': channel_id,
'title': stream_info['title'],
'view_count': int(stream_info.get('viewers')),
'thumbnail': thumbnail,
'is_live': True,
'formats': formats,
}
def _prefer_source(self, formats):
try:
source = next(f for f in formats if f['format_id'] == 'Source')
source['preference'] = 10
except StopIteration:
pass
self._sort_formats(formats)
class GoodgameVideoIE(GoodgameBaseIE):
IE_NAME = 'goodgame:video'
_VALID_URL = r'https?://(?:www\.)?goodgame\.ru/video/(?P<id>\d+)'
_TEST = {
# Embedded youtube video
'url': 'https://goodgame.ru/video/49294/',
'add_ie': ['Youtube'],
'info_dict': {
'id': 'EihUN4ylsn4',
'title': 'Шахматы с Бонивуром | Осторожно, мат на стриме! [запись 25.09.17]',
'description': r're:^Большое спасибо за поддержку трансляций интеллектуального формата\..*',
'uploader': 'bonivur',
'uploader_id': '30',
'timestamp': 1506416377,
'upload_date': '20170925',
'ext': 'mp4',
},
'params': {
'skip_download': True,
}
}
def _real_extract(self, url):
video_id = self._match_id(url)
webpage = self._download_webpage(url, video_id)
title = self._html_search_regex(
r'<div[^>]+class=([\"\'])[^\"\']*video-description[^\"\']*\1[^>]*>.*'
r'<div[^>]+class=\"title\"[^>]*>(?P<title>[^<]+)',
webpage, 'title', group='title', flags=re.DOTALL)
description = self._html_search_regex(r'<div[^>]+class=\"description\"[^>]*>(?P<info>[^\0]*?)</div>',
webpage, 'info', fatal=False, default=None)
timestamp = self._html_search_regex(self._RE_TIMESTAMP, webpage, 'timestamp', fatal=False, default=None)
uploader, uploader_id = self._extract_uploader(webpage)
embed_url = YoutubeIE._extract_url(webpage)
if embed_url:
return {
'_type': 'url_transparent',
'url': embed_url,
'title': title,
'description': description,
'timestamp': int_or_none(timestamp),
'uploader': uploader,
'uploader_id': uploader_id
}
file = self._html_search_regex(r'<param[^>]+name=\"flashvars\"[^>]+value=\"src=(?P<path>[^=\"]+)\"[^>]*>',
webpage, 'path', default=None)
if file:
rtmp_url = '%s%s' % (self._RTMP_SERVER, file)
formats = [{
'format_id': 'rtmp',
'url': rtmp_url,
'ext': 'flv',
}]
return {
'id': video_id,
'title': title,
'description': description,
'timestamp': int_or_none(timestamp),
'uploader': uploader,
'uploader_id': uploader_id,
'formats': formats,
}
raise ExtractorError('Video %s was deleted' % video_id, expected=True)
class GoodgameClipIE(GoodgameBaseIE):
IE_NAME = 'goodgame:clip'
_VALID_URL = r'https?://(?:www\.)?goodgame\.ru/clip/(?P<id>\d+)'
_TESTS = [{
'url': 'https://goodgame.ru/clip/397722/',
'info_dict': {
'id': '397722',
'title': 'ЭТО ФИАСКО',
'uploader': '0x111BA6FA',
'uploader_id': '1035569',
'timestamp': 1506975639,
'upload_date': '20171002',
'ext': 'mp4',
},
'params': {
'skip_download': True,
}
}, {
'url': 'https://goodgame.ru/clip/397155/?from=rec',
'only_matching': True,
}]
def _real_extract(self, url):
video_id = self._match_id(url)
webpage = self._download_webpage(url, video_id)
timestamp = self._html_search_regex(self._RE_TIMESTAMP, webpage, 'timestamp')
uploader, uploader_id = self._extract_uploader(webpage)
formats = [{
'format_id': 'clip',
'url': self._og_search_video_url(webpage),
'ext': 'mp4',
}]
return {
'id': video_id,
'title': self._og_search_title(webpage),
'thumbnail': self._og_search_thumbnail(webpage),
'timestamp': int_or_none(timestamp),
'uploader': uploader,
'uploader_id': uploader_id,
'formats': formats,
}

View File

@ -35,15 +35,6 @@ from ..utils import (
class ITVBaseIE(InfoExtractor): class ITVBaseIE(InfoExtractor):
def _search_nextjs_data(self, webpage, video_id, **kw):
transform_source = kw.pop('transform_source', None)
fatal = kw.pop('fatal', True)
return self._parse_json(
self._search_regex(
r'''<script\b[^>]+\bid=('|")__NEXT_DATA__\1[^>]*>(?P<js>[^<]+)</script>''',
webpage, 'next.js data', group='js', fatal=fatal, **kw),
video_id, transform_source=transform_source, fatal=fatal)
def __handle_request_webpage_error(self, err, video_id=None, errnote=None, fatal=True): def __handle_request_webpage_error(self, err, video_id=None, errnote=None, fatal=True):
if errnote is False: if errnote is False:
return False return False
@ -109,7 +100,9 @@ class ITVBaseIE(InfoExtractor):
class ITVIE(ITVBaseIE): class ITVIE(ITVBaseIE):
_VALID_URL = r'https?://(?:www\.)?itv\.com/(?:(?P<w>watch)|hub)/[^/]+/(?(w)[\w-]+/)(?P<id>\w+)' _VALID_URL = r'https?://(?:www\.)?itv\.com/(?:(?P<w>watch)|hub)/[^/]+/(?(w)[\w-]+/)(?P<id>\w+)'
_IE_DESC = 'ITVX' IE_DESC = 'ITVX'
_WORKING = False
_TESTS = [{ _TESTS = [{
'note': 'Hub URLs redirect to ITVX', 'note': 'Hub URLs redirect to ITVX',
'url': 'https://www.itv.com/hub/liar/2a4547a0012', 'url': 'https://www.itv.com/hub/liar/2a4547a0012',
@ -270,7 +263,7 @@ class ITVIE(ITVBaseIE):
'ext': determine_ext(href, 'vtt'), 'ext': determine_ext(href, 'vtt'),
}) })
next_data = self._search_nextjs_data(webpage, video_id, fatal=False, default='{}') next_data = self._search_nextjs_data(webpage, video_id, fatal=False, default={})
video_data.update(traverse_obj(next_data, ('props', 'pageProps', ('title', 'episode')), expected_type=dict)[0] or {}) video_data.update(traverse_obj(next_data, ('props', 'pageProps', ('title', 'episode')), expected_type=dict)[0] or {})
title = traverse_obj(video_data, 'headerTitle', 'episodeTitle') title = traverse_obj(video_data, 'headerTitle', 'episodeTitle')
info = self._og_extract(webpage, require_title=not title) info = self._og_extract(webpage, require_title=not title)
@ -323,7 +316,7 @@ class ITVIE(ITVBaseIE):
class ITVBTCCIE(ITVBaseIE): class ITVBTCCIE(ITVBaseIE):
_VALID_URL = r'https?://(?:www\.)?itv\.com/(?!(?:watch|hub)/)(?:[^/]+/)+(?P<id>[^/?#&]+)' _VALID_URL = r'https?://(?:www\.)?itv\.com/(?!(?:watch|hub)/)(?:[^/]+/)+(?P<id>[^/?#&]+)'
_IE_DESC = 'ITV articles: News, British Touring Car Championship' IE_DESC = 'ITV articles: News, British Touring Car Championship'
_TESTS = [{ _TESTS = [{
'note': 'British Touring Car Championship', 'note': 'British Touring Car Championship',
'url': 'https://www.itv.com/btcc/articles/btcc-2018-all-the-action-from-brands-hatch', 'url': 'https://www.itv.com/btcc/articles/btcc-2018-all-the-action-from-brands-hatch',

View File

@ -47,7 +47,7 @@ class SenateISVPIE(InfoExtractor):
['vetaff', '76462', 'http://vetaff-f.akamaihd.net'], ['vetaff', '76462', 'http://vetaff-f.akamaihd.net'],
['arch', '', 'http://ussenate-f.akamaihd.net/'] ['arch', '', 'http://ussenate-f.akamaihd.net/']
] ]
_IE_NAME = 'senate.gov' IE_NAME = 'senate.gov'
_VALID_URL = r'https?://(?:www\.)?senate\.gov/isvp/?\?(?P<qs>.+)' _VALID_URL = r'https?://(?:www\.)?senate\.gov/isvp/?\?(?P<qs>.+)'
_TESTS = [{ _TESTS = [{
'url': 'http://www.senate.gov/isvp/?comm=judiciary&type=live&stt=&filename=judiciary031715&auto_play=false&wmode=transparent&poster=http%3A%2F%2Fwww.judiciary.senate.gov%2Fthemes%2Fjudiciary%2Fimages%2Fvideo-poster-flash-fit.png', 'url': 'http://www.senate.gov/isvp/?comm=judiciary&type=live&stt=&filename=judiciary031715&auto_play=false&wmode=transparent&poster=http%3A%2F%2Fwww.judiciary.senate.gov%2Fthemes%2Fjudiciary%2Fimages%2Fvideo-poster-flash-fit.png',

View File

@ -686,6 +686,8 @@ class JSInterpreter(object):
raise self.Exception('Cannot get index {idx!r:.100}'.format(**locals()), expr=repr(obj), cause=e) raise self.Exception('Cannot get index {idx!r:.100}'.format(**locals()), expr=repr(obj), cause=e)
def _dump(self, obj, namespace): def _dump(self, obj, namespace):
if obj is JS_Undefined:
return 'undefined'
try: try:
return json.dumps(obj) return json.dumps(obj)
except TypeError: except TypeError: