mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-12-12 14:26:49 +01:00
parent
b4b855ebc7
commit
d3c93ec2b7
@ -87,10 +87,10 @@
|
|||||||
parse_filesize,
|
parse_filesize,
|
||||||
PerRequestProxyHandler,
|
PerRequestProxyHandler,
|
||||||
platform_name,
|
platform_name,
|
||||||
|
Popen,
|
||||||
PostProcessingError,
|
PostProcessingError,
|
||||||
preferredencoding,
|
preferredencoding,
|
||||||
prepend_extension,
|
prepend_extension,
|
||||||
process_communicate_or_kill,
|
|
||||||
register_socks_protocols,
|
register_socks_protocols,
|
||||||
RejectedVideoReached,
|
RejectedVideoReached,
|
||||||
render_table,
|
render_table,
|
||||||
@ -578,12 +578,9 @@ def check_deprecated(param, option, suggestion):
|
|||||||
stdout=slave,
|
stdout=slave,
|
||||||
stderr=self._err_file)
|
stderr=self._err_file)
|
||||||
try:
|
try:
|
||||||
self._output_process = subprocess.Popen(
|
self._output_process = Popen(['bidiv'] + width_args, **sp_kwargs)
|
||||||
['bidiv'] + width_args, **sp_kwargs
|
|
||||||
)
|
|
||||||
except OSError:
|
except OSError:
|
||||||
self._output_process = subprocess.Popen(
|
self._output_process = Popen(['fribidi', '-c', 'UTF-8'] + width_args, **sp_kwargs)
|
||||||
['fribidi', '-c', 'UTF-8'] + width_args, **sp_kwargs)
|
|
||||||
self._output_channel = os.fdopen(master, 'rb')
|
self._output_channel = os.fdopen(master, 'rb')
|
||||||
except OSError as ose:
|
except OSError as ose:
|
||||||
if ose.errno == errno.ENOENT:
|
if ose.errno == errno.ENOENT:
|
||||||
@ -3280,11 +3277,11 @@ def print_debug_header(self):
|
|||||||
if self.params.get('compat_opts'):
|
if self.params.get('compat_opts'):
|
||||||
write_debug('Compatibility options: %s\n' % ', '.join(self.params.get('compat_opts')))
|
write_debug('Compatibility options: %s\n' % ', '.join(self.params.get('compat_opts')))
|
||||||
try:
|
try:
|
||||||
sp = subprocess.Popen(
|
sp = Popen(
|
||||||
['git', 'rev-parse', '--short', 'HEAD'],
|
['git', 'rev-parse', '--short', 'HEAD'],
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||||
cwd=os.path.dirname(os.path.abspath(__file__)))
|
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()
|
out = out.decode().strip()
|
||||||
if re.match('[0-9a-f]+', out):
|
if re.match('[0-9a-f]+', out):
|
||||||
write_debug('Git HEAD: %s\n' % out)
|
write_debug('Git HEAD: %s\n' % out)
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
from .utils import (
|
from .utils import (
|
||||||
bug_reports_message,
|
bug_reports_message,
|
||||||
expand_path,
|
expand_path,
|
||||||
process_communicate_or_kill,
|
Popen,
|
||||||
YoutubeDLCookieJar,
|
YoutubeDLCookieJar,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -599,14 +599,14 @@ def _get_mac_keyring_password(browser_keyring_name, logger):
|
|||||||
return password.encode('utf-8')
|
return password.encode('utf-8')
|
||||||
else:
|
else:
|
||||||
logger.debug('using find-generic-password to obtain password')
|
logger.debug('using find-generic-password to obtain password')
|
||||||
proc = subprocess.Popen(['security', 'find-generic-password',
|
proc = Popen(
|
||||||
|
['security', 'find-generic-password',
|
||||||
'-w', # write password to stdout
|
'-w', # write password to stdout
|
||||||
'-a', browser_keyring_name, # match 'account'
|
'-a', browser_keyring_name, # match 'account'
|
||||||
'-s', '{} Safe Storage'.format(browser_keyring_name)], # match 'service'
|
'-s', '{} Safe Storage'.format(browser_keyring_name)], # match 'service'
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
|
||||||
stderr=subprocess.DEVNULL)
|
|
||||||
try:
|
try:
|
||||||
stdout, stderr = process_communicate_or_kill(proc)
|
stdout, stderr = proc.communicate_or_kill()
|
||||||
if stdout[-1:] == b'\n':
|
if stdout[-1:] == b'\n':
|
||||||
stdout = stdout[:-1]
|
stdout = stdout[:-1]
|
||||||
return stdout
|
return stdout
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
handle_youtubedl_headers,
|
handle_youtubedl_headers,
|
||||||
check_executable,
|
check_executable,
|
||||||
is_outdated_version,
|
is_outdated_version,
|
||||||
process_communicate_or_kill,
|
Popen,
|
||||||
sanitize_open,
|
sanitize_open,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -116,9 +116,8 @@ def _call_downloader(self, tmpfilename, info_dict):
|
|||||||
self._debug_cmd(cmd)
|
self._debug_cmd(cmd)
|
||||||
|
|
||||||
if 'fragments' not in info_dict:
|
if 'fragments' not in info_dict:
|
||||||
p = subprocess.Popen(
|
p = Popen(cmd, stderr=subprocess.PIPE)
|
||||||
cmd, stderr=subprocess.PIPE)
|
_, stderr = p.communicate_or_kill()
|
||||||
_, stderr = process_communicate_or_kill(p)
|
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
self.to_stderr(stderr.decode('utf-8', 'replace'))
|
self.to_stderr(stderr.decode('utf-8', 'replace'))
|
||||||
return p.returncode
|
return p.returncode
|
||||||
@ -128,9 +127,8 @@ def _call_downloader(self, tmpfilename, info_dict):
|
|||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
while count <= fragment_retries:
|
while count <= fragment_retries:
|
||||||
p = subprocess.Popen(
|
p = Popen(cmd, stderr=subprocess.PIPE)
|
||||||
cmd, stderr=subprocess.PIPE)
|
_, stderr = p.communicate_or_kill()
|
||||||
_, stderr = process_communicate_or_kill(p)
|
|
||||||
if p.returncode == 0:
|
if p.returncode == 0:
|
||||||
break
|
break
|
||||||
# TODO: Decide whether to retry based on error code
|
# TODO: Decide whether to retry based on error code
|
||||||
@ -199,8 +197,8 @@ def _call_downloader(self, tmpfilename, info_dict):
|
|||||||
self._debug_cmd(cmd)
|
self._debug_cmd(cmd)
|
||||||
|
|
||||||
# curl writes the progress to stderr so don't capture it.
|
# curl writes the progress to stderr so don't capture it.
|
||||||
p = subprocess.Popen(cmd)
|
p = Popen(cmd)
|
||||||
process_communicate_or_kill(p)
|
p.communicate_or_kill()
|
||||||
return p.returncode
|
return p.returncode
|
||||||
|
|
||||||
|
|
||||||
@ -476,7 +474,7 @@ def _call_downloader(self, tmpfilename, info_dict):
|
|||||||
args.append(encodeFilename(ffpp._ffmpeg_filename_argument(tmpfilename), True))
|
args.append(encodeFilename(ffpp._ffmpeg_filename_argument(tmpfilename), True))
|
||||||
self._debug_cmd(args)
|
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:'):
|
if url in ('-', 'pipe:'):
|
||||||
self.on_process_started(proc, proc.stdin)
|
self.on_process_started(proc, proc.stdin)
|
||||||
try:
|
try:
|
||||||
@ -488,7 +486,7 @@ def _call_downloader(self, tmpfilename, info_dict):
|
|||||||
# streams). Note that Windows is not affected and produces playable
|
# streams). Note that Windows is not affected and produces playable
|
||||||
# files (see https://github.com/ytdl-org/youtube-dl/issues/8300).
|
# files (see https://github.com/ytdl-org/youtube-dl/issues/8300).
|
||||||
if isinstance(e, KeyboardInterrupt) and sys.platform != 'win32' and url not in ('-', 'pipe:'):
|
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:
|
else:
|
||||||
proc.kill()
|
proc.kill()
|
||||||
proc.wait()
|
proc.wait()
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
encodeFilename,
|
encodeFilename,
|
||||||
encodeArgument,
|
encodeArgument,
|
||||||
get_exe_version,
|
get_exe_version,
|
||||||
|
Popen,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ def run_rtmpdump(args):
|
|||||||
start = time.time()
|
start = time.time()
|
||||||
resume_percent = None
|
resume_percent = None
|
||||||
resume_downloaded_data_len = None
|
resume_downloaded_data_len = None
|
||||||
proc = subprocess.Popen(args, stderr=subprocess.PIPE)
|
proc = Popen(args, stderr=subprocess.PIPE)
|
||||||
cursor_in_new_line = True
|
cursor_in_new_line = True
|
||||||
proc_stderr_closed = False
|
proc_stderr_closed = False
|
||||||
try:
|
try:
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
get_exe_version,
|
get_exe_version,
|
||||||
is_outdated_version,
|
is_outdated_version,
|
||||||
std_headers,
|
std_headers,
|
||||||
process_communicate_or_kill,
|
Popen,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -223,11 +223,10 @@ def get(self, url, html=None, video_id=None, note=None, note2='Executing JS on w
|
|||||||
else:
|
else:
|
||||||
self.extractor.to_screen('%s: %s' % (video_id, note2))
|
self.extractor.to_screen('%s: %s' % (video_id, note2))
|
||||||
|
|
||||||
p = subprocess.Popen([
|
p = Popen(
|
||||||
self.exe, '--ssl-protocol=any',
|
[self.exe, '--ssl-protocol=any', self._TMP_FILES['script'].name],
|
||||||
self._TMP_FILES['script'].name
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
out, err = p.communicate_or_kill()
|
||||||
out, err = process_communicate_or_kill(p)
|
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
raise ExtractorError(
|
raise ExtractorError(
|
||||||
'Executing JS failed\n:' + encodeArgument(err))
|
'Executing JS failed\n:' + encodeArgument(err))
|
||||||
|
@ -26,9 +26,9 @@
|
|||||||
encodeArgument,
|
encodeArgument,
|
||||||
encodeFilename,
|
encodeFilename,
|
||||||
error_to_compat_str,
|
error_to_compat_str,
|
||||||
|
Popen,
|
||||||
PostProcessingError,
|
PostProcessingError,
|
||||||
prepend_extension,
|
prepend_extension,
|
||||||
process_communicate_or_kill,
|
|
||||||
shell_quote,
|
shell_quote,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -183,8 +183,8 @@ def run(self, info):
|
|||||||
|
|
||||||
self._report_run('atomicparsley', filename)
|
self._report_run('atomicparsley', filename)
|
||||||
self.write_debug('AtomicParsley command line: %s' % shell_quote(cmd))
|
self.write_debug('AtomicParsley command line: %s' % shell_quote(cmd))
|
||||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
p = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
stdout, stderr = process_communicate_or_kill(p)
|
stdout, stderr = p.communicate_or_kill()
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
msg = stderr.decode('utf-8', 'replace').strip()
|
msg = stderr.decode('utf-8', 'replace').strip()
|
||||||
raise EmbedThumbnailPPError(msg)
|
raise EmbedThumbnailPPError(msg)
|
||||||
|
@ -20,9 +20,9 @@
|
|||||||
is_outdated_version,
|
is_outdated_version,
|
||||||
ISO639Utils,
|
ISO639Utils,
|
||||||
orderedSet,
|
orderedSet,
|
||||||
|
Popen,
|
||||||
PostProcessingError,
|
PostProcessingError,
|
||||||
prepend_extension,
|
prepend_extension,
|
||||||
process_communicate_or_kill,
|
|
||||||
replace_extension,
|
replace_extension,
|
||||||
shell_quote,
|
shell_quote,
|
||||||
traverse_obj,
|
traverse_obj,
|
||||||
@ -178,10 +178,8 @@ def get_audio_codec(self, path):
|
|||||||
encodeArgument('-i')]
|
encodeArgument('-i')]
|
||||||
cmd.append(encodeFilename(self._ffmpeg_filename_argument(path), True))
|
cmd.append(encodeFilename(self._ffmpeg_filename_argument(path), True))
|
||||||
self.write_debug('%s command line: %s' % (self.basename, shell_quote(cmd)))
|
self.write_debug('%s command line: %s' % (self.basename, shell_quote(cmd)))
|
||||||
handle = subprocess.Popen(
|
handle = Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
cmd, stderr=subprocess.PIPE,
|
stdout_data, stderr_data = handle.communicate_or_kill()
|
||||||
stdout=subprocess.PIPE, stdin=subprocess.PIPE)
|
|
||||||
stdout_data, stderr_data = process_communicate_or_kill(handle)
|
|
||||||
expected_ret = 0 if self.probe_available else 1
|
expected_ret = 0 if self.probe_available else 1
|
||||||
if handle.wait() != expected_ret:
|
if handle.wait() != expected_ret:
|
||||||
return None
|
return None
|
||||||
@ -223,7 +221,7 @@ def get_metadata_object(self, path, opts=[]):
|
|||||||
cmd += opts
|
cmd += opts
|
||||||
cmd.append(encodeFilename(self._ffmpeg_filename_argument(path), True))
|
cmd.append(encodeFilename(self._ffmpeg_filename_argument(path), True))
|
||||||
self.write_debug('ffprobe command line: %s' % shell_quote(cmd))
|
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()
|
stdout, stderr = p.communicate()
|
||||||
return json.loads(stdout.decode('utf-8', 'replace'))
|
return json.loads(stdout.decode('utf-8', 'replace'))
|
||||||
|
|
||||||
@ -284,8 +282,8 @@ def make_args(file, args, name, number):
|
|||||||
for i, (path, opts) in enumerate(path_opts) if path)
|
for i, (path, opts) in enumerate(path_opts) if path)
|
||||||
|
|
||||||
self.write_debug('ffmpeg command line: %s' % shell_quote(cmd))
|
self.write_debug('ffmpeg 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 = process_communicate_or_kill(p)
|
stdout, stderr = p.communicate_or_kill()
|
||||||
if p.returncode not in variadic(expected_retcodes):
|
if p.returncode not in variadic(expected_retcodes):
|
||||||
stderr = stderr.decode('utf-8', 'replace').strip()
|
stderr = stderr.decode('utf-8', 'replace').strip()
|
||||||
self.write_debug(stderr)
|
self.write_debug(stderr)
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
encodeFilename,
|
encodeFilename,
|
||||||
shell_quote,
|
shell_quote,
|
||||||
str_or_none,
|
str_or_none,
|
||||||
|
Popen,
|
||||||
PostProcessingError,
|
PostProcessingError,
|
||||||
prepend_extension,
|
prepend_extension,
|
||||||
process_communicate_or_kill,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -81,8 +81,8 @@ def run(self, information):
|
|||||||
|
|
||||||
self.write_debug('sponskrub command line: %s' % shell_quote(cmd))
|
self.write_debug('sponskrub command line: %s' % shell_quote(cmd))
|
||||||
pipe = None if self.get_param('verbose') else subprocess.PIPE
|
pipe = None if self.get_param('verbose') else subprocess.PIPE
|
||||||
p = subprocess.Popen(cmd, stdout=pipe)
|
p = Popen(cmd, stdout=pipe)
|
||||||
stdout = process_communicate_or_kill(p)[0]
|
stdout = p.communicate_or_kill()[0]
|
||||||
|
|
||||||
if p.returncode == 0:
|
if p.returncode == 0:
|
||||||
os.replace(temp_filename, filename)
|
os.replace(temp_filename, filename)
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
from zipimport import zipimporter
|
from zipimport import zipimporter
|
||||||
|
|
||||||
from .compat import compat_realpath
|
from .compat import compat_realpath
|
||||||
from .utils import encode_compat_str
|
from .utils import encode_compat_str, Popen
|
||||||
|
|
||||||
from .version import __version__
|
from .version import __version__
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ def get_sha256sum(bin_or_exe, version):
|
|||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
# Continues to run in the background
|
# Continues to run in the background
|
||||||
subprocess.Popen(
|
Popen(
|
||||||
'ping 127.0.0.1 -n 5 -w 1000 & del /F "%s.old"' % exe,
|
'ping 127.0.0.1 -n 5 -w 1000 & del /F "%s.old"' % exe,
|
||||||
shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
ydl.to_screen('Updated yt-dlp to version %s' % version_id)
|
ydl.to_screen('Updated yt-dlp to version %s' % version_id)
|
||||||
|
@ -2272,6 +2272,20 @@ def process_communicate_or_kill(p, *args, **kwargs):
|
|||||||
raise
|
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():
|
def get_subprocess_encoding():
|
||||||
if sys.platform == 'win32' and sys.getwindowsversion()[0] >= 5:
|
if sys.platform == 'win32' and sys.getwindowsversion()[0] >= 5:
|
||||||
# For subprocess calls, encode with locale encoding
|
# For subprocess calls, encode with locale encoding
|
||||||
@ -3977,8 +3991,7 @@ def check_executable(exe, args=[]):
|
|||||||
""" Checks if the given binary is installed somewhere in PATH, and returns its name.
|
""" 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) """
|
args can be a list of arguments for a short output (like -version) """
|
||||||
try:
|
try:
|
||||||
process_communicate_or_kill(subprocess.Popen(
|
Popen([exe] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate_or_kill()
|
||||||
[exe] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE))
|
|
||||||
except OSError:
|
except OSError:
|
||||||
return False
|
return False
|
||||||
return exe
|
return exe
|
||||||
@ -3992,10 +4005,9 @@ def get_exe_version(exe, args=['--version'],
|
|||||||
# STDIN should be redirected too. On UNIX-like systems, ffmpeg triggers
|
# STDIN should be redirected too. On UNIX-like systems, ffmpeg triggers
|
||||||
# SIGTTOU if yt-dlp is run in the background.
|
# SIGTTOU if yt-dlp is run in the background.
|
||||||
# See https://github.com/ytdl-org/youtube-dl/issues/955#issuecomment-209789656
|
# See https://github.com/ytdl-org/youtube-dl/issues/955#issuecomment-209789656
|
||||||
out, _ = process_communicate_or_kill(subprocess.Popen(
|
out, _ = Popen(
|
||||||
[encodeArgument(exe)] + args,
|
[encodeArgument(exe)] + args, stdin=subprocess.PIPE,
|
||||||
stdin=subprocess.PIPE,
|
stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate_or_kill()
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
|
||||||
except OSError:
|
except OSError:
|
||||||
return False
|
return False
|
||||||
if isinstance(out, bytes): # Python 2.x
|
if isinstance(out, bytes): # Python 2.x
|
||||||
@ -6155,11 +6167,11 @@ def write_xattr(path, key, value):
|
|||||||
+ [encodeFilename(path, True)])
|
+ [encodeFilename(path, True)])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
p = subprocess.Popen(
|
p = Popen(
|
||||||
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
||||||
except EnvironmentError as e:
|
except EnvironmentError as e:
|
||||||
raise XAttrMetadataError(e.errno, e.strerror)
|
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')
|
stderr = stderr.decode('utf-8', 'replace')
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
raise XAttrMetadataError(p.returncode, stderr)
|
raise XAttrMetadataError(p.returncode, stderr)
|
||||||
|
Loading…
Reference in New Issue
Block a user