forked from LBRYCommunity/lbry-sdk
don't require ProactorEventLoop on Windows
fix linter errors
This commit is contained in:
parent
5ab634e375
commit
ac89ba9b8d
2 changed files with 40 additions and 28 deletions
|
@ -6,13 +6,12 @@ import pathlib
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import shutil
|
import shutil
|
||||||
import platform
|
import subprocess
|
||||||
|
|
||||||
import lbry.utils
|
import lbry.utils
|
||||||
from lbry.conf import TranscodeConfig
|
from lbry.conf import TranscodeConfig
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
DISABLED = platform.system() == "Windows"
|
|
||||||
|
|
||||||
|
|
||||||
class VideoFileAnalyzer:
|
class VideoFileAnalyzer:
|
||||||
|
@ -27,31 +26,47 @@ class VideoFileAnalyzer:
|
||||||
self._conf = conf
|
self._conf = conf
|
||||||
self._available_encoders = ""
|
self._available_encoders = ""
|
||||||
self._ffmpeg_installed = None
|
self._ffmpeg_installed = None
|
||||||
self._whichFFmpeg = None
|
self._which_ffmpeg = None
|
||||||
self._whichFFprobe = None
|
self._which_ffprobe = None
|
||||||
self._env_copy = dict(os.environ)
|
self._env_copy = dict(os.environ)
|
||||||
if lbry.utils.is_running_from_bundle():
|
if lbry.utils.is_running_from_bundle():
|
||||||
# handle the situation where PyInstaller overrides our runtime environment:
|
# handle the situation where PyInstaller overrides our runtime environment:
|
||||||
self._replace_or_pop_env('LD_LIBRARY_PATH')
|
self._replace_or_pop_env('LD_LIBRARY_PATH')
|
||||||
|
|
||||||
async def _execute(self, command, arguments):
|
async def _execute(self, command, arguments):
|
||||||
if DISABLED:
|
|
||||||
return "Disabled on Windows", -1
|
|
||||||
args = shlex.split(arguments)
|
args = shlex.split(arguments)
|
||||||
|
|
||||||
process = await asyncio.create_subprocess_exec(
|
# This create_subprocess_exec call is broken in Windows Python 3.7, but it's prettier than what's below.
|
||||||
command, *args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, env=self._env_copy
|
# The recommended fix is switching to ProactorEventLoop, but that breaks UDP in Linux Python 3.7.
|
||||||
)
|
# Test it again in Python 3.8, both Linux and Windows.
|
||||||
stdout, stderr = await process.communicate() # returns when the streams are closed
|
# process = await asyncio.create_subprocess_exec(
|
||||||
return stdout.decode(errors='replace') + stderr.decode(errors='replace'), process.returncode
|
# command, *args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, env=self._env_copy
|
||||||
|
# )
|
||||||
|
# stdout, stderr = await process.communicate() # returns when the streams are closed
|
||||||
|
# return stdout.decode(errors='replace') + stderr.decode(errors='replace'), process.returncode
|
||||||
|
|
||||||
|
def execute_internal():
|
||||||
|
args.insert(0, command)
|
||||||
|
try:
|
||||||
|
# if log.isEnabledFor(logging.DEBUG):
|
||||||
|
# log.debug("Executing: %s", " ".join(args))
|
||||||
|
with subprocess.Popen(
|
||||||
|
args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self._env_copy
|
||||||
|
) as process:
|
||||||
|
(stdout, stderr) = process.communicate() # blocks until the process exits
|
||||||
|
return stdout.decode(errors='replace') + stderr.decode(errors='replace'), process.returncode
|
||||||
|
except subprocess.SubprocessError as e:
|
||||||
|
return str(e), -1
|
||||||
|
|
||||||
|
return await asyncio.get_event_loop().run_in_executor(None, execute_internal)
|
||||||
|
|
||||||
async def _execute_ffmpeg(self, arguments):
|
async def _execute_ffmpeg(self, arguments):
|
||||||
assert self._ffmpeg_installed
|
assert self._ffmpeg_installed
|
||||||
return await self._execute(self._whichFFmpeg, arguments)
|
return await self._execute(self._which_ffmpeg, arguments)
|
||||||
|
|
||||||
async def _execute_ffprobe(self, arguments):
|
async def _execute_ffprobe(self, arguments):
|
||||||
assert self._ffmpeg_installed
|
assert self._ffmpeg_installed
|
||||||
return await self._execute(self._whichFFprobe, arguments)
|
return await self._execute(self._which_ffprobe, arguments)
|
||||||
|
|
||||||
async def _verify_executable(self, name):
|
async def _verify_executable(self, name):
|
||||||
try:
|
try:
|
||||||
|
@ -74,20 +89,20 @@ class VideoFileAnalyzer:
|
||||||
path += os.path.pathsep + os.path.join(getattr(self._conf, "data_dir"), "ffmpeg", "bin")
|
path += os.path.pathsep + os.path.join(getattr(self._conf, "data_dir"), "ffmpeg", "bin")
|
||||||
path += os.path.pathsep + self._env_copy.get("PATH", "")
|
path += os.path.pathsep + self._env_copy.get("PATH", "")
|
||||||
|
|
||||||
self._whichFFmpeg = shutil.which("ffmpeg", path=path)
|
self._which_ffmpeg = shutil.which("ffmpeg", path=path)
|
||||||
if not self._whichFFmpeg:
|
if not self._which_ffmpeg:
|
||||||
log.warning("Unable to locate ffmpeg executable. Path: %s", path)
|
log.warning("Unable to locate ffmpeg executable. Path: %s", path)
|
||||||
raise FileNotFoundError(f"Unable to locate ffmpeg executable. Path: {path}")
|
raise FileNotFoundError(f"Unable to locate ffmpeg executable. Path: {path}")
|
||||||
self._whichFFprobe = shutil.which("ffprobe", path=path)
|
self._which_ffprobe = shutil.which("ffprobe", path=path)
|
||||||
if not self._whichFFprobe:
|
if not self._which_ffprobe:
|
||||||
log.warning("Unable to locate ffprobe executable. Path: %s", path)
|
log.warning("Unable to locate ffprobe executable. Path: %s", path)
|
||||||
raise FileNotFoundError(f"Unable to locate ffprobe executable. Path: {path}")
|
raise FileNotFoundError(f"Unable to locate ffprobe executable. Path: {path}")
|
||||||
if os.path.dirname(self._whichFFmpeg) != os.path.dirname(self._whichFFprobe):
|
if os.path.dirname(self._which_ffmpeg) != os.path.dirname(self._which_ffprobe):
|
||||||
log.warning("ffmpeg and ffprobe are in different folders!")
|
log.warning("ffmpeg and ffprobe are in different folders!")
|
||||||
await self._verify_executable(self._whichFFprobe)
|
await self._verify_executable(self._which_ffprobe)
|
||||||
version = await self._verify_executable(self._whichFFmpeg)
|
version = await self._verify_executable(self._which_ffmpeg)
|
||||||
self._ffmpeg_installed = True
|
self._ffmpeg_installed = True
|
||||||
log.debug("Using %s at %s", version.splitlines()[0].split(" Copyright")[0], self._whichFFmpeg)
|
log.debug("Using %s at %s", version.splitlines()[0].split(" Copyright")[0], self._which_ffmpeg)
|
||||||
|
|
||||||
async def status(self, reset=False):
|
async def status(self, reset=False):
|
||||||
if reset:
|
if reset:
|
||||||
|
@ -100,7 +115,7 @@ class VideoFileAnalyzer:
|
||||||
pass
|
pass
|
||||||
return {
|
return {
|
||||||
"available": self._ffmpeg_installed,
|
"available": self._ffmpeg_installed,
|
||||||
"which": self._whichFFmpeg,
|
"which": self._which_ffmpeg,
|
||||||
"analyze_audio_volume": int(self._conf.volume_analysis_time) > 0
|
"analyze_audio_volume": int(self._conf.volume_analysis_time) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,12 +154,12 @@ class VideoFileAnalyzer:
|
||||||
bit_rate = float(scan_data["format"]["bit_rate"])
|
bit_rate = float(scan_data["format"]["bit_rate"])
|
||||||
else:
|
else:
|
||||||
bit_rate = os.stat(file_path).st_size / float(scan_data["format"]["duration"])
|
bit_rate = os.stat(file_path).st_size / float(scan_data["format"]["duration"])
|
||||||
log.debug(" Detected bitrate is %s Mbps. Allowed is %s Mbps",
|
log.debug(" Detected bitrate is %s Mbps. Allowed max: %s Mbps",
|
||||||
str(bit_rate / 1000000.0), str(bit_rate_max / 1000000.0))
|
str(bit_rate / 1000000.0), str(bit_rate_max / 1000000.0))
|
||||||
|
|
||||||
if bit_rate > bit_rate_max:
|
if bit_rate > bit_rate_max:
|
||||||
return "The bit rate is above the configured maximum. Actual: " \
|
return "The bit rate is above the configured maximum. Actual: " \
|
||||||
f"{bit_rate / 1000000.0} Mbps; Allowed: {bit_rate_max / 1000000.0} Mbps"
|
f"{bit_rate / 1000000.0} Mbps; Allowed max: {bit_rate_max / 1000000.0} Mbps"
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ def enable_logging():
|
||||||
|
|
||||||
handler = logging.StreamHandler(sys.stdout)
|
handler = logging.StreamHandler(sys.stdout)
|
||||||
handler.setLevel(logging.DEBUG)
|
handler.setLevel(logging.DEBUG)
|
||||||
formatter = logging.Formatter('%(message)s') # %(asctime)s - %(levelname)s -
|
formatter = logging.Formatter('%(message)s')
|
||||||
handler.setFormatter(formatter)
|
handler.setFormatter(formatter)
|
||||||
root.addHandler(handler)
|
root.addHandler(handler)
|
||||||
|
|
||||||
|
@ -49,9 +49,6 @@ def main():
|
||||||
video_file = sys.argv[1]
|
video_file = sys.argv[1]
|
||||||
conf = TranscodeConfig()
|
conf = TranscodeConfig()
|
||||||
analyzer = VideoFileAnalyzer(conf)
|
analyzer = VideoFileAnalyzer(conf)
|
||||||
if sys.version_info < (3, 8) and platform.system() == "Windows":
|
|
||||||
# TODO: remove after we move to requiring Python 3.8
|
|
||||||
asyncio.set_event_loop(asyncio.ProactorEventLoop())
|
|
||||||
try:
|
try:
|
||||||
asyncio.run(process_video(analyzer, video_file))
|
asyncio.run(process_video(analyzer, video_file))
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
|
Loading…
Reference in a new issue