From 10069098812cd11ebe0a63dbfa914713f2520c36 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Fri, 2 Aug 2019 01:33:33 +0200 Subject: [PATCH] Add /dlmusic command --- royalnet/audio/__init__.py | 3 +- royalnet/audio/ytdlfile.py | 1 + royalnet/audio/ytdlvorbis.py | 66 +++++++++++++++++++++++++++++++++++ royalnet/commands/__init__.py | 3 +- royalnet/commands/dlmusic.py | 31 ++++++++++++++++ royalnet/royalgames.py | 6 ++-- 6 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 royalnet/audio/ytdlvorbis.py create mode 100644 royalnet/commands/dlmusic.py diff --git a/royalnet/audio/__init__.py b/royalnet/audio/__init__.py index ff7942f5..6e5471c7 100644 --- a/royalnet/audio/__init__.py +++ b/royalnet/audio/__init__.py @@ -5,5 +5,6 @@ from .ytdlinfo import YtdlInfo from .ytdlfile import YtdlFile from .fileaudiosource import FileAudioSource from .ytdldiscord import YtdlDiscord +from .ytdlvorbis import YtdlVorbis -__all__ = ["playmodes", "YtdlInfo", "YtdlFile", "FileAudioSource", "YtdlDiscord"] +__all__ = ["playmodes", "YtdlInfo", "YtdlFile", "FileAudioSource", "YtdlDiscord", "YtdlVorbis"] diff --git a/royalnet/audio/ytdlfile.py b/royalnet/audio/ytdlfile.py index f19ab4f1..338d780e 100644 --- a/royalnet/audio/ytdlfile.py +++ b/royalnet/audio/ytdlfile.py @@ -28,6 +28,7 @@ class YtdlFile: return self.info is not None def is_downloaded(self) -> bool: + # FIXME: Weird characters break everything return self.filename is not None and os.path.exists(self.filename) @contextlib.contextmanager diff --git a/royalnet/audio/ytdlvorbis.py b/royalnet/audio/ytdlvorbis.py new file mode 100644 index 00000000..f1c9088b --- /dev/null +++ b/royalnet/audio/ytdlvorbis.py @@ -0,0 +1,66 @@ +import typing +import re +import ffmpeg +import os +from .ytdlinfo import YtdlInfo +from .ytdlfile import YtdlFile +from .fileaudiosource import FileAudioSource + + +class YtdlVorbis: + def __init__(self, ytdl_file: YtdlFile): + self.ytdl_file: YtdlFile = ytdl_file + self.vorbis_filename: typing.Optional[str] = None + self._fas_spawned: typing.List[FileAudioSource] = [] + + def pcm_available(self): + return self.vorbis_filename is not None and os.path.exists(self.vorbis_filename) + + def convert_to_vorbis(self) -> None: + if not self.ytdl_file.is_downloaded(): + raise FileNotFoundError("File hasn't been downloaded yet") + destination_filename = re.sub(r"\.[^.]+$", ".mp3", self.ytdl_file.filename) + out, err = ( + ffmpeg.input(self.ytdl_file.filename) + .output(destination_filename, format="mp3") + .overwrite_output() + .run() + ) + self.vorbis_filename = destination_filename + + def ready_up(self): + if not self.ytdl_file.has_info(): + self.ytdl_file.update_info() + if not self.ytdl_file.is_downloaded(): + self.ytdl_file.download_file() + if not self.pcm_available(): + self.convert_to_vorbis() + + def delete(self) -> None: + if self.pcm_available(): + for source in self._fas_spawned: + if not source.file.closed: + source.file.close() + os.remove(self.vorbis_filename) + self.vorbis_filename = None + self.ytdl_file.delete() + + @classmethod + def create_from_url(cls, url, **ytdl_args) -> typing.List["YtdlVorbis"]: + files = YtdlFile.download_from_url(url, **ytdl_args) + dfiles = [] + for file in files: + dfile = YtdlVorbis(file) + dfiles.append(dfile) + return dfiles + + @classmethod + def create_and_ready_from_url(cls, url, **ytdl_args) -> typing.List["YtdlVorbis"]: + dfiles = cls.create_from_url(url, **ytdl_args) + for dfile in dfiles: + dfile.ready_up() + return dfiles + + @property + def info(self) -> typing.Optional[YtdlInfo]: + return self.ytdl_file.info diff --git a/royalnet/commands/__init__.py b/royalnet/commands/__init__.py index f353c19e..f6712cdd 100644 --- a/royalnet/commands/__init__.py +++ b/royalnet/commands/__init__.py @@ -29,10 +29,11 @@ from .pause import PauseCommand from .queue import QueueCommand from .royalnetprofile import RoyalnetprofileCommand from .id import IdCommand +from .dlmusic import DlmusicCommand __all__ = ["NullCommand", "PingCommand", "ShipCommand", "SmecdsCommand", "CiaoruoziCommand", "ColorCommand", "SyncCommand", "DiarioCommand", "RageCommand", "DateparserCommand", "AuthorCommand", "ReminderCommand", "KvactiveCommand", "KvCommand", "KvrollCommand", "VideoinfoCommand", "SummonCommand", "PlayCommand", "SkipCommand", "PlaymodeCommand", "VideochannelCommand", "MissingCommand", "CvCommand", "PauseCommand", - "QueueCommand", "RoyalnetprofileCommand", "IdCommand"] + "QueueCommand", "RoyalnetprofileCommand", "IdCommand", "DlmusicCommand"] diff --git a/royalnet/commands/dlmusic.py b/royalnet/commands/dlmusic.py new file mode 100644 index 00000000..0c0f6980 --- /dev/null +++ b/royalnet/commands/dlmusic.py @@ -0,0 +1,31 @@ +import asyncio +import typing +import urllib.parse +from ..utils import Command, Call, asyncify +from ..audio import YtdlVorbis + + +ytdl_args = { + "format": "bestaudio", + "outtmpl": f"./downloads/%(epoch)s_%(title)s.%(ext)s" +} + + +seconds_before_deletion = 15*60 + + +class DlmusicCommand(Command): + + command_name = "dlmusic" + command_description = "Scarica un video." + command_syntax = "(url)" + + @classmethod + async def common(cls, call: Call): + url = call.args[0] + files: typing.List[YtdlVorbis] = await asyncify(YtdlVorbis.create_and_ready_from_url, url, **ytdl_args) + for file in files: + await call.reply(f"⬇️ https://scaleway.steffo.eu/musicbot_cache/{urllib.parse.quote(file.vorbis_filename)}") + await asyncio.sleep(seconds_before_deletion) + for file in files: + file.delete() diff --git a/royalnet/royalgames.py b/royalnet/royalgames.py index dc8c843b..7882a61e 100644 --- a/royalnet/royalgames.py +++ b/royalnet/royalgames.py @@ -18,17 +18,19 @@ log = logging.root stream_handler = logging.StreamHandler() stream_handler.formatter = logging.Formatter("{asctime}\t{name}\t{levelname}\t{message}", style="{") log.addHandler(stream_handler) -log.setLevel(logging.INFO) commands = [PingCommand, ShipCommand, SmecdsCommand, ColorCommand, CiaoruoziCommand, SyncCommand, DiarioCommand, RageCommand, ReminderCommand, KvactiveCommand, KvCommand, KvrollCommand, SummonCommand, PlayCommand, SkipCommand, PlaymodeCommand, VideochannelCommand, CvCommand, PauseCommand, QueueCommand, RoyalnetprofileCommand, VideoinfoCommand, - IdCommand] + IdCommand, DlmusicCommand] # noinspection PyUnreachableCode if __debug__: commands = [DebugCreateCommand, DateparserCommand, AuthorCommand, *commands] + log.setLevel(logging.DEBUG) +else: + log.setLevel(logging.INFO) address, port = "127.0.0.1", 1234