Merge pull request #89 from arabcoders/dev

Video Streaming imporvements.
This commit is contained in:
Abdulmohsen 2024-04-21 00:06:07 +03:00 committed by GitHub
commit d639762001
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 34 additions and 28 deletions

4
.vscode/launch.json vendored
View file

@ -33,7 +33,7 @@
"YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads", "YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads",
"YTP_TEMP_PATH": "${workspaceFolder}/var/tmp", "YTP_TEMP_PATH": "${workspaceFolder}/var/tmp",
"YTP_URL_HOST": "http://localhost:8081", "YTP_URL_HOST": "http://localhost:8081",
"YTP_LOG_LEVEL": "DEBUG" "YTP_LOG_LEVEL": "DEBUG",
} }
}, },
{ {
@ -48,7 +48,7 @@
"YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads", "YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads",
"YTP_TEMP_PATH": "${workspaceFolder}/var/tmp", "YTP_TEMP_PATH": "${workspaceFolder}/var/tmp",
"YTP_URL_HOST": "http://localhost:8081", "YTP_URL_HOST": "http://localhost:8081",
"YTP_LOG_LEVEL": "DEBUG" "YTP_LOG_LEVEL": "DEBUG",
} }
}, },
{ {

View file

@ -28,6 +28,7 @@
"preferredcodec", "preferredcodec",
"preferredquality", "preferredquality",
"tmpfilename", "tmpfilename",
"vcodec",
"vconvert", "vconvert",
"writedescription", "writedescription",
"xerror" "xerror"

View file

@ -34,12 +34,8 @@ ENV YTP_PORT=8081
ENV XDG_CONFIG_HOME=/config ENV XDG_CONFIG_HOME=/config
ENV XDG_CACHE_HOME=/tmp ENV XDG_CACHE_HOME=/tmp
# removed ffmpeg as 6.1.0 is broken with DASH protocal downloads
COPY --from=mwader/static-ffmpeg:6.1.1 /ffmpeg /usr/bin/
COPY --from=mwader/static-ffmpeg:6.1.1 /ffprobe /usr/bin/
RUN mkdir /config /downloads && ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone && \ RUN mkdir /config /downloads && ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone && \
apk add --update --no-cache bash mkvtoolnix patch aria2 coreutils curl shadow sqlite tzdata libmagic && \ apk add --update --no-cache bash mkvtoolnix patch aria2 coreutils curl shadow sqlite tzdata libmagic ffmpeg && \
useradd -u ${USER_ID:-1000} -U -d /app -s /bin/bash app && \ useradd -u ${USER_ID:-1000} -U -d /app -s /bin/bash app && \
rm -rf /var/cache/apk/* rm -rf /var/cache/apk/*

View file

@ -75,6 +75,9 @@ Certain values can be set via environment variables, using the `-e` parameter on
* __YTP_PORT__: Which port to bind to. Defaults to `8081`. * __YTP_PORT__: Which port to bind to. Defaults to `8081`.
* __YTP_LOG_LEVEL__: Log level. Defaults to `info`. * __YTP_LOG_LEVEL__: Log level. Defaults to `info`.
* __YTP_MAX_WORKERS__: How many works to use for downloads. Defaults to `1`. * __YTP_MAX_WORKERS__: How many works to use for downloads. Defaults to `1`.
* __YTP_MAX_WORKERS__: How many works to use for downloads. Defaults to `1`.
* __YTP_STREAMER_VCODEC__: The video codec to use for in-browser streaming. Defaults to `libx264`.
* __YTP_STREAMER_ACODEC__: The audio codec to use for in-browser streaming. Defaults to `aac`.
## Running behind a reverse proxy ## Running behind a reverse proxy

View file

@ -53,6 +53,9 @@ class Config:
started: int = 0 started: int = 0
streamer_vcodec = 'libx264'
streamer_acodec = 'aac'
ytdlp_version: str = YTDLP_VERSION ytdlp_version: str = YTDLP_VERSION
_int_vars: tuple = ('port', 'max_workers', 'socket_timeout', 'extract_info_timeout',) _int_vars: tuple = ('port', 'max_workers', 'socket_timeout', 'extract_info_timeout',)

View file

@ -4,7 +4,9 @@ import asyncio
from datetime import datetime from datetime import datetime
import json import json
import os import os
import pathlib
import random import random
import time
from Config import Config from Config import Config
from DownloadQueue import DownloadQueue from DownloadQueue import DownloadQueue
from Utils import ObjectSerializer, Notifier from Utils import ObjectSerializer, Notifier
@ -18,6 +20,7 @@ import logging
import caribou import caribou
import sqlite3 import sqlite3
from aiocron import crontab from aiocron import crontab
from aiohttp.helpers import parse_http_date
import magic import magic
LOG = logging.getLogger('app') LOG = logging.getLogger('app')
@ -440,6 +443,11 @@ class Main:
vc: int = int(request.query.get('vc', 0)) vc: int = int(request.query.get('vc', 0))
ac: int = int(request.query.get('ac', 0)) ac: int = int(request.query.get('ac', 0))
if request.if_modified_since:
file_path = os.path.join(self.config.download_path, file)
if os.path.exists(file_path) and request.if_modified_since.timestamp() == os.path.getmtime(file_path):
return web.Response(status=304)
if not file: if not file:
raise web.HTTPBadRequest(reason='file is required') raise web.HTTPBadRequest(reason='file is required')
@ -457,10 +465,14 @@ class Main:
body=await segmenter.stream(path=self.config.download_path, file=file), body=await segmenter.stream(path=self.config.download_path, file=file),
headers={ headers={
'Content-Type': 'video/mpegts', 'Content-Type': 'video/mpegts',
'Cache-Control': 'no-cache',
'X-Accel-Buffering': 'no', 'X-Accel-Buffering': 'no',
'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Origin': '*',
'Access-Control-Max-Age': "300", 'Pragma': 'public',
'Cache-Control': f'public, max-age={time.time() + 31536000}',
'Last-Modified': time.strftime('%a, %d %b %Y %H:%M:%S GMT', datetime.fromtimestamp(
os.path.getmtime(os.path.join(self.config.download_path, file))).timetuple()
),
'Expires': time.strftime('%a, %d %b %Y %H:%M:%S GMT', datetime.fromtimestamp(time.time() + 31536000).timetuple()),
} }
) )

View file

@ -4,6 +4,7 @@ import logging
import os import os
import tempfile import tempfile
from Utils import calcDownloadPath from Utils import calcDownloadPath
from Config import Config
LOG = logging.getLogger('segments') LOG = logging.getLogger('segments')
@ -13,12 +14,18 @@ class Segments:
index: int index: int
vconvert: bool vconvert: bool
aconvert: bool aconvert: bool
vcodec: str
acodec: str
def __init__(self, index: int, duration: float, vconvert: bool, aconvert: bool): def __init__(self, index: int, duration: float, vconvert: bool, aconvert: bool):
config = Config.get_instance()
self.index = int(index) self.index = int(index)
self.duration = float(duration) self.duration = float(duration)
self.vconvert = bool(vconvert) self.vconvert = bool(vconvert)
self.aconvert = bool(aconvert) self.aconvert = bool(aconvert)
self.vcodec = config.streamer_vcodec
self.acodec = config.streamer_acodec
self.vconvert = True
async def stream(self, path: str, file: str) -> bytes: async def stream(self, path: str, file: str) -> bytes:
realFile: str = calcDownloadPath(basePath=path, folder=file, createPath=False) realFile: str = calcDownloadPath(basePath=path, folder=file, createPath=False)
@ -66,29 +73,13 @@ class Segments:
fargs.append('-2') fargs.append('-2')
fargs.append('-codec:v') fargs.append('-codec:v')
fargs.append('libx264' if self.vconvert else 'copy') fargs.append(self.vcodec if self.vconvert else 'copy')
if self.vconvert:
fargs.append('-crf')
fargs.append('23')
fargs.append('-preset:v')
fargs.append('fast')
fargs.append('-level')
fargs.append('4.1')
fargs.append('-profile:v')
fargs.append('baseline')
# audio section. # audio section.
fargs.append('-map') fargs.append('-map')
fargs.append('0:a:0') fargs.append('0:a:0')
fargs.append('-codec:a') fargs.append('-codec:a')
fargs.append('aac' if self.aconvert else 'copy') fargs.append(self.acodec if self.aconvert else 'copy')
if self.aconvert:
fargs.append('-b:a')
fargs.append('192k')
fargs.append('-ar')
fargs.append('22050')
fargs.append('-ac')
fargs.append('2')
fargs.append('-sn') fargs.append('-sn')
@ -110,7 +101,7 @@ class Segments:
data, err = await proc.communicate() data, err = await proc.communicate()
if 0 != proc.returncode: if 0 != proc.returncode:
LOG.error(f'Failed to stream {realFile} segment {self.index}. {err.decode("utf-8")}') LOG.error(f'Failed to stream {realFile} segment {self.index}. {err.decode("utf-8")}.')
raise Exception(f'Failed to stream {realFile} segment {self.index}.') raise Exception(f'Failed to stream {realFile} segment {self.index}.')
return data return data