aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesús <heckyel@hyperbola.info>2021-10-20 11:45:20 -0500
committerJesús <heckyel@hyperbola.info>2021-10-20 11:45:20 -0500
commitc7afb25e19a91493db6069d1db9f7d1bc8491dc1 (patch)
treed4f4d0a125e191585af49dcfb189d2f1ba9acf17
parent000d2844fd93d8c35fc74d22588291e7c7d742fa (diff)
parentd3c93ec2b7f5bcb872b0afb169efaa2f1abdf6e2 (diff)
downloadhypervideo-pre-c7afb25e19a91493db6069d1db9f7d1bc8491dc1.tar.lz
hypervideo-pre-c7afb25e19a91493db6069d1db9f7d1bc8491dc1.tar.xz
hypervideo-pre-c7afb25e19a91493db6069d1db9f7d1bc8491dc1.zip
updated from upstream | 20/10/2021 at 11:45
-rw-r--r--test/test_utils.py8
-rw-r--r--yt_dlp/YoutubeDL.py13
-rw-r--r--yt_dlp/cookies.py16
-rw-r--r--yt_dlp/downloader/common.py13
-rw-r--r--yt_dlp/downloader/external.py26
-rw-r--r--yt_dlp/downloader/fragment.py10
-rw-r--r--yt_dlp/downloader/http.py8
-rw-r--r--yt_dlp/downloader/rtmp.py3
-rw-r--r--yt_dlp/extractor/adn.py9
-rw-r--r--yt_dlp/extractor/openload.py11
-rw-r--r--yt_dlp/options.py4
-rw-r--r--yt_dlp/postprocessor/embedthumbnail.py6
-rw-r--r--yt_dlp/postprocessor/ffmpeg.py14
-rw-r--r--yt_dlp/postprocessor/modify_chapters.py10
-rw-r--r--yt_dlp/postprocessor/sponskrub.py6
-rw-r--r--yt_dlp/utils.py58
-rw-r--r--yt_dlp/webvtt.py8
17 files changed, 122 insertions, 101 deletions
diff --git a/test/test_utils.py b/test/test_utils.py
index 9a5e3f0f0..d84c3d3ee 100644
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -1390,21 +1390,21 @@ The first line
</body>
</tt>'''.encode('utf-8')
srt_data = '''1
-00:00:02,080 --> 00:00:05,839
+00:00:02,080 --> 00:00:05,840
<font color="white" face="sansSerif" size="16">default style<font color="red">custom style</font></font>
2
-00:00:02,080 --> 00:00:05,839
+00:00:02,080 --> 00:00:05,840
<b><font color="cyan" face="sansSerif" size="16"><font color="lime">part 1
</font>part 2</font></b>
3
-00:00:05,839 --> 00:00:09,560
+00:00:05,840 --> 00:00:09,560
<u><font color="lime">line 3
part 3</font></u>
4
-00:00:09,560 --> 00:00:12,359
+00:00:09,560 --> 00:00:12,360
<i><u><font color="yellow"><font color="lime">inner
</font>style</font></u></i>
diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py
index 18ab19e09..d1ab540d2 100644
--- a/yt_dlp/YoutubeDL.py
+++ b/yt_dlp/YoutubeDL.py
@@ -87,10 +87,10 @@ from .utils import (
parse_filesize,
PerRequestProxyHandler,
platform_name,
+ Popen,
PostProcessingError,
preferredencoding,
prepend_extension,
- process_communicate_or_kill,
register_socks_protocols,
RejectedVideoReached,
render_table,
@@ -577,12 +577,9 @@ class YoutubeDL(object):
stdout=slave,
stderr=self._err_file)
try:
- self._output_process = subprocess.Popen(
- ['bidiv'] + width_args, **sp_kwargs
- )
+ self._output_process = Popen(['bidiv'] + width_args, **sp_kwargs)
except OSError:
- self._output_process = subprocess.Popen(
- ['fribidi', '-c', 'UTF-8'] + width_args, **sp_kwargs)
+ self._output_process = Popen(['fribidi', '-c', 'UTF-8'] + width_args, **sp_kwargs)
self._output_channel = os.fdopen(master, 'rb')
except OSError as ose:
if ose.errno == errno.ENOENT:
@@ -3278,11 +3275,11 @@ class YoutubeDL(object):
if self.params.get('compat_opts'):
write_debug('Compatibility options: %s\n' % ', '.join(self.params.get('compat_opts')))
try:
- sp = subprocess.Popen(
+ sp = Popen(
['git', 'rev-parse', '--short', 'HEAD'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
cwd=os.path.dirname(os.path.abspath(__file__)))
- out, err = process_communicate_or_kill(sp)
+ out, err = sp.communicate_or_kill()
out = out.decode().strip()
if re.match('[0-9a-f]+', out):
write_debug('Git HEAD: %s\n' % out)
diff --git a/yt_dlp/cookies.py b/yt_dlp/cookies.py
index 049ec9fb1..5f7fdf584 100644
--- a/yt_dlp/cookies.py
+++ b/yt_dlp/cookies.py
@@ -17,7 +17,7 @@ from .compat import (
from .utils import (
bug_reports_message,
expand_path,
- process_communicate_or_kill,
+ Popen,
YoutubeDLCookieJar,
)
@@ -599,14 +599,14 @@ def _get_mac_keyring_password(browser_keyring_name, logger):
return password.encode('utf-8')
else:
logger.debug('using find-generic-password to obtain password')
- proc = subprocess.Popen(['security', 'find-generic-password',
- '-w', # write password to stdout
- '-a', browser_keyring_name, # match 'account'
- '-s', '{} Safe Storage'.format(browser_keyring_name)], # match 'service'
- stdout=subprocess.PIPE,
- stderr=subprocess.DEVNULL)
+ proc = Popen(
+ ['security', 'find-generic-password',
+ '-w', # write password to stdout
+ '-a', browser_keyring_name, # match 'account'
+ '-s', '{} Safe Storage'.format(browser_keyring_name)], # match 'service'
+ stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
try:
- stdout, stderr = process_communicate_or_kill(proc)
+ stdout, stderr = proc.communicate_or_kill()
if stdout[-1:] == b'\n':
stdout = stdout[:-1]
return stdout
diff --git a/yt_dlp/downloader/common.py b/yt_dlp/downloader/common.py
index 9081794db..6cfbb6657 100644
--- a/yt_dlp/downloader/common.py
+++ b/yt_dlp/downloader/common.py
@@ -12,6 +12,7 @@ from ..utils import (
format_bytes,
shell_quote,
timeconvert,
+ timetuple_from_msec,
)
from ..minicurses import (
MultilineLogger,
@@ -75,14 +76,12 @@ class FileDownloader(object):
@staticmethod
def format_seconds(seconds):
- (mins, secs) = divmod(seconds, 60)
- (hours, mins) = divmod(mins, 60)
- if hours > 99:
+ time = timetuple_from_msec(seconds * 1000)
+ if time.hours > 99:
return '--:--:--'
- if hours == 0:
- return '%02d:%02d' % (mins, secs)
- else:
- return '%02d:%02d:%02d' % (hours, mins, secs)
+ if not time.hours:
+ return '%02d:%02d' % time[1:-1]
+ return '%02d:%02d:%02d' % time[:-1]
@staticmethod
def calc_percent(byte_counter, data_len):
diff --git a/yt_dlp/downloader/external.py b/yt_dlp/downloader/external.py
index 40b9dcfe3..ce3370fb7 100644
--- a/yt_dlp/downloader/external.py
+++ b/yt_dlp/downloader/external.py
@@ -22,7 +22,7 @@ from ..utils import (
handle_youtubedl_headers,
check_executable,
is_outdated_version,
- process_communicate_or_kill,
+ Popen,
sanitize_open,
)
@@ -116,9 +116,8 @@ class ExternalFD(FragmentFD):
self._debug_cmd(cmd)
if 'fragments' not in info_dict:
- p = subprocess.Popen(
- cmd, stderr=subprocess.PIPE)
- _, stderr = process_communicate_or_kill(p)
+ p = Popen(cmd, stderr=subprocess.PIPE)
+ _, stderr = p.communicate_or_kill()
if p.returncode != 0:
self.to_stderr(stderr.decode('utf-8', 'replace'))
return p.returncode
@@ -128,9 +127,8 @@ class ExternalFD(FragmentFD):
count = 0
while count <= fragment_retries:
- p = subprocess.Popen(
- cmd, stderr=subprocess.PIPE)
- _, stderr = process_communicate_or_kill(p)
+ p = Popen(cmd, stderr=subprocess.PIPE)
+ _, stderr = p.communicate_or_kill()
if p.returncode == 0:
break
# TODO: Decide whether to retry based on error code
@@ -152,11 +150,11 @@ class ExternalFD(FragmentFD):
fragment_filename = '%s-Frag%d' % (tmpfilename, frag_index)
try:
src, _ = sanitize_open(fragment_filename, 'rb')
- except IOError:
+ except IOError as err:
if skip_unavailable_fragments and frag_index > 1:
- self.to_screen('[%s] Skipping fragment %d ...' % (self.get_basename(), frag_index))
+ self.report_skip_fragment(frag_index, err)
continue
- self.report_error('Unable to open fragment %d' % frag_index)
+ self.report_error(f'Unable to open fragment {frag_index}; {err}')
return -1
dest.write(decrypt_fragment(fragment, src.read()))
src.close()
@@ -199,8 +197,8 @@ class CurlFD(ExternalFD):
self._debug_cmd(cmd)
# curl writes the progress to stderr so don't capture it.
- p = subprocess.Popen(cmd)
- process_communicate_or_kill(p)
+ p = Popen(cmd)
+ p.communicate_or_kill()
return p.returncode
@@ -476,7 +474,7 @@ class FFmpegFD(ExternalFD):
args.append(encodeFilename(ffpp._ffmpeg_filename_argument(tmpfilename), True))
self._debug_cmd(args)
- proc = subprocess.Popen(args, stdin=subprocess.PIPE, env=env)
+ proc = Popen(args, stdin=subprocess.PIPE, env=env)
if url in ('-', 'pipe:'):
self.on_process_started(proc, proc.stdin)
try:
@@ -488,7 +486,7 @@ class FFmpegFD(ExternalFD):
# streams). Note that Windows is not affected and produces playable
# files (see https://github.com/ytdl-org/youtube-dl/issues/8300).
if isinstance(e, KeyboardInterrupt) and sys.platform != 'win32' and url not in ('-', 'pipe:'):
- process_communicate_or_kill(proc, b'q')
+ proc.communicate_or_kill(b'q')
else:
proc.kill()
proc.wait()
diff --git a/yt_dlp/downloader/fragment.py b/yt_dlp/downloader/fragment.py
index d0eaede7e..c345f3148 100644
--- a/yt_dlp/downloader/fragment.py
+++ b/yt_dlp/downloader/fragment.py
@@ -72,8 +72,9 @@ class FragmentFD(FileDownloader):
'\r[download] Got server HTTP error: %s. Retrying fragment %d (attempt %d of %s) ...'
% (error_to_compat_str(err), frag_index, count, self.format_retries(retries)))
- def report_skip_fragment(self, frag_index):
- self.to_screen('[download] Skipping fragment %d ...' % frag_index)
+ def report_skip_fragment(self, frag_index, err=None):
+ err = f' {err};' if err else ''
+ self.to_screen(f'[download]{err} Skipping fragment {frag_index:d} ...')
def _prepare_url(self, info_dict, url):
headers = info_dict.get('http_headers')
@@ -355,8 +356,7 @@ class FragmentFD(FileDownloader):
# not what it decrypts to.
if self.params.get('test', False):
return frag_content
- padding_len = 16 - (len(frag_content) % 16)
- decrypted_data = aes_cbc_decrypt_bytes(frag_content + bytes([padding_len] * padding_len), decrypt_info['KEY'], iv)
+ decrypted_data = aes_cbc_decrypt_bytes(frag_content, decrypt_info['KEY'], iv)
return decrypted_data[:-decrypted_data[-1]]
return decrypt_fragment
@@ -444,7 +444,7 @@ class FragmentFD(FileDownloader):
def append_fragment(frag_content, frag_index, ctx):
if not frag_content:
if not is_fatal(frag_index - 1):
- self.report_skip_fragment(frag_index)
+ self.report_skip_fragment(frag_index, 'fragment not found')
return True
else:
ctx['dest_stream'].close()
diff --git a/yt_dlp/downloader/http.py b/yt_dlp/downloader/http.py
index 2e95bb9d1..6290884a8 100644
--- a/yt_dlp/downloader/http.py
+++ b/yt_dlp/downloader/http.py
@@ -191,11 +191,13 @@ class HttpFD(FileDownloader):
# Unexpected HTTP error
raise
raise RetryDownload(err)
+ except socket.timeout as err:
+ raise RetryDownload(err)
except socket.error as err:
- if err.errno != errno.ECONNRESET:
+ if err.errno in (errno.ECONNRESET, errno.ETIMEDOUT):
# Connection reset is no problem, just retry
- raise
- raise RetryDownload(err)
+ raise RetryDownload(err)
+ raise
def download():
nonlocal throttle_start
diff --git a/yt_dlp/downloader/rtmp.py b/yt_dlp/downloader/rtmp.py
index 6dca64725..90f1acfd4 100644
--- a/yt_dlp/downloader/rtmp.py
+++ b/yt_dlp/downloader/rtmp.py
@@ -12,6 +12,7 @@ from ..utils import (
encodeFilename,
encodeArgument,
get_exe_version,
+ Popen,
)
@@ -26,7 +27,7 @@ class RtmpFD(FileDownloader):
start = time.time()
resume_percent = None
resume_downloaded_data_len = None
- proc = subprocess.Popen(args, stderr=subprocess.PIPE)
+ proc = Popen(args, stderr=subprocess.PIPE)
cursor_in_new_line = True
proc_stderr_closed = False
try:
diff --git a/yt_dlp/extractor/adn.py b/yt_dlp/extractor/adn.py
index a55ebbcbd..5a1283baa 100644
--- a/yt_dlp/extractor/adn.py
+++ b/yt_dlp/extractor/adn.py
@@ -15,6 +15,7 @@ from ..compat import (
compat_ord,
)
from ..utils import (
+ ass_subtitles_timecode,
bytes_to_intlist,
bytes_to_long,
ExtractorError,
@@ -68,10 +69,6 @@ class ADNIE(InfoExtractor):
'end': 4,
}
- @staticmethod
- def _ass_subtitles_timecode(seconds):
- return '%01d:%02d:%02d.%02d' % (seconds / 3600, (seconds % 3600) / 60, seconds % 60, (seconds % 1) * 100)
-
def _get_subtitles(self, sub_url, video_id):
if not sub_url:
return None
@@ -117,8 +114,8 @@ Format: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text'''
continue
alignment = self._POS_ALIGN_MAP.get(position_align, 2) + self._LINE_ALIGN_MAP.get(line_align, 0)
ssa += os.linesep + 'Dialogue: Marked=0,%s,%s,Default,,0,0,0,,%s%s' % (
- self._ass_subtitles_timecode(start),
- self._ass_subtitles_timecode(end),
+ ass_subtitles_timecode(start),
+ ass_subtitles_timecode(end),
'{\\a%d}' % alignment if alignment != 2 else '',
text.replace('\n', '\\N').replace('<i>', '{\\i1}').replace('</i>', '{\\i0}'))
diff --git a/yt_dlp/extractor/openload.py b/yt_dlp/extractor/openload.py
index dfdd0e526..6ec54509b 100644
--- a/yt_dlp/extractor/openload.py
+++ b/yt_dlp/extractor/openload.py
@@ -17,7 +17,7 @@ from ..utils import (
get_exe_version,
is_outdated_version,
std_headers,
- process_communicate_or_kill,
+ Popen,
)
@@ -223,11 +223,10 @@ class PhantomJSwrapper(object):
else:
self.extractor.to_screen('%s: %s' % (video_id, note2))
- p = subprocess.Popen([
- self.exe, '--ssl-protocol=any',
- self._TMP_FILES['script'].name
- ], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- out, err = process_communicate_or_kill(p)
+ p = Popen(
+ [self.exe, '--ssl-protocol=any', self._TMP_FILES['script'].name],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out, err = p.communicate_or_kill()
if p.returncode != 0:
raise ExtractorError(
'Executing JS failed\n:' + encodeArgument(err))
diff --git a/yt_dlp/options.py b/yt_dlp/options.py
index f45332ee1..b45b79bc9 100644
--- a/yt_dlp/options.py
+++ b/yt_dlp/options.py
@@ -968,6 +968,10 @@ def parseOpts(overrideArguments=None):
help="File containing URLs to download ('-' for stdin), one URL per line. "
"Lines starting with '#', ';' or ']' are considered as comments and ignored")
filesystem.add_option(
+ '--no-batch-file',
+ dest='batchfile', action='store_const', const=None,
+ help='Do not read URLs from batch file (default)')
+ filesystem.add_option(
'-P', '--paths',
metavar='[TYPES:]PATH', dest='paths', default={}, type='str',
action='callback', callback=_dict_from_options_callback,
diff --git a/yt_dlp/postprocessor/embedthumbnail.py b/yt_dlp/postprocessor/embedthumbnail.py
index 3139a6338..918d3e788 100644
--- a/yt_dlp/postprocessor/embedthumbnail.py
+++ b/yt_dlp/postprocessor/embedthumbnail.py
@@ -26,9 +26,9 @@ from ..utils import (
encodeArgument,
encodeFilename,
error_to_compat_str,
+ Popen,
PostProcessingError,
prepend_extension,
- process_communicate_or_kill,
shell_quote,
)
@@ -183,8 +183,8 @@ class EmbedThumbnailPP(FFmpegPostProcessor):
self._report_run('atomicparsley', filename)
self.write_debug('AtomicParsley command line: %s' % shell_quote(cmd))
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- stdout, stderr = process_communicate_or_kill(p)
+ p = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate_or_kill()
if p.returncode != 0:
msg = stderr.decode('utf-8', 'replace').strip()
raise EmbedThumbnailPPError(msg)
diff --git a/yt_dlp/postprocessor/ffmpeg.py b/yt_dlp/postprocessor/ffmpeg.py
index e5595341d..4a0a96427 100644
--- a/yt_dlp/postprocessor/ffmpeg.py
+++ b/yt_dlp/postprocessor/ffmpeg.py
@@ -20,9 +20,9 @@ from ..utils import (
is_outdated_version,
ISO639Utils,
orderedSet,
+ Popen,
PostProcessingError,
prepend_extension,
- process_communicate_or_kill,
replace_extension,
shell_quote,
traverse_obj,
@@ -178,10 +178,8 @@ class FFmpegPostProcessor(PostProcessor):
encodeArgument('-i')]
cmd.append(encodeFilename(self._ffmpeg_filename_argument(path), True))
self.write_debug('%s command line: %s' % (self.basename, shell_quote(cmd)))
- handle = subprocess.Popen(
- cmd, stderr=subprocess.PIPE,
- stdout=subprocess.PIPE, stdin=subprocess.PIPE)
- stdout_data, stderr_data = process_communicate_or_kill(handle)
+ handle = Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout_data, stderr_data = handle.communicate_or_kill()
expected_ret = 0 if self.probe_available else 1
if handle.wait() != expected_ret:
return None
@@ -223,7 +221,7 @@ class FFmpegPostProcessor(PostProcessor):
cmd += opts
cmd.append(encodeFilename(self._ffmpeg_filename_argument(path), True))
self.write_debug('ffprobe command line: %s' % shell_quote(cmd))
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
+ p = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
stdout, stderr = p.communicate()
return json.loads(stdout.decode('utf-8', 'replace'))
@@ -284,8 +282,8 @@ class FFmpegPostProcessor(PostProcessor):
for i, (path, opts) in enumerate(path_opts) if path)
self.write_debug('ffmpeg command line: %s' % shell_quote(cmd))
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
- stdout, stderr = process_communicate_or_kill(p)
+ p = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
+ stdout, stderr = p.communicate_or_kill()
if p.returncode not in variadic(expected_retcodes):
stderr = stderr.decode('utf-8', 'replace').strip()
self.write_debug(stderr)
diff --git a/yt_dlp/postprocessor/modify_chapters.py b/yt_dlp/postprocessor/modify_chapters.py
index a0818c41b..dca876200 100644
--- a/yt_dlp/postprocessor/modify_chapters.py
+++ b/yt_dlp/postprocessor/modify_chapters.py
@@ -31,8 +31,10 @@ class ModifyChaptersPP(FFmpegPostProcessor):
@PostProcessor._restrict_to(images=False)
def run(self, info):
+ # Chapters must be preserved intact when downloading multiple formats of the same video.
chapters, sponsor_chapters = self._mark_chapters_to_remove(
- info.get('chapters') or [], info.get('sponsorblock_chapters') or [])
+ copy.deepcopy(info.get('chapters')) or [],
+ copy.deepcopy(info.get('sponsorblock_chapters')) or [])
if not chapters and not sponsor_chapters:
return [], info
@@ -126,7 +128,7 @@ class ModifyChaptersPP(FFmpegPostProcessor):
cuts = []
def append_cut(c):
- assert 'remove' in c
+ assert 'remove' in c, 'Not a cut is appended to cuts'
last_to_cut = cuts[-1] if cuts else None
if last_to_cut and last_to_cut['end_time'] >= c['start_time']:
last_to_cut['end_time'] = max(last_to_cut['end_time'], c['end_time'])
@@ -154,7 +156,7 @@ class ModifyChaptersPP(FFmpegPostProcessor):
new_chapters = []
def append_chapter(c):
- assert 'remove' not in c
+ assert 'remove' not in c, 'Cut is appended to chapters'
length = c['end_time'] - c['start_time'] - excess_duration(c)
# Chapter is completely covered by cuts or sponsors.
if length <= 0:
@@ -237,7 +239,7 @@ class ModifyChaptersPP(FFmpegPostProcessor):
heapq.heappush(chapters, (c['start_time'], i, c))
# (normal, sponsor) and (sponsor, sponsor)
else:
- assert '_categories' in c
+ assert '_categories' in c, 'Normal chapters overlap'
cur_chapter['_was_cut'] = True
c['_was_cut'] = True
# Push the part after the sponsor to PQ.
diff --git a/yt_dlp/postprocessor/sponskrub.py b/yt_dlp/postprocessor/sponskrub.py
index 932555a0e..37e7411e4 100644
--- a/yt_dlp/postprocessor/sponskrub.py
+++ b/yt_dlp/postprocessor/sponskrub.py
@@ -11,9 +11,9 @@ from ..utils import (
encodeFilename,
shell_quote,
str_or_none,
+ Popen,
PostProcessingError,
prepend_extension,
- process_communicate_or_kill,
)
@@ -81,8 +81,8 @@ class SponSkrubPP(PostProcessor):
self.write_debug('sponskrub command line: %s' % shell_quote(cmd))
pipe = None if self.get_param('verbose') else subprocess.PIPE
- p = subprocess.Popen(cmd, stdout=pipe)
- stdout = process_communicate_or_kill(p)[0]
+ p = Popen(cmd, stdout=pipe)
+ stdout = p.communicate_or_kill()[0]
if p.returncode == 0:
os.replace(temp_filename, filename)
diff --git a/yt_dlp/utils.py b/yt_dlp/utils.py
index c42817e75..88adbd3b9 100644
--- a/yt_dlp/utils.py
+++ b/yt_dlp/utils.py
@@ -2272,6 +2272,20 @@ def process_communicate_or_kill(p, *args, **kwargs):
raise
+class Popen(subprocess.Popen):
+ if sys.platform == 'win32':
+ _startupinfo = subprocess.STARTUPINFO()
+ _startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
+ else:
+ _startupinfo = None
+
+ def __init__(self, *args, **kwargs):
+ super(Popen, self).__init__(*args, **kwargs, startupinfo=self._startupinfo)
+
+ def communicate_or_kill(self, *args, **kwargs):
+ return process_communicate_or_kill(self, *args, **kwargs)
+
+
def get_subprocess_encoding():
if sys.platform == 'win32' and sys.getwindowsversion()[0] >= 5:
# For subprocess calls, encode with locale encoding
@@ -2342,14 +2356,25 @@ def decodeOption(optval):
return optval
+_timetuple = collections.namedtuple('Time', ('hours', 'minutes', 'seconds', 'milliseconds'))
+
+
+def timetuple_from_msec(msec):
+ secs, msec = divmod(msec, 1000)
+ mins, secs = divmod(secs, 60)
+ hrs, mins = divmod(mins, 60)
+ return _timetuple(hrs, mins, secs, msec)
+
+
def formatSeconds(secs, delim=':', msec=False):
- if secs > 3600:
- ret = '%d%s%02d%s%02d' % (secs // 3600, delim, (secs % 3600) // 60, delim, secs % 60)
- elif secs > 60:
- ret = '%d%s%02d' % (secs // 60, delim, secs % 60)
+ time = timetuple_from_msec(secs * 1000)
+ if time.hours:
+ ret = '%d%s%02d%s%02d' % (time.hours, delim, time.minutes, delim, time.seconds)
+ elif time.minutes:
+ ret = '%d%s%02d' % (time.minutes, delim, time.seconds)
else:
- ret = '%d' % secs
- return '%s.%03d' % (ret, secs % 1) if msec else ret
+ ret = '%d' % time.seconds
+ return '%s.%03d' % (ret, time.milliseconds) if msec else ret
def _ssl_load_windows_store_certs(ssl_context, storename):
@@ -3966,8 +3991,7 @@ def check_executable(exe, args=[]):
""" Checks if the given binary is installed somewhere in PATH, and returns its name.
args can be a list of arguments for a short output (like -version) """
try:
- process_communicate_or_kill(subprocess.Popen(
- [exe] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE))
+ Popen([exe] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate_or_kill()
except OSError:
return False
return exe
@@ -3981,10 +4005,9 @@ def get_exe_version(exe, args=['--version'],
# STDIN should be redirected too. On UNIX-like systems, ffmpeg triggers
# SIGTTOU if yt-dlp is run in the background.
# See https://github.com/ytdl-org/youtube-dl/issues/955#issuecomment-209789656
- out, _ = process_communicate_or_kill(subprocess.Popen(
- [encodeArgument(exe)] + args,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
+ out, _ = Popen(
+ [encodeArgument(exe)] + args, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate_or_kill()
except OSError:
return False
if isinstance(out, bytes): # Python 2.x
@@ -4855,7 +4878,12 @@ def parse_dfxp_time_expr(time_expr):
def srt_subtitles_timecode(seconds):
- return '%02d:%02d:%02d,%03d' % (seconds / 3600, (seconds % 3600) / 60, seconds % 60, (seconds % 1) * 1000)
+ return '%02d:%02d:%02d,%03d' % timetuple_from_msec(seconds * 1000)
+
+
+def ass_subtitles_timecode(seconds):
+ time = timetuple_from_msec(seconds * 1000)
+ return '%01d:%02d:%02d.%02d' % (*time[:-1], time.milliseconds / 10)
def dfxp2srt(dfxp_data):
@@ -6139,11 +6167,11 @@ def write_xattr(path, key, value):
+ [encodeFilename(path, True)])
try:
- p = subprocess.Popen(
+ p = Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
except EnvironmentError as e:
raise XAttrMetadataError(e.errno, e.strerror)
- stdout, stderr = process_communicate_or_kill(p)
+ stdout, stderr = p.communicate_or_kill()
stderr = stderr.decode('utf-8', 'replace')
if p.returncode != 0:
raise XAttrMetadataError(p.returncode, stderr)
diff --git a/yt_dlp/webvtt.py b/yt_dlp/webvtt.py
index cd936e7e5..962aa57ad 100644
--- a/yt_dlp/webvtt.py
+++ b/yt_dlp/webvtt.py
@@ -13,7 +13,7 @@ in RFC 8216 §3.5 <https://tools.ietf.org/html/rfc8216#section-3.5>.
import re
import io
-from .utils import int_or_none
+from .utils import int_or_none, timetuple_from_msec
from .compat import (
compat_str as str,
compat_Pattern,
@@ -124,11 +124,7 @@ def _format_ts(ts):
Convert an MPEG PES timestamp into a WebVTT timestamp.
This will lose sub-millisecond precision.
"""
- msec = int((ts + 45) // 90)
- secs, msec = divmod(msec, 1000)
- mins, secs = divmod(secs, 60)
- hrs, mins = divmod(mins, 60)
- return '%02u:%02u:%02u.%03u' % (hrs, mins, secs, msec)
+ return '%02u:%02u:%02u.%03u' % timetuple_from_msec(int((ts + 45) // 90))
class Block(object):