import asyncio import hashlib import logging import os import tempfile from pathlib import Path from .config import Config from .ffprobe import ffprobe from .Utils import StreamingError LOG = logging.getLogger("player.segments") class Segments: def __init__(self, download_path: str, index: int, duration: float, vconvert: bool, aconvert: bool): config = Config.get_instance() self.download_path = download_path self.index = int(index) self.duration = float(duration) self.vconvert = bool(vconvert) self.aconvert = bool(aconvert) self.vcodec = config.streamer_vcodec self.acodec = config.streamer_acodec # sadly due to unforeseen circumstances, we have to convert the video for now. self.vconvert = True self.aconvert = True async def stream(self, file: Path) -> bytes: try: ff = await ffprobe(file) except UnicodeDecodeError: pass tmpDir: str = tempfile.gettempdir() tmpFile = os.path.join(tmpDir, f'ytptube_stream.{hashlib.sha256(str(file).encode("utf-8")).hexdigest()}') if not os.path.exists(tmpFile): os.symlink(file, tmpFile) if self.index == 0: startTime: float = f"{0:.6f}" else: startTime: float = f"{self.duration * self.index:.6f}" fargs = [] fargs.append("-xerror") fargs.append("-hide_banner") fargs.append("-loglevel") fargs.append("error") fargs.append("-ss") fargs.append(str(startTime if startTime else "0.00000")) fargs.append("-t") fargs.append(str(f"{self.duration:.6f}")) fargs.append("-copyts") fargs.append("-i") fargs.append(f"file:{tmpFile}") fargs.append("-map_metadata") fargs.append("-1") # video section. if ff.has_video(): fargs.append("-pix_fmt") fargs.append("yuv420p") fargs.append("-g") fargs.append("52") fargs.append("-map") fargs.append("0:v:0") fargs.append("-strict") fargs.append("-2") fargs.append("-codec:v") fargs.append(self.vcodec if self.vconvert else "copy") # audio section. if ff.has_audio(): fargs.append("-map") fargs.append("0:a:0") fargs.append("-codec:a") fargs.append(self.acodec if self.aconvert else "copy") fargs.append("-sn") fargs.append("-muxdelay") fargs.append("0") fargs.append("-f") fargs.append("mpegts") fargs.append("pipe:1") LOG.debug(f"Streaming '{file}' segment '{self.index}'. ffmpeg: {' '.join(fargs)}") proc = await asyncio.subprocess.create_subprocess_exec( "ffmpeg", *fargs, stdin=asyncio.subprocess.DEVNULL, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) data, err = await proc.communicate() if 0 != proc.returncode: LOG.error(f"Failed to stream '{file}' segment '{self.index}'. {err.decode('utf-8')}.") msg = f"Failed to stream '{file}' segment '{self.index}'." raise StreamingError(msg) return data