210 lines
5.2 KiB
Ruby
210 lines
5.2 KiB
Ruby
require 'uri'
|
|
require 'open-uri'
|
|
require 'tempfile'
|
|
require 'youtube-dl.rb'
|
|
require 'streamio-ffmpeg'
|
|
|
|
# @!visibility private
|
|
class QueueItem
|
|
attr_accessor :next, :prev
|
|
attr_reader :time, :ptime, :size, :fname, :url, :descr, :is_stream
|
|
|
|
def initialize uri, stream = false
|
|
@is_stream = stream
|
|
if uri.scheme == "file"
|
|
@fname = uri.path
|
|
elsif uri.host and check_youtube_dl uri.host
|
|
@fname = get_yt_vid_from uri
|
|
else
|
|
@fname = uri.to_s
|
|
end
|
|
|
|
@url = uri.to_s
|
|
|
|
unless @is_stream
|
|
if (mov = FFMPEG::Movie.new(@fname)) and mov.valid?
|
|
@time = mov.duration
|
|
@size = mov.size
|
|
@ptime = "%.2i:%.2i" % [@time / 60, @time % 60]
|
|
else
|
|
throw ArgumentError, "File is invalid or is a stream."
|
|
end
|
|
else
|
|
@descr = @url
|
|
end
|
|
end
|
|
|
|
def cleanup
|
|
File.delete @tmpf if @tmpf
|
|
end
|
|
|
|
private
|
|
def check_youtube_dl host
|
|
host.end_with? "youtube.com" or
|
|
host.end_with? "youtu.be" or
|
|
host.end_with? "soundcloud.com"
|
|
end
|
|
|
|
def get_yt_vid_from uri
|
|
yt_tmp = get_tmp_name "vrobot4_yt_temp_", ".m4a"
|
|
mv_tmp = get_tmp_name "vrobot4_mv_temp_", ".mp3"
|
|
opt = {
|
|
output: yt_tmp,
|
|
extract_audio: true,
|
|
audio_format: "m4a"
|
|
}
|
|
YoutubeDL.get uri.to_s, opt
|
|
FFMPEG::Movie.new(yt_tmp).transcode(mv_tmp)
|
|
File.delete yt_tmp
|
|
@tmpf = mv_tmp
|
|
end
|
|
|
|
def get_tmp_name name, ext
|
|
fname = Dir::Tmpname.tmpdir + "/"
|
|
fname += Dir::Tmpname.make_tmpname name, ext
|
|
end
|
|
end
|
|
|
|
class Mod_Audio < Vrobot4::Module::Module
|
|
def self.type() "Audio" end
|
|
Vrobot4::Module.add_module_type self, servflags: "A"
|
|
|
|
def initialize info
|
|
super
|
|
register :c_join, "voicejoin", "Brings the bot to your voice channel."
|
|
register :c_quit, "voicequit", "Kicks the bot from voice.", roles: "h"
|
|
register :c_queue, "queue", "Adds an item to the queue or lists it."
|
|
register :c_volume, "volume", "Sets the bot's volume."
|
|
register :c_skip, "skip", "Skips to the next song."
|
|
register :c_pause, "pause", "Pauses the current song."
|
|
register :c_play, "play", "Resumes the current song."
|
|
register :c_rmqueue, "rmqueue", "Removes an item from the queue."
|
|
# TODO: add stream playing command
|
|
@queue = []
|
|
@queue_mtx = Mutex.new
|
|
@running = false
|
|
end
|
|
|
|
def c_join m, argv
|
|
vc = m.user.real.voice_channel
|
|
raise RuntimeError, "You're not in a voice channel" unless vc
|
|
m.serv.bot.voice_connect vc
|
|
end
|
|
|
|
def c_quit m, argv
|
|
m.serv.voice_quit m
|
|
end
|
|
|
|
def c_queue m, argv
|
|
if argv.empty?
|
|
list_queue m
|
|
elsif push_queue m, argv
|
|
if @queue_mtx.synchronize {not @running}
|
|
start_playback m, 0
|
|
end
|
|
end
|
|
end
|
|
|
|
def c_rmqueue m, argv
|
|
@queue_mtx.synchronize {rm_queue @queue[argv.to_i + 1]}
|
|
end
|
|
|
|
def c_volume m, argv
|
|
num = argv.to_f
|
|
num = num > 1 ? 1 : num < 0.1 ? 0.1 : num
|
|
m.serv.bot.voice(m.chan.real).volume = num
|
|
end
|
|
|
|
def c_skip m, argv
|
|
m.serv.bot.voice(m.chan.real).stop_playing
|
|
end
|
|
|
|
def c_pause m, argv
|
|
m.serv.bot.voice(m.chan.real).pause
|
|
end
|
|
|
|
def c_play m, argv
|
|
m.serv.bot.voice(m.chan.real).continue
|
|
end
|
|
|
|
private
|
|
def list_queue m
|
|
@queue_mtx.synchronize do
|
|
text = ""
|
|
i = 1
|
|
for qi in @queue
|
|
text << "%i - <%s> (%s" % [i, qi.descr, qi.ptime]
|
|
text << ", %i bytes" % [qi.size] unless qi.is_stream
|
|
text << ")\n"
|
|
i += 1
|
|
end
|
|
if text.empty? then m.reply "No items in queue."
|
|
else m.reply_b text end
|
|
end
|
|
end
|
|
|
|
def rm_queue qi
|
|
qi.prev.next = qi.next if qi.prev
|
|
qi.next.prev = qi.prev if qi.next
|
|
qi.cleanup
|
|
@queue.delete qi
|
|
end
|
|
|
|
def push_queue m, argv, stream = false
|
|
uri = URI.parse argv
|
|
|
|
m.reply "Queueing <#{uri.to_s}>..."
|
|
m.chan.real.start_typing if m.serv.class.type == "Discord"
|
|
|
|
begin
|
|
qi = QueueItem.new(uri, stream)
|
|
@queue_mtx.synchronize do
|
|
if @queue.any?
|
|
@queue.last.next = qi
|
|
qi.prev = @queue.last
|
|
end
|
|
@queue.push qi
|
|
if qi.is_stream
|
|
m.reply "Stream <#{qi.descr}> queued"
|
|
else
|
|
m.reply "<#{qi.descr}> loaded (%s, %i bytes)" %
|
|
[qi.ptime, qi.size]
|
|
end
|
|
end
|
|
return true
|
|
rescue
|
|
p $!
|
|
m.reply "Couldn't load queue item"
|
|
return false
|
|
end
|
|
end
|
|
|
|
def start_playback m, start
|
|
qi = nil
|
|
|
|
@queue_mtx.synchronize do
|
|
@running = true
|
|
qi = @queue[start]
|
|
end
|
|
|
|
while qi
|
|
begin
|
|
m.serv.voice_play(m, @queue_mtx.synchronize {qi.fname})
|
|
rescue
|
|
p $!
|
|
m.reply "Error playing queue item."
|
|
end
|
|
|
|
@queue_mtx.synchronize do
|
|
rm_queue qi
|
|
qi = qi.next
|
|
end
|
|
end
|
|
|
|
m.reply "End of queue reached."
|
|
@queue_mtx.synchronize {@running = false}
|
|
end
|
|
end
|
|
|
|
## EOF
|