1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-11-23 11:34:18 +00:00

Pretty sure this was a bad idea

This commit is contained in:
Steffo 2019-01-05 00:45:01 +01:00
parent 30a951c69e
commit 279de8efe4
2 changed files with 155 additions and 139 deletions

View file

@ -32,8 +32,9 @@ logger = logging.getLogger(__name__)
os.environ["COLOREDLOGS_LOG_FORMAT"] = "%(asctime)s %(levelname)s %(name)s %(message)s"
coloredlogs.install(level="DEBUG", logger=logger)
# Number emojis from one to ten
number_emojis = [":one:",
# Number emojis from zero (now playing) to ten
number_emojis = [":arrow_forward:",
":one:",
":two:",
":three:",
":four:",
@ -128,6 +129,7 @@ class YoutubeDLVideo(Video):
"""Get info about the video."""
if self.info:
return
logger.debug(f"Getting info on {self.url}...")
with youtube_dl.YoutubeDL({"quiet": True,
"ignoreerrors": True,
"simulate": True}) as ytdl:
@ -193,9 +195,13 @@ class YoutubeDLVideo(Video):
return
# Ensure the video has info
self.get_info()
# Ensure the video is from youtube
if self.info["extractor"] != "youtube":
# TODO: add more websites?
self.suggestion = NotImplemented
# Log the attempt
logger.debug(f"Getting a suggestion for {self.url}...")
# Check for the api key
if self.youtube_api_key is None:
raise errors.MissingAPIKeyError()
# Request search data (costs 100 API units)
@ -231,19 +237,13 @@ class VideoQueue:
def __init__(self):
self.list: typing.List[Video] = []
self.now_playing: typing.Optional[Video] = None
self.loop_mode = LoopMode.NORMAL
def __len__(self) -> int:
return len(self.list)
def __next__(self) -> Video:
video = self.next_video()
self.advance_queue()
return video
def __repr__(self) -> str:
return f"<VideoQueue of length {len(self)}>"
return f"<VideoQueue of length {len(self)} in mode {self.loop_mode}>"
def add(self, video: Video, position: int = None) -> None:
if position is None:
@ -251,44 +251,28 @@ class VideoQueue:
return
self.list.insert(position, video)
def pop(self, index) -> Video:
if self.loop_mode == LoopMode.FOLLOW_SUGGESTIONS:
raise errors.LoopModeError("Can't pop items from a suggestion queue.")
result = self[index]
self.list.remove(result)
return result
def advance_queue(self):
"""Advance the queue to the next video."""
if self.loop_mode == LoopMode.NORMAL:
try:
self.now_playing = self.list.pop(0)
except IndexError:
self.now_playing = None
elif self.loop_mode == LoopMode.LOOP_QUEUE:
self.add(self.list[0])
self.now_playing = self.list.pop(0)
elif self.loop_mode == LoopMode.LOOP_SINGLE:
pass
elif self.loop_mode == LoopMode.FOLLOW_SUGGESTIONS:
if self.now_playing is None:
if len(self.list) > 0:
self.now_playing = self.list.pop(0)
else:
return
self.now_playing.get_suggestion()
self.now_playing = self.now_playing.suggestion
elif self.loop_mode == LoopMode.AUTO_SHUFFLE:
self.shuffle()
try:
self.now_playing = self.list.pop(0)
except IndexError:
self.now_playing = None
elif self.loop_mode == LoopMode.LOOPING_SHUFFLE:
self.shuffle()
self.add(self.list[0])
self.now_playing = self.list.pop(0)
del self[0]
def next_video(self) -> typing.Optional[Video]:
if len(self.list) == 0:
try:
return self[1]
except IndexError:
return None
return self.list[0]
def shuffle(self):
random.shuffle(self.list)
part = self.list[1:]
random.shuffle(part)
part.insert(0, self.list[0])
self.list = part
def clear(self):
self.list = []
@ -303,33 +287,90 @@ class VideoQueue:
def not_ready_videos(self, limit: typing.Optional[int] = None):
"""Return the non-ready videos in the first limit positions of the queue."""
video_list = []
# In single video repeat, the only video to be loaded is the one currently playing
if self.loop_mode == LoopMode.LOOP_SINGLE:
if self.now_playing is None or self.now_playing.is_ready:
return video_list
else:
video_list.append(self.now_playing)
return video_list
# In suggestion mode, preload the current video and the next suggested one
if self.loop_mode == LoopMode.FOLLOW_SUGGESTIONS:
video = self.now_playing
if video is None:
return video_list
video.get_suggestion()
if not video.is_ready:
video_list.append(video)
if not video.suggestion.is_ready:
video_list.append(video.suggestion)
return video_list
# In all other modes, the videos to be loaded are the current one plus the following ones
for video in (self.list[:limit] + ([self.now_playing] if self.now_playing else [])):
for video in (self[:limit]):
if not video.is_ready:
video_list.append(video)
return video_list
def __getitem__(self, index: int) -> Video:
"""Get an element from the list."""
return self.list[index]
async def async_not_ready_videos(self, limit: typing.Optional[int] = None):
"""Return the non-ready videos in the first limit positions of the queue."""
video_list = []
full_list = await loop.run_in_executor(executor, functools.partial(self.__getitem__, slice(None, limit, None)))
for video in full_list:
if not video.is_ready:
video_list.append(video)
return video_list
def __getitem__(self, index: typing.Union[int, slice]) -> typing.Union[Video, typing.Iterable]:
"""Get an enqueued element."""
if isinstance(index, int):
if self.loop_mode == LoopMode.NORMAL:
return self.list[index]
elif self.loop_mode == LoopMode.LOOP_QUEUE:
if len(self.list) == 0:
raise IndexError()
return self.list[index % len(self.list)]
elif self.loop_mode == LoopMode.LOOP_SINGLE:
if len(self.list) == 0:
raise IndexError()
return self.list[0]
elif self.loop_mode == LoopMode.FOLLOW_SUGGESTIONS:
counter = index
video = self.list[0]
while counter > 0:
video.get_suggestion()
video = video.suggestion
counter -= 1
return video
elif self.loop_mode == LoopMode.AUTO_SHUFFLE:
if index == 0:
return self.list[0]
elif index >= len(self.list):
raise IndexError()
number = random.randrange(1, len(self.list))
return self.list[number]
elif self.loop_mode == LoopMode.LOOPING_SHUFFLE:
if index == 0:
return self.list[0]
number = random.randrange(1, len(self.list))
return self.list[number]
else:
# FIXME: won't work properly in multiple cases, but should work fine enough for now
video_list = []
if self.loop_mode == LoopMode.FOLLOW_SUGGESTIONS:
try:
video = self.list[0]
except IndexError:
return video_list
while True:
if video is None:
break
video_list.append(video)
video = video.suggestion
else:
for i in range(len(self)):
video_list.append(self[i])
return video_list[index]
async def async_getitem(self, index):
return await loop.run_in_executor(executor, functools.partial(self.__getitem__, index))
def __delitem__(self, index: typing.Union[int, slice]):
if isinstance(index, int):
if self.loop_mode == LoopMode.LOOP_SINGLE:
pass
elif self.loop_mode == LoopMode.FOLLOW_SUGGESTIONS:
if index != 0:
raise errors.LoopModeError("Deleting suggested videos different than the current one is impossible.")
else:
video = self[0]
if video.suggestion is not None:
self.list.append(video.suggestion)
del self.list[0]
del self.list[index]
else:
for i in range(index.start, index.stop, index.step):
del self[i]
def escape(message: str):
@ -758,7 +799,8 @@ class RoyalDiscordBot(discord.Client):
while True:
await asyncio.sleep(1)
# Might have some problems with del
for index, video in enumerate(self.video_queue.not_ready_videos(self.max_videos_to_predownload)):
not_ready_videos = await self.video_queue.async_not_ready_videos(self.max_videos_to_predownload)
for index, video in enumerate(not_ready_videos):
try:
with async_timeout.timeout(self.max_video_ready_time):
await loop.run_in_executor(executor, video.ready_up)
@ -783,12 +825,22 @@ class RoyalDiscordBot(discord.Client):
})
self.sentry.captureException()
logger.error(f"Uncaught video download error: {e}")
await self.main_channel.send(f"⚠️ E' stato incontrato un errore durante il download di "
f"{str(video)}, quindi è stato rimosso dalla coda.\n\n"
f"```python\n"
f"{str(e.args)}"
f"```")
del self.video_queue.list[index]
if self.video_queue.loop_mode == LoopMode.FOLLOW_SUGGESTIONS or \
self.video_queue.loop_mode == LoopMode.LOOPING_SHUFFLE or \
self.video_queue.loop_mode == LoopMode.AUTO_SHUFFLE:
await self.main_channel.send(f"⚠️ E' stato incontrato un errore durante il download di "
f"{str(video)}, quindi la coda è stata svuotata.\n\n"
f"```python\n"
f"{str(e.args)}"
f"```")
self.video_queue.clear()
else:
await self.main_channel.send(f"⚠️ E' stato incontrato un errore durante il download di "
f"{str(video)}, quindi è stato rimosso dalla coda.\n\n"
f"```python\n"
f"{str(e.args)}"
f"```")
del self.video_queue[index]
continue
async def queue_play_next_video(self):
@ -799,39 +851,34 @@ class RoyalDiscordBot(discord.Client):
# Do not add play videos if something else is playing!
if not voice_client.is_connected():
continue
if voice_client.is_playing():
# Find the "now_playing" video
try:
current_video = await self.video_queue.async_getitem(0)
except IndexError:
continue
if voice_client.is_paused():
continue
# Ensure the next video is ready
next_video = self.video_queue.next_video()
if next_video is None or not next_video.is_ready:
continue
# Advance the queue
self.video_queue.advance_queue()
# Try to generate an AudioSource
if self.video_queue.now_playing is None:
try:
audio_source = current_video.make_audio_source()
except errors.VideoIsNotReady:
continue
audio_source = self.video_queue.now_playing.make_audio_source()
# Start playing the AudioSource
logger.info(f"Started playing {self.video_queue.now_playing.plain_text()}.")
logger.info(f"Started playing {current_video.plain_text()}.")
voice_client.play(audio_source)
# Update the voice_client activity
activity = discord.Activity(name=self.video_queue.now_playing.plain_text(),
activity = discord.Activity(name=current_video.plain_text(),
type=discord.ActivityType.listening)
logger.debug("Updating bot presence...")
await self.change_presence(status=discord.Status.online, activity=activity)
# Record the played song in the database
if self.video_queue.now_playing.enqueuer is not None:
logger.debug(f"Adding {self.video_queue.now_playing.plain_text()} to db.PlayedMusic...")
if current_video.enqueuer is not None:
logger.debug(f"Adding {current_video.plain_text()} to db.PlayedMusic...")
try:
session = db.Session()
enqueuer = await loop.run_in_executor(executor, session.query(db.Discord)
.filter_by(
discord_id=self.video_queue.now_playing.enqueuer.id)
.filter_by(discord_id=current_video.enqueuer.id)
.one_or_none)
played_music = db.PlayedMusic(enqueuer=enqueuer,
filename=self.video_queue.now_playing.database_text(),
filename=current_video.database_text(),
timestamp=datetime.datetime.now())
session.add(played_music)
await loop.run_in_executor(executor, session.commit)
@ -840,13 +887,17 @@ class RoyalDiscordBot(discord.Client):
pass
# Send a message in chat
for key in self.song_text_easter_eggs:
if key in self.video_queue.now_playing.name.lower():
if key in current_video.name.lower():
await self.main_channel.send(
self.song_text_easter_eggs[key].format(song=str(self.video_queue.now_playing)))
self.song_text_easter_eggs[key].format(song=str(current_video)))
break
else:
await self.main_channel.send(
f":arrow_forward: Ora in riproduzione: {str(self.video_queue.now_playing)}")
f":arrow_forward: Ora in riproduzione: {str(current_video)}")
# Wait until the song is finished
while voice_client.is_playing() or voice_client.is_paused():
await asyncio.sleep(1)
self.video_queue.advance_queue()
async def inactivity_countdown(self):
while True:
@ -1063,7 +1114,7 @@ class RoyalDiscordBot(discord.Client):
await channel.send("⚠️ Il numero inserito non corrisponde a nessun video nella playlist.\n"
"Sintassi: `!remove [numerovideoiniziale] [numerovideofinale]`")
return
video = self.video_queue.list.pop(index)
video = self.video_queue.pop(index)
await channel.send(f":regional_indicator_x: {str(video)} è stato rimosso dalla coda.")
logger.debug(f"Removed from queue: {video.plain_text()}")
return
@ -1093,7 +1144,7 @@ class RoyalDiscordBot(discord.Client):
await channel.send("⚠️ Il numero iniziale è maggiore del numero finale.\n"
"Sintassi: `!remove [numerovideoiniziale] [numerovideofinale]`")
return
del self.video_queue.list[start:end]
del self.video_queue[start:end]
await channel.send(f":regional_indicator_x: {end - start} video rimossi dalla coda.")
logger.debug(f"Removed from queue {end - start} videos.")
@ -1114,53 +1165,14 @@ class RoyalDiscordBot(discord.Client):
elif self.video_queue.loop_mode == LoopMode.LOOPING_SHUFFLE:
msg += "Modalità attuale: :arrows_counterclockwise: **Video casuali infiniti dalla coda**\n"
msg += "**Video in coda:**\n"
if self.video_queue.now_playing is None:
if len(self.video_queue) == 0:
msg += ":cloud: _nessuno_\n"
else:
msg += f":arrow_forward: {str(self.video_queue.now_playing)}\n"
if self.video_queue.loop_mode == LoopMode.NORMAL:
for index, video in enumerate(self.video_queue.list[:10]):
msg += f"{number_emojis[index]} {str(video)}\n"
if len(self.video_queue) > 10:
msg += f"più altri {len(self.video_queue) - 10} video!"
elif self.video_queue.loop_mode == LoopMode.LOOP_QUEUE:
for index, video in enumerate(self.video_queue.list[:10]):
msg += f"{number_emojis[index]} {str(video)}\n"
if len(self.video_queue) > 10:
msg += f"più altri {len(self.video_queue) - 10} video che si ripetono!"
else:
if len(self.video_queue) < 6:
count = len(self.video_queue)
while count < 10:
for index, video in enumerate(self.video_queue.list[:10]):
msg += f":asterisk: {str(video)}\n"
count += len(self.video_queue)
msg += "e avanti così!"
elif self.video_queue.loop_mode == LoopMode.LOOP_SINGLE:
video = self.video_queue.now_playing
for index in range(9):
msg += f":asterisk: {str(video)}\n"
msg += "all'infinito!"
elif self.video_queue.loop_mode == LoopMode.FOLLOW_SUGGESTIONS:
msg += "e i prossimi video suggeriti!"
elif self.video_queue.loop_mode == LoopMode.AUTO_SHUFFLE:
for index, video in enumerate(self.video_queue.list[:10]):
msg += f":hash: {str(video)}\n"
if len(self.video_queue) > 10:
msg += f"più altri {len(self.video_queue) - 10} video!"
elif self.video_queue.loop_mode == LoopMode.LOOPING_SHUFFLE:
for index, video in enumerate(self.video_queue.list[:10]):
msg += f":hash: {str(video)}\n"
if len(self.video_queue) > 10:
msg += f"più altri {len(self.video_queue) - 10} video che si ripetono!"
else:
if len(self.video_queue) < 6:
count = len(self.video_queue)
while count < 10:
for index, video in enumerate(self.video_queue.list[:10]):
msg += f":asterisk: {str(video)}\n"
count += len(self.video_queue)
msg += "a ripetizione casuale!"
for index in range(11):
try:
video = await self.video_queue.async_getitem(index)
except IndexError:
break
msg += f"{number_emojis[index]} {str(video)}\n"
await channel.send(msg)
# noinspection PyUnusedLocal

View file

@ -56,3 +56,7 @@ class PastDateError(Exception):
class MissingAPIKeyError(Exception):
pass
class LoopModeError(Exception):
pass