buffering in parallel, youtube now buffered -> streaming is fallback, currently broken for s3
This commit is contained in:
parent
fcb10f7d10
commit
b234b7ea81
5 changed files with 69 additions and 18 deletions
|
@ -25,7 +25,7 @@ state = {"current": None, "queue": [], "recent": [], "room": None, "server": "",
|
||||||
@sio.on("skip")
|
@sio.on("skip")
|
||||||
async def handle_skip():
|
async def handle_skip():
|
||||||
logger.info("Skipping current")
|
logger.info("Skipping current")
|
||||||
await state["current"].skip_current()
|
await state["current"].skip_current(state["queue"][0])
|
||||||
|
|
||||||
|
|
||||||
@sio.on("state")
|
@sio.on("state")
|
||||||
|
@ -55,6 +55,11 @@ async def handle_buffer(data):
|
||||||
await sio.emit("meta-info", {"uuid": data["uuid"], "meta": meta_info})
|
await sio.emit("meta-info", {"uuid": data["uuid"], "meta": meta_info})
|
||||||
|
|
||||||
|
|
||||||
|
async def buffer_and_report(entry):
|
||||||
|
meta_info = await sources[entry.source].buffer(entry)
|
||||||
|
await sio.emit("meta-info", {"uuid": entry.uuid, "meta": meta_info})
|
||||||
|
|
||||||
|
|
||||||
@sio.on("play")
|
@sio.on("play")
|
||||||
async def handle_play(data):
|
async def handle_play(data):
|
||||||
entry = Entry(**data)
|
entry = Entry(**data)
|
||||||
|
@ -62,9 +67,8 @@ async def handle_play(data):
|
||||||
f"Playing: {entry.artist} - {entry.title} [{entry.album}] ({entry.source}) for {entry.performer}"
|
f"Playing: {entry.artist} - {entry.title} [{entry.album}] ({entry.source}) for {entry.performer}"
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
meta_info = await sources[entry.source].buffer(entry)
|
|
||||||
await sio.emit("meta-info", {"uuid": data["uuid"], "meta": meta_info})
|
|
||||||
state["current"] = sources[entry.source]
|
state["current"] = sources[entry.source]
|
||||||
|
asyncio.create_task(buffer_and_report(entry))
|
||||||
await sources[entry.source].play(entry)
|
await sources[entry.source].play(entry)
|
||||||
except Exception:
|
except Exception:
|
||||||
print_exc()
|
print_exc()
|
||||||
|
|
|
@ -3,9 +3,9 @@ import asyncio
|
||||||
|
|
||||||
|
|
||||||
async def play_mpv(
|
async def play_mpv(
|
||||||
video: str, audio: str | None, options
|
video: str, audio: str | None, options: list[str] = list()
|
||||||
) -> asyncio.subprocess.Process:
|
) -> asyncio.subprocess.Process:
|
||||||
args = [*options, video] + ([f"--audio-file={audio}"] if audio else [])
|
args = ["--fullscreen", *options, video] + ([f"--audio-file={audio}"] if audio else [])
|
||||||
|
|
||||||
mpv_process = asyncio.create_subprocess_exec("mpv", *args)
|
mpv_process = asyncio.create_subprocess_exec("mpv", *args)
|
||||||
return await mpv_process
|
return await mpv_process
|
||||||
|
|
|
@ -57,12 +57,12 @@ class S3Source(Source):
|
||||||
mp3_file = self.downloaded_files[entry.uuid]["mp3"]
|
mp3_file = self.downloaded_files[entry.uuid]["mp3"]
|
||||||
|
|
||||||
self.player = await play_mpv(
|
self.player = await play_mpv(
|
||||||
cdg_file, mp3_file, ["--scale=oversample", "--fullscreen"]
|
cdg_file, mp3_file, ["--scale=oversample"]
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.player.wait()
|
await self.player.wait()
|
||||||
|
|
||||||
async def skip_current(self) -> None:
|
async def skip_current(self, entry) -> None:
|
||||||
await self.player.kill()
|
await self.player.kill()
|
||||||
|
|
||||||
@async_in_thread
|
@async_in_thread
|
||||||
|
|
|
@ -29,7 +29,7 @@ class Source:
|
||||||
async def play(self, entry: Entry) -> None:
|
async def play(self, entry: Entry) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def skip_current(self) -> None:
|
async def skip_current(self, entry: Entry) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def init_server(self) -> None:
|
async def init_server(self) -> None:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import shlex
|
import shlex
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from threading import Event, Lock
|
||||||
|
|
||||||
from pytube import YouTube, Search, Channel, innertube
|
from pytube import YouTube, Search, Channel, innertube
|
||||||
|
|
||||||
|
@ -15,24 +16,36 @@ class YoutubeSource(Source):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.innertube_client = innertube.InnerTube(client="WEB")
|
self.innertube_client = innertube.InnerTube(client="WEB")
|
||||||
self.channels = config["channels"] if "channels" in config else []
|
self.channels = config["channels"] if "channels" in config else []
|
||||||
|
self.tmp_dir = config["tmp_dir"] if "tmp_dir" in config else "/tmp/syng"
|
||||||
self.player: None | asyncio.subprocess.Process = None
|
self.player: None | asyncio.subprocess.Process = None
|
||||||
|
self.downloaded_files = {}
|
||||||
|
self.masterlock = Lock()
|
||||||
|
|
||||||
async def get_config(self):
|
async def get_config(self):
|
||||||
return {"channels": self.channels}
|
return {"channels": self.channels}
|
||||||
|
|
||||||
async def play(self, entry: Entry) -> None:
|
async def play(self, entry: Entry) -> None:
|
||||||
self.player = await play_mpv(
|
|
||||||
entry.id,
|
if entry.uuid in self.downloaded_files and "video" in self.downloaded_files[entry.uuid]:
|
||||||
None,
|
print("playing locally")
|
||||||
[
|
video_file = self.downloaded_files[entry.uuid]["video"]
|
||||||
"--script-opts=ytdl_hook-ytdl_path=yt-dlp,ytdl_hook-exclude='%.pls$'",
|
audio_file = self.downloaded_files[entry.uuid]["audio"]
|
||||||
"--ytdl-format=bestvideo[height<=720]+bestaudio/best[height<=720]",
|
self.player = await play_mpv(video_file, audio_file)
|
||||||
"--fullscreen",
|
else:
|
||||||
],
|
print("streaming")
|
||||||
)
|
self.player = await play_mpv(
|
||||||
|
entry.id,
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
"--script-opts=ytdl_hook-ytdl_path=yt-dlp,ytdl_hook-exclude='%.pls$'",
|
||||||
|
"--ytdl-format=bestvideo[height<=720]+bestaudio/best[height<=720]",
|
||||||
|
"--fullscreen",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
await self.player.wait()
|
await self.player.wait()
|
||||||
|
|
||||||
async def skip_current(self) -> None:
|
async def skip_current(self, entry) -> None:
|
||||||
await self.player.kill()
|
await self.player.kill()
|
||||||
|
|
||||||
@async_in_thread
|
@async_in_thread
|
||||||
|
@ -121,5 +134,39 @@ class YoutubeSource(Source):
|
||||||
pass
|
pass
|
||||||
return list_of_videos
|
return list_of_videos
|
||||||
|
|
||||||
|
@async_in_thread
|
||||||
|
def buffer(self, entry: Entry) -> dict:
|
||||||
|
print(f"Buffering {entry}")
|
||||||
|
with self.masterlock:
|
||||||
|
if entry.uuid in self.downloaded_files:
|
||||||
|
print(f"Already buffering {entry}")
|
||||||
|
return {}
|
||||||
|
self.downloaded_files[entry.uuid] = {}
|
||||||
|
|
||||||
|
yt = YouTube(entry.id)
|
||||||
|
|
||||||
|
streams = yt.streams
|
||||||
|
|
||||||
|
video_streams = streams.filter(
|
||||||
|
type="video",
|
||||||
|
custom_filter_functions=[lambda s: int(s.resolution[:-1]) <= 1080]
|
||||||
|
)
|
||||||
|
audio_streams = streams.filter(only_audio=True)
|
||||||
|
|
||||||
|
best_720_stream = sorted(video_streams, key=lambda s: int(s.resolution[:-1]) + (1 if s.is_progressive else 0))[-1]
|
||||||
|
best_audio_stream = sorted(audio_streams, key=lambda s: int(s.abr[:-4]))[-1]
|
||||||
|
|
||||||
|
print(best_720_stream)
|
||||||
|
print(best_audio_stream)
|
||||||
|
|
||||||
|
if not best_720_stream.is_progressive:
|
||||||
|
self.downloaded_files[entry.uuid]["audio"] = best_audio_stream.download(output_path=self.tmp_dir, filename_prefix=f"{entry.uuid}-audio")
|
||||||
|
else:
|
||||||
|
self.downloaded_files[entry.uuid]["audio"] = None
|
||||||
|
|
||||||
|
self.downloaded_files[entry.uuid]["video"] = best_720_stream.download(output_path=self.tmp_dir, filename_prefix=entry.uuid)
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
available_sources["youtube"] = YoutubeSource
|
available_sources["youtube"] = YoutubeSource
|
||||||
|
|
Loading…
Add table
Reference in a new issue