Compare commits

...

5 Commits

Author SHA1 Message Date
dirkf
c0a75d6c4e
Merge 51f781c75ba2152642ef17d140cd1dec7ea763a9 into 3eb8d22ddb8982ca4fb56bb7a8d6517538bf14c6 2025-04-01 09:35:01 +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
df
51f781c75b Fix handling of output template fields with initial '-' 2021-08-31 02:58:54 +01:00
10 changed files with 78 additions and 41 deletions

View File

@ -652,6 +652,10 @@ class TestYoutubeDL(unittest.TestCase):
'height': 1080,
'title1': '$PATH',
'title2': '%PATH%',
'track': '-track with initial hyphen',
'display_id': '-initial_hyphen',
'episode': '.episode with initial period',
'episode_id': '.initial_period',
}
def fname(templ, na_placeholder='NA'):
@ -667,7 +671,9 @@ class TestYoutubeDL(unittest.TestCase):
self.assertEqual(fname(NA_TEST_OUTTMPL), 'NA-NA-1234.mp4')
# Or by provided placeholder
self.assertEqual(fname(NA_TEST_OUTTMPL, na_placeholder='none'), 'none-none-1234.mp4')
self.assertEqual(fname(NA_TEST_OUTTMPL, na_placeholder=''), '--1234.mp4')
self.assertEqual(fname(NA_TEST_OUTTMPL, na_placeholder=''), '_-1234.mp4')
NA_TEST_OUTTMPL = '%(uploader_date)s+%(width)d+%(id)s.%(ext)s'
self.assertEqual(fname(NA_TEST_OUTTMPL, na_placeholder=''), '++1234.mp4')
self.assertEqual(fname('%(height)d.%(ext)s'), '1080.mp4')
self.assertEqual(fname('%(height)6d.%(ext)s'), ' 1080.mp4')
self.assertEqual(fname('%(height)-6d.%(ext)s'), '1080 .mp4')
@ -685,6 +691,16 @@ class TestYoutubeDL(unittest.TestCase):
self.assertEqual(fname('%%(width)06d.%(ext)s'), '%(width)06d.mp4')
self.assertEqual(fname('Hello %(title1)s'), 'Hello $PATH')
self.assertEqual(fname('Hello %(title2)s'), 'Hello %PATH%')
self.assertEqual(fname('%(track)s at start changes hyphen'),
'_track with initial hyphen at start changes hyphen')
self.assertEqual(fname('medial %(track)s doesn\'t change hyphen'),
'medial -track with initial hyphen doesn\'t change hyphen')
self.assertEqual(fname('%(display_id)s at start doesn\'t change hyphen'),
'-initial_hyphen at start doesn\'t change hyphen')
self.assertEqual(fname('%(episode)s at start changes period'),
'_episode with initial period at start changes period')
self.assertEqual(fname('%(episode_id)s at start doesn\'t change period'),
'.initial_period at start doesn\'t change period')
def test_format_note(self):
ydl = YoutubeDL()

View File

@ -156,10 +156,8 @@ class TestUtil(unittest.TestCase):
sanitize_filename('New World record at 0:12:34'),
'New World record at 0_12_34')
self.assertEqual(sanitize_filename('--gasdgf'), '_-gasdgf')
self.assertEqual(sanitize_filename('--gasdgf', is_id=True), '--gasdgf')
self.assertEqual(sanitize_filename('.gasdgf'), 'gasdgf')
self.assertEqual(sanitize_filename('.gasdgf', is_id=True), '.gasdgf')
self.assertEqual(sanitize_filename('__gas__dgf_'), 'gas_dgf')
self.assertEqual(sanitize_filename('__gas__dgf_', is_id=True), '__gas__dgf_')
forbidden = '"\0\\/'
for fc in forbidden:

View File

@ -694,6 +694,20 @@ class YoutubeDL(object):
except UnicodeEncodeError:
self.to_screen('[download] The file has already been downloaded')
# As of [1] format syntax is:
# %[mapping_key][conversion_flags][minimum_width][.precision][length_modifier]type
# 1. https://docs.python.org/2/library/stdtypes.html#string-formatting
_FORMAT_RE = r'''(?x)
(?<!%)
%
\({0}\) # mapping key
(?:[#0\-+ ]+)? # conversion flags (optional)
(?:\d+)? # minimum field width (optional)
(?:\.\d+)? # precision (optional)
[hlL]? # length modifier (optional)
[diouxXeEfFgGcrs%] # conversion type
'''
def prepare_filename(self, info_dict):
"""Generate the output filename."""
try:
@ -712,10 +726,11 @@ class YoutubeDL(object):
elif template_dict.get('width'):
template_dict['resolution'] = '%dx?' % template_dict['width']
is_id = lambda k: k == 'id' or k.endswith('_id')
sanitize = lambda k, v: sanitize_filename(
compat_str(v),
restricted=self.params.get('restrictfilenames'),
is_id=(k == 'id' or k.endswith('_id')))
is_id=is_id(k))
template_dict = dict((k, v if isinstance(v, compat_numeric_types) else sanitize(k, v))
for k, v in template_dict.items()
if v is not None and not isinstance(v, (list, tuple, dict)))
@ -743,21 +758,8 @@ class YoutubeDL(object):
# output template for missing fields to meet string presentation type.
for numeric_field in self._NUMERIC_FIELDS:
if numeric_field not in template_dict:
# As of [1] format syntax is:
# %[mapping_key][conversion_flags][minimum_width][.precision][length_modifier]type
# 1. https://docs.python.org/2/library/stdtypes.html#string-formatting
FORMAT_RE = r'''(?x)
(?<!%)
%
\({0}\) # mapping key
(?:[#0\-+ ]+)? # conversion flags (optional)
(?:\d+)? # minimum field width (optional)
(?:\.\d+)? # precision (optional)
[hlL]? # length modifier (optional)
[diouxXeEfFgGcrs%] # conversion type
'''
outtmpl = re.sub(
FORMAT_RE.format(numeric_field),
self._FORMAT_RE.format(numeric_field),
r'%({0})s'.format(numeric_field), outtmpl)
# expand_path translates '%%' into '%' and '$$' into '$'
@ -771,7 +773,35 @@ class YoutubeDL(object):
# because meta fields may contain env variables we don't want to
# be expanded. For example, for outtmpl "%(title)s.%(ext)s" and
# title "Hello $PATH", we don't want `$PATH` to be expanded.
filename = expand_path(outtmpl).replace(sep, '') % template_dict
filename = expand_path(outtmpl).replace(sep, '')
# avoid non-mandatory (#571) initial '-' (#5035): convert to '_'
# also remove initial '.'
# first split the filename around the format fields
FORMAT_SPLIT_RE = (
'%s(%s)' % tuple(
re.split(r'(\(\?[iLmsux]+\))',
self._FORMAT_RE.format(r'\w+'), maxsplit=1)[1:]))
filename = re.split(FORMAT_SPLIT_RE, filename)
# tag any filename fragment that is an ID format
FORMAT_FIELD_RE = (
'%s(%s)' % tuple(
re.split(r'(\(\?[iLmsux]+\))',
self._FORMAT_RE.format(r'(?P<field>\w+)'), maxsplit=1)[1:]))
field_is_id = (
lambda x:
(lambda y: y and is_id(y.group('field')))(re.match(FORMAT_FIELD_RE, x)))
# list formatted fragments and tags
tagged_fn = [(x % template_dict, field_is_id(x)) for x in filename]
# list the initial fragments that can be munged from [.-] to _, ie
# those that not IDs, or empty
mungable = itertools.takewhile(lambda x: not(x[1] and x[0]), tagged_fn)
col0 = lambda l: next(iter(zip(*l)), [])
mungable = col0(mungable)
# finally combine the munged initial part and the rest
filename = (re.sub(r'^[.-]', '_', ''.join(mungable))
+ ''.join(col0(tagged_fn[len(mungable):])))
# Temporary fix for #4787
# 'Treat' all problem characters by passing filename through preferredencoding

View File

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

View File

@ -9,7 +9,7 @@ from ..utils import (
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]+)'
_TESTS = [{
'url': 'https://www.cloudy.ec/v/af511e2527aac',

View File

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

View File

@ -35,15 +35,6 @@ from ..utils import (
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):
if errnote is False:
return False
@ -109,7 +100,9 @@ class ITVBaseIE(InfoExtractor):
class ITVIE(ITVBaseIE):
_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 = [{
'note': 'Hub URLs redirect to ITVX',
'url': 'https://www.itv.com/hub/liar/2a4547a0012',
@ -270,7 +263,7 @@ class ITVIE(ITVBaseIE):
'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 {})
title = traverse_obj(video_data, 'headerTitle', 'episodeTitle')
info = self._og_extract(webpage, require_title=not title)
@ -323,7 +316,7 @@ class ITVIE(ITVBaseIE):
class ITVBTCCIE(ITVBaseIE):
_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 = [{
'note': 'British Touring Car Championship',
'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'],
['arch', '', 'http://ussenate-f.akamaihd.net/']
]
_IE_NAME = 'senate.gov'
IE_NAME = 'senate.gov'
_VALID_URL = r'https?://(?:www\.)?senate\.gov/isvp/?\?(?P<qs>.+)'
_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',

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)
def _dump(self, obj, namespace):
if obj is JS_Undefined:
return 'undefined'
try:
return json.dumps(obj)
except TypeError:

View File

@ -2113,15 +2113,11 @@ def sanitize_filename(s, restricted=False, is_id=False):
s = re.sub(r'[0-9]+(?::[0-9]+)+', lambda m: m.group(0).replace(':', '_'), s)
result = ''.join(map(replace_insane, s))
if not is_id:
while '__' in result:
result = result.replace('__', '_')
result = re.sub(r'_{2,}', '_', result)
result = result.strip('_')
# Common case of "Foreign band name - English song title"
if restricted and result.startswith('-_'):
result = result[2:]
if result.startswith('-'):
result = '_' + result[len('-'):]
result = result.lstrip('.')
if not result:
result = '_'
return result