Support for livestreams
This commit is contained in:
parent
d0cacd7334
commit
fa68cba89a
|
@ -32,6 +32,7 @@ class VoiceState:
|
|||
self.user_queue = user_queue
|
||||
self.loop = bot.loop
|
||||
self._volume = volume or .5
|
||||
self.live = False
|
||||
|
||||
@property
|
||||
def volume(self):
|
||||
|
@ -527,6 +528,11 @@ class Music:
|
|||
await ctx.send("The current queue type is the DJ queue. "
|
||||
"Use the command {}dj to join this queue".format(ctx.prefix))
|
||||
return
|
||||
# If the state is live, songs can't play at the same time
|
||||
if state and state.live:
|
||||
await ctx.send("Currently playing a live stream! The stream needs to stop before a song can be played")
|
||||
return
|
||||
|
||||
# Ensure the user is in the voice channel
|
||||
try:
|
||||
if ctx.message.author.voice.channel != ctx.message.guild.me.voice.channel:
|
||||
|
@ -798,7 +804,8 @@ class Music:
|
|||
embed = discord.Embed()
|
||||
# Fill in the simple things
|
||||
embed.add_field(name='Title', value=state.current.title, inline=False)
|
||||
embed.add_field(name='Requester', value=state.current.requester.display_name, inline=False)
|
||||
if state.current.requester:
|
||||
embed.add_field(name='Requester', value=state.current.requester.display_name, inline=False)
|
||||
# Get the amount of current skips, and display how many have been skipped/how many required
|
||||
skip_count = len(state.skip_votes)
|
||||
embed.add_field(name='Skip Count', value='{}/{}'.format(skip_count, state.required_skips), inline=False)
|
||||
|
@ -827,10 +834,14 @@ class Music:
|
|||
return
|
||||
|
||||
state = self.voice_states.get(ctx.message.guild.id)
|
||||
if not state.user_queue:
|
||||
if state and not state.user_queue:
|
||||
await ctx.send("The current queue type is the song queue. "
|
||||
"Use the command {}play to add a song to the queue".format(ctx.prefix))
|
||||
return
|
||||
# If the state is live, songs can't play at the same time
|
||||
if state and state.live:
|
||||
await ctx.send("Currently playing a live stream! The stream needs to stop before a song can be played")
|
||||
return
|
||||
|
||||
if state.get_dj(ctx.message.author):
|
||||
await ctx.send("You are already in the DJ queue!")
|
||||
|
@ -874,6 +885,43 @@ class Music:
|
|||
else:
|
||||
await ctx.send("There needs to be a queue before I can shuffle it!")
|
||||
|
||||
@commands.command()
|
||||
@commands.guild_only()
|
||||
@utils.custom_perms(mute_members=True)
|
||||
@utils.check_restricted()
|
||||
async def stream(self, ctx, *, url):
|
||||
"""Plays a livestream
|
||||
|
||||
EXAMPLE: !stream live_stream_url
|
||||
RESULT: A livestream starts playing"""
|
||||
# If we don't have a voice state yet, create one
|
||||
if ctx.message.guild.id not in self.voice_states:
|
||||
if not await ctx.invoke(self.join):
|
||||
return
|
||||
|
||||
state = self.voice_states.get(ctx.message.guild.id)
|
||||
|
||||
# If we have a state, clear the songs, dj's, then skip the current song
|
||||
if state and state.voice:
|
||||
state.songs.clear()
|
||||
state.djs.clear()
|
||||
state.skip()
|
||||
|
||||
# Now we can start the livestream
|
||||
# Create the source used
|
||||
source = YoutubeDLLiveStreamSource(self.bot, url)
|
||||
# Download the info
|
||||
await source.get_ready()
|
||||
# Set the current song as the livestream
|
||||
state.current = source
|
||||
# Use the volume transformer
|
||||
source = PCMVolumeTransformer(source, volume=state.volume)
|
||||
# Then play the livestream
|
||||
state.voice.play(source)
|
||||
state.live = True
|
||||
else:
|
||||
await ctx.send("Failed to join the channel...")
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Music(bot))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from .downloader import Downloader
|
||||
from .exceptions import *
|
||||
from .playlist import Playlist
|
||||
|
||||
from .source import *
|
||||
|
|
|
@ -77,7 +77,7 @@ class YoutubeDLSource(discord.FFmpegPCMAudio):
|
|||
self.ready = True
|
||||
opts = {
|
||||
'before_options': '-nostdin',
|
||||
'options': '-vn -b:a 128k'
|
||||
'options': '-vn -b:a 128k -v fatal'
|
||||
}
|
||||
super().__init__(self.downloader.ytdl.prepare_filename(self.info), **opts)
|
||||
|
||||
|
@ -122,3 +122,49 @@ class YoutubeDLSource(discord.FFmpegPCMAudio):
|
|||
embed.add_field(name='Duration', value=fmt, inline=False)
|
||||
# And return the embed we created
|
||||
return embed
|
||||
|
||||
class YoutubeDLLiveStreamSource(discord.FFmpegPCMAudio):
|
||||
def __init__(self, bot, url):
|
||||
self._process = None
|
||||
self.downloader = bot.downloader
|
||||
self.loop = bot.loop
|
||||
self.url = url
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return self.info.get('title', 'Untitled')
|
||||
|
||||
@property
|
||||
def thumbnail(self):
|
||||
return self.info.get('thumbnail', None)
|
||||
|
||||
@property
|
||||
def embed(self):
|
||||
"""A property that returns an embed that can be used to display information about this particular song"""
|
||||
# Create the embed object we'll use
|
||||
embed = discord.Embed()
|
||||
# Fill in the simple things
|
||||
embed.add_field(name='Title', value=self.title, inline=False)
|
||||
embed.add_field(name='Requester', value=self.requester.display_name, inline=False)
|
||||
if self.thumbnail:
|
||||
embed.set_thumbnail(url=self.thumbnail)
|
||||
# And return the embed we created
|
||||
return embed
|
||||
|
||||
async def get_ready(self):
|
||||
try:
|
||||
# First attempt to gather the information
|
||||
info = await self.downloader.extract_info(self.loop, self.url, download=False)
|
||||
except Exception as e:
|
||||
raise ExtractionError('Could not extract information from {}\n\n{}'.format(self.url, e))
|
||||
|
||||
if not info.get('is_live', False):
|
||||
raise WrongEntryTypeError("This is not a livestream!")
|
||||
|
||||
# Set our info
|
||||
self.info = info
|
||||
opts = {
|
||||
'before_options': '-nostdin',
|
||||
'options': '-vn -b:a 128k -v fatal'
|
||||
}
|
||||
super().__init__(info.get('manifest_url'), **opts)
|
||||
|
|
Loading…
Reference in a new issue