mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-27 13:34:28 +00:00
yes yes yes
This commit is contained in:
parent
a86f2edd0e
commit
c925bf9360
27 changed files with 48 additions and 4 deletions
|
@ -34,6 +34,7 @@ class PlayEvent(Event):
|
||||||
break
|
break
|
||||||
if guild is None:
|
if guild is None:
|
||||||
raise InvalidInputError("No guild_id or guild_name specified.")
|
raise InvalidInputError("No guild_id or guild_name specified.")
|
||||||
|
log.debug(f"Selected guild: {guild}")
|
||||||
# Find the bard
|
# Find the bard
|
||||||
bard: Optional[DiscordBard] = self.serf.bards.get(guild)
|
bard: Optional[DiscordBard] = self.serf.bards.get(guild)
|
||||||
if bard is None:
|
if bard is None:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from typing import Optional, List, Dict, Any
|
from typing import Optional, List, Dict, Any
|
||||||
from royalnet.utils import asyncify, MultiLock
|
from royalnet.utils import asyncify, MultiLock
|
||||||
|
@ -12,6 +13,9 @@ except ImportError:
|
||||||
youtube_dl = None
|
youtube_dl = None
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class YtdlFile:
|
class YtdlFile:
|
||||||
"""A representation of a file download with :mod:`youtube_dl`."""
|
"""A representation of a file download with :mod:`youtube_dl`."""
|
||||||
|
|
||||||
|
@ -71,11 +75,13 @@ class YtdlFile:
|
||||||
"""Download function block to be asyncified."""
|
"""Download function block to be asyncified."""
|
||||||
with YoutubeDL(self.ytdl_args) as ytdl:
|
with YoutubeDL(self.ytdl_args) as ytdl:
|
||||||
filename = ytdl.prepare_filename(self.info.__dict__)
|
filename = ytdl.prepare_filename(self.info.__dict__)
|
||||||
|
with YoutubeDL({**self.ytdl_args, "outtmpl": filename}) as ytdl:
|
||||||
ytdl.download([self.info.webpage_url])
|
ytdl.download([self.info.webpage_url])
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
|
|
||||||
await self.retrieve_info()
|
await self.retrieve_info()
|
||||||
async with self.lock.exclusive():
|
async with self.lock.exclusive():
|
||||||
|
log.debug(f"Downloading with youtube-dl: {self}")
|
||||||
await asyncify(download, loop=self._loop)
|
await asyncify(download, loop=self._loop)
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
|
@ -91,14 +97,19 @@ class YtdlFile:
|
||||||
"""
|
"""
|
||||||
await self.download_file()
|
await self.download_file()
|
||||||
async with self.lock.normal():
|
async with self.lock.normal():
|
||||||
|
log.debug(f"File opened: {self.filename}")
|
||||||
with open(self.filename, "rb") as file:
|
with open(self.filename, "rb") as file:
|
||||||
yield file
|
yield file
|
||||||
|
log.debug(f"File closed: {self.filename}")
|
||||||
|
|
||||||
async def delete_asap(self):
|
async def delete_asap(self):
|
||||||
"""As soon as nothing is using the file, delete it."""
|
"""As soon as nothing is using the file, delete it."""
|
||||||
async with self.lock.exclusive():
|
log.debug(f"Trying to delete: {self.filename}")
|
||||||
os.remove(self.filename)
|
if self.filename is not None:
|
||||||
self.filename = None
|
async with self.lock.exclusive():
|
||||||
|
os.remove(self.filename)
|
||||||
|
log.debug(f"Deleted: {self.filename}")
|
||||||
|
self.filename = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def from_url(cls, url: str, **ytdl_args) -> List["YtdlFile"]:
|
async def from_url(cls, url: str, **ytdl_args) -> List["YtdlFile"]:
|
||||||
|
|
|
@ -2,6 +2,7 @@ from asyncio import AbstractEventLoop, get_event_loop
|
||||||
from typing import Optional, Dict, List, Any
|
from typing import Optional, Dict, List, Any
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import dateparser
|
import dateparser
|
||||||
|
import logging
|
||||||
from royalnet.utils import ytdldateformat, asyncify
|
from royalnet.utils import ytdldateformat, asyncify
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -10,6 +11,9 @@ except ImportError:
|
||||||
YoutubeDL = None
|
YoutubeDL = None
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class YtdlInfo:
|
class YtdlInfo:
|
||||||
"""A wrapper around youtube_dl extracted info."""
|
"""A wrapper around youtube_dl extracted info."""
|
||||||
|
|
||||||
|
@ -95,6 +99,7 @@ class YtdlInfo:
|
||||||
if loop is None:
|
if loop is None:
|
||||||
loop: AbstractEventLoop = get_event_loop()
|
loop: AbstractEventLoop = get_event_loop()
|
||||||
# So many redundant options!
|
# So many redundant options!
|
||||||
|
log.debug(f"Fetching info: {url}")
|
||||||
with YoutubeDL({**cls._default_ytdl_args, **ytdl_args}) as ytdl:
|
with YoutubeDL({**cls._default_ytdl_args, **ytdl_args}) as ytdl:
|
||||||
first_info = await asyncify(ytdl.extract_info, loop=loop, url=url, download=False)
|
first_info = await asyncify(ytdl.extract_info, loop=loop, url=url, download=False)
|
||||||
# No video was found
|
# No video was found
|
||||||
|
@ -102,12 +107,14 @@ class YtdlInfo:
|
||||||
return []
|
return []
|
||||||
# If it is a playlist, create multiple videos!
|
# If it is a playlist, create multiple videos!
|
||||||
if "entries" in first_info and first_info["entries"][0] is not None:
|
if "entries" in first_info and first_info["entries"][0] is not None:
|
||||||
|
log.debug(f"Found a playlist: {url}")
|
||||||
second_info_list = []
|
second_info_list = []
|
||||||
for second_info in first_info["entries"]:
|
for second_info in first_info["entries"]:
|
||||||
if second_info is None:
|
if second_info is None:
|
||||||
continue
|
continue
|
||||||
second_info_list.append(YtdlInfo(second_info))
|
second_info_list.append(YtdlInfo(second_info))
|
||||||
return second_info_list
|
return second_info_list
|
||||||
|
log.debug(f"Found a single video: {url}")
|
||||||
return [YtdlInfo(first_info)]
|
return [YtdlInfo(first_info)]
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -50,7 +50,7 @@ class DiscordBard:
|
||||||
"""Get the next :class:`FileAudioSource` that should be played, and change :attr:`.now_playing`.
|
"""Get the next :class:`FileAudioSource` that should be played, and change :attr:`.now_playing`.
|
||||||
|
|
||||||
Args and kwargs can be passed to the generator to select differently."""
|
Args and kwargs can be passed to the generator to select differently."""
|
||||||
fas: Optional[FileAudioSource] = await self.generator.asend((args, kwargs))
|
fas: Optional[FileAudioSource] = await self.generator.asend((args, kwargs,))
|
||||||
self.now_playing = fas
|
self.now_playing = fas
|
||||||
return fas
|
return fas
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import typing
|
import typing
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from royalnet.utils import asyncify, MultiLock
|
from royalnet.utils import asyncify, MultiLock
|
||||||
from royalnet.bard import YtdlInfo, YtdlFile
|
from royalnet.bard import YtdlInfo, YtdlFile
|
||||||
|
@ -16,6 +17,9 @@ except ImportError:
|
||||||
ffmpeg = None
|
ffmpeg = None
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class YtdlDiscord:
|
class YtdlDiscord:
|
||||||
"""A representation of a YtdlFile conversion to the :mod:`discord` PCM format."""
|
"""A representation of a YtdlFile conversion to the :mod:`discord` PCM format."""
|
||||||
def __init__(self, ytdl_file: YtdlFile):
|
def __init__(self, ytdl_file: YtdlFile):
|
||||||
|
@ -37,6 +41,7 @@ class YtdlDiscord:
|
||||||
async with self.ytdl_file.lock.normal():
|
async with self.ytdl_file.lock.normal():
|
||||||
destination_filename = re.sub(r"\.[^.]+$", ".pcm", self.ytdl_file.filename)
|
destination_filename = re.sub(r"\.[^.]+$", ".pcm", self.ytdl_file.filename)
|
||||||
async with self.lock.exclusive():
|
async with self.lock.exclusive():
|
||||||
|
log.debug(f"Converting to PCM: {self.ytdl_file.filename}")
|
||||||
await asyncify(
|
await asyncify(
|
||||||
ffmpeg.input(self.ytdl_file.filename)
|
ffmpeg.input(self.ytdl_file.filename)
|
||||||
.output(destination_filename, format="s16le", ac=2, ar="48000")
|
.output(destination_filename, format="s16le", ac=2, ar="48000")
|
||||||
|
@ -47,9 +52,11 @@ class YtdlDiscord:
|
||||||
|
|
||||||
async def delete_asap(self) -> None:
|
async def delete_asap(self) -> None:
|
||||||
"""Delete the mp3 file."""
|
"""Delete the mp3 file."""
|
||||||
|
log.debug(f"Trying to delete: {self}")
|
||||||
if self.is_converted:
|
if self.is_converted:
|
||||||
async with self.lock.exclusive():
|
async with self.lock.exclusive():
|
||||||
os.remove(self.pcm_filename)
|
os.remove(self.pcm_filename)
|
||||||
|
log.debug(f"Deleted: {self.pcm_filename}")
|
||||||
self.pcm_filename = None
|
self.pcm_filename = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -69,6 +76,7 @@ class YtdlDiscord:
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def spawn_audiosource(self):
|
async def spawn_audiosource(self):
|
||||||
|
log.debug(f"Spawning audio_source for: {self}")
|
||||||
if FileAudioSource is None:
|
if FileAudioSource is None:
|
||||||
raise ImportError("'discord' extra is not installed")
|
raise ImportError("'discord' extra is not installed")
|
||||||
await self.convert_to_pcm()
|
await self.convert_to_pcm()
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
from asyncio import Event
|
from asyncio import Event
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class MultiLock:
|
class MultiLock:
|
||||||
|
@ -8,6 +12,7 @@ class MultiLock:
|
||||||
self._counter: int = 0
|
self._counter: int = 0
|
||||||
self._normal_event: Event = Event()
|
self._normal_event: Event = Event()
|
||||||
self._exclusive_event: Event = Event()
|
self._exclusive_event: Event = Event()
|
||||||
|
self._normal_event.set()
|
||||||
self._exclusive_event.set()
|
self._exclusive_event.set()
|
||||||
|
|
||||||
def _check_event(self):
|
def _check_event(self):
|
||||||
|
@ -19,23 +24,35 @@ class MultiLock:
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def normal(self):
|
async def normal(self):
|
||||||
"""Acquire the lock for simultaneous access."""
|
"""Acquire the lock for simultaneous access."""
|
||||||
|
log.debug(f"Waiting for exclusive lock end: {self}")
|
||||||
await self._exclusive_event.wait()
|
await self._exclusive_event.wait()
|
||||||
|
log.debug(f"Acquiring normal lock: {self}")
|
||||||
self._counter += 1
|
self._counter += 1
|
||||||
self._check_event()
|
self._check_event()
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
|
log.debug(f"Releasing normal lock: {self}")
|
||||||
self._counter -= 1
|
self._counter -= 1
|
||||||
self._check_event()
|
self._check_event()
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def exclusive(self):
|
async def exclusive(self):
|
||||||
"""Acquire the lock for exclusive access."""
|
"""Acquire the lock for exclusive access."""
|
||||||
|
log.debug(f"Waiting for exclusive lock end: {self}")
|
||||||
# TODO: check if this actually works
|
# TODO: check if this actually works
|
||||||
await self._exclusive_event.wait()
|
await self._exclusive_event.wait()
|
||||||
self._exclusive_event.clear()
|
self._exclusive_event.clear()
|
||||||
|
log.debug(f"Waiting for normal lock end: {self}")
|
||||||
await self._normal_event.wait()
|
await self._normal_event.wait()
|
||||||
try:
|
try:
|
||||||
|
log.debug("Acquiring exclusive lock: {self}")
|
||||||
|
self._exclusive_event.clear()
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
|
log.debug("Releasing exclusive lock: {self}")
|
||||||
self._exclusive_event.set()
|
self._exclusive_event.set()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<MultiLock {self._counter} " \
|
||||||
|
f"{'N' if self._normal_event.is_set() else '_'}{'E' if self._exclusive_event.is_set() else '_'}>"
|
||||||
|
|
Loading…
Reference in a new issue