111 lines
3.3 KiB
Python
111 lines
3.3 KiB
Python
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
|