New feature

This commit is contained in:
azlux 2018-05-17 23:55:42 +02:00
parent 54068a151e
commit ad378a586c
5 changed files with 193 additions and 29 deletions

View File

@ -1,8 +1,9 @@
[bot] [bot]
comment = Coucou, Je suis née du savoir du Azlux, accès au https://azlux.fr/bot comment = Coucou, Je suis née du savoir du Azlux, accès au https://azlux.fr/bot
volume = 0.1 volume = 0.1
admin = Azlux;AzMobile admin = Azlux;AzMobile
music_folder = /home/dmichel/botamusique/music/ music_folder = /home/dmichel/botamusique/music/
tmp_folder = /tmp/
is_proxified = True is_proxified = True
[debug] [debug]
@ -10,14 +11,20 @@ ffmpeg = False
mumbleConnection = False mumbleConnection = False
[command] [command]
play_file = play play_file = file
play_url = url
play_radio = radio
help = help
stop = stop stop = stop
list = list
next = next
current_music = np current_music = np
volume = v volume = v
kill = kill kill = kill
stop_and_getout = oust stop_and_getout = oust
joinme = joinme joinme = joinme
list = list
[strings] [strings]
current_volume = volume : %d%% current_volume = volume : %d%%
@ -27,3 +34,14 @@ not_admin = T'es pas admin, patate !
not_playing = Aucun stream en lecture not_playing = Aucun stream en lecture
bad_file = Bad file asked bad_file = Bad file asked
no_file = Not file here no_file = Not file here
bad_url = Bad URL asked
help = Command available:
<br />!play_file <path>
<br />!play_url <url> - youtube or soundcloud
<br />!play_radio <url> - url of a stream
<br />!next - jump to the next music of the playlist
<br />!stop - stop and clear the playlist
<br />!play_file - stop + Go to default channel
<br />!v - get or change the volume (in %)
<br />!joinme

View File

@ -64,14 +64,14 @@ def index():
files[director] = [f for f in listdir(folder_path + director) if os.path.isfile(os.path.join(folder_path + director, f))] files[director] = [f for f in listdir(folder_path + director) if os.path.isfile(os.path.join(folder_path + director, f))]
if request.method == 'POST': if request.method == 'POST':
if 'add_music' in request.form and ".." not in request.form['add_music']: if 'add_file' in request.form and ".." not in request.form['add_music']:
var.playlist.append(request.form['add_music']) var.playlist.append((request.form['type'], request.form['add_music']))
if 'add_folder' in request.form and ".." not in request.form['add_folder']: if 'add_folder' in request.form and ".." not in request.form['add_folder']:
dir_files = [request.form['add_folder'] + '/' + i for i in files[request.form['add_folder']]] dir_files = [("file", request.form['add_folder'] + '/' + i) for i in files[request.form['add_folder']]]
var.playlist.extend(dir_files) var.playlist.extend(dir_files)
elif 'delete_music' in request.form: elif 'delete_music' in request.form:
try: try:
var.playlist.remove(request.form['delete_music']) var.playlist.remove("file", request.form['delete_music'])
except ValueError: except ValueError:
pass pass
elif 'action' in request.form: elif 'action' in request.form:

67
media.py Normal file
View File

@ -0,0 +1,67 @@
import re
import urllib
import logging
import json
import http.client
import struct
def get_radio_server_description(url):
p = re.compile('(https?\:\/\/[^\/]*)', re.IGNORECASE)
res = re.search(p, url)
base_url = res.group(1)
url_icecast = base_url + '/status-json.xsl'
url_shoutcast = base_url + '/stats?json=1'
title_server = None
try:
request = urllib.request.Request(url_shoutcast)
response = urllib.request.urlopen(request)
data = json.loads(response.read().decode("utf-8"))
title_server = data['servertitle']
logging.debug("TITLE FOUND SHOUTCAST: " + title_server)
except urllib.error.HTTPError:
pass
except http.client.BadStatusLine:
pass
except ValueError:
return False
if not title_server:
try:
request = urllib.request.Request(url_icecast)
response = urllib.request.urlopen(request)
data = json.loads(response.read().decode('utf-8',errors='ignore'),strict=False)
title_server = data['icestats']['source'][0]['server_name'] + ' - ' + data['icestats']['source'][0]['server_description']
logging.debug("TITLE FOUND ICECAST: " + title_server)
if not title_server:
title_server = url
except urllib.error.URLError:
title_server = url
except urllib.error.HTTPError:
return False
except http.client.BadStatusLine:
pass
return title_server
def get_radio_title(url):
request = urllib.request.Request(url, headers={'Icy-MetaData': 1})
try:
response = urllib.request.urlopen(request)
icy_metaint_header = int(response.headers['icy-metaint'])
if icy_metaint_header is not None:
response.read(icy_metaint_header)
metadata_length = struct.unpack('B', response.read(1))[0] * 16 # length byte
metadata = response.read(metadata_length).rstrip(b'\0')
logging.debug(metadata)
# extract title from the metadata
m = re.search(br"StreamTitle='([^']*)';", metadata)
if m:
title = m.group(1)
if title:
return title.decode()
except (urllib.error.URLError, urllib.error.HTTPError):
pass
return 'Impossible to get the music title'

View File

@ -1,6 +1,8 @@
#!/usr/bin/python3 #!/usr/bin/python3
import threading from __future__ import unicode_literals
import re
import threading
import time import time
import sys import sys
import signal import signal
@ -13,6 +15,9 @@ from os import listdir
import pymumble.pymumble_py3 as pymumble import pymumble.pymumble_py3 as pymumble
import interface import interface
import variables as var import variables as var
import hashlib
import youtube_dl
import media
class MumbleBot: class MumbleBot:
@ -34,7 +39,24 @@ class MumbleBot:
self.channel = args.channel self.channel = args.channel
var.current_music = None var.current_music = None
######
## Format of the Playlist :
## [("<type>","<path>")]
## [("<radio>","<luna>"), ("<youtube>","<url>")]
## types : file, radio, url
######
######
## Format of the current_music variable
# len(var.current_music) = 4
# var.current_music[0] = <Type>
# var.current_music[1] = <url> if url of radio
# var.current_music[2] = <title>
# var.current_music[3] = <path> if url or file
var.playlist = [] var.playlist = []
var.user = args.user var.user = args.user
var.music_folder = self.config.get('bot', 'music_folder') var.music_folder = self.config.get('bot', 'music_folder')
var.is_proxified = self.config.getboolean("bot", "is_proxified") var.is_proxified = self.config.getboolean("bot", "is_proxified")
@ -44,9 +66,9 @@ class MumbleBot:
interface.init_proxy() interface.init_proxy()
t = threading.Thread(target=start_web_interface) # t = threading.Thread(target=start_web_interface)
t.daemon = True # t.daemon = True
t.start() # t.start()
self.mumble = pymumble.Mumble(args.host, user=args.user, port=args.port, password=args.password, self.mumble = pymumble.Mumble(args.host, user=args.user, port=args.port, password=args.password,
debug=self.config.getboolean('debug', 'mumbleConnection')) debug=self.config.getboolean('debug', 'mumbleConnection'))
@ -91,10 +113,19 @@ class MumbleBot:
if "/" in parameter: if "/" in parameter:
self.mumble.users[text.actor].send_message(self.config.get('strings', 'bad_file')) self.mumble.users[text.actor].send_message(self.config.get('strings', 'bad_file'))
elif os.path.isfile(path): elif os.path.isfile(path):
self.launch_play_file(path) var.playlist.append(["file", path])
else: else:
self.mumble.users[text.actor].send_message(self.config.get('strings', 'bad_file')) self.mumble.users[text.actor].send_message(self.config.get('strings', 'bad_file'))
elif command == self.config.get('command', 'play_url') and parameter:
var.playlist.append(["url", parameter])
elif command == self.config.get('command', 'play_radio') and parameter:
var.playlist.append(["radio", parameter])
elif command == self.config.get('command', 'help'):
self.send_msg_channel(self.config.get('strings', 'help'))
elif command == self.config.get('command', 'stop'): elif command == self.config.get('command', 'stop'):
self.stop() self.stop()
@ -123,17 +154,17 @@ class MumbleBot:
elif command == self.config.get('command', 'current_music'): elif command == self.config.get('command', 'current_music'):
if var.current_music is not None: if var.current_music is not None:
self.send_msg_channel(var.current_music) if var.current_music[0] == "radio":
self.send_msg_channel(media.get_radio_title(var.current_music[1]) + " sur " + var.current_music[2])
else:
self.send_msg_channel(var.current_music[2] + "<br />" + var.current_music[1])
else: else:
self.mumble.users[text.actor].send_message(self.config.get('strings', 'not_playing')) self.mumble.users[text.actor].send_message(self.config.get('strings', 'not_playing'))
elif command == self.config.get('command', 'list'): elif command == self.config.get('command', 'next'):
folder_path = self.config.get('bot', 'music_folder') var.current_music = var.playlist[0]
files = [f for f in listdir(folder_path) if os.path.isfile(os.path.join(folder_path, f))] var.playlist.pop(0)
if files: self.launch_next()
self.mumble.users[text.actor].send_message('<br>'.join(files))
else:
self.mumble.users[text.actor].send_message(self.config.get('strings', 'no_file'))
else: else:
self.mumble.users[text.actor].send_message(self.config.get('strings', 'bad_command')) self.mumble.users[text.actor].send_message(self.config.get('strings', 'bad_command'))
@ -145,21 +176,69 @@ class MumbleBot:
else: else:
return False return False
def launch_play_file(self, path=None): def launch_next(self):
if not path: path = ""
path = self.config.get('bot', 'music_folder') + var.current_music title = ""
if var.current_music[0] == "url":
regex = re.compile("<a href=\"(.*?)\"")
m = regex.match(var.current_music[1])
url = m.group(1)
path, title = self.download_music(url)
var.current_music[1] = url
elif var.current_music[0] == "file":
path = self.config.get('bot', 'music_folder') + var.current_music[1]
title = var.current_music[1]
elif var.current_music[0] == "radio":
regex = re.compile("<a href=\"(.*?)\"")
m = regex.match(var.current_music[1])
url = m.group(1)
var.current_music[1] = url
path = url
title = media.get_radio_server_description(url)
if self.config.getboolean('debug', 'ffmpeg'): if self.config.getboolean('debug', 'ffmpeg'):
ffmpeg_debug = "debug" ffmpeg_debug = "debug"
else: else:
ffmpeg_debug = "warning" ffmpeg_debug = "warning"
command = ["ffmpeg", '-v', ffmpeg_debug, '-nostdin', '-i', path, '-ac', '1', '-f', 's16le', '-ar', '48000', '-'] command = ["ffmpeg", '-v', ffmpeg_debug, '-nostdin', '-i', path, '-ac', '1', '-f', 's16le', '-ar', '48000', '-']
self.thread = sp.Popen(command, stdout=sp.PIPE, bufsize=480) self.thread = sp.Popen(command, stdout=sp.PIPE, bufsize=480)
var.current_music = path var.current_music.append(title)
var.current_music.append(path)
def download_music(self, url):
url_hash = hashlib.md5(url.encode()).hexdigest()
path = self.config.get('bot', 'tmp_folder') + url_hash + ".mp3"
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': path,
'noplaylist': True,
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192',
}]
}
video_title = ""
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
for i in range(2):
try:
info_dict = ydl.extract_info(url, download=False)
video_title = info_dict['title']
ydl.download([url])
except youtube_dl.utils.DownloadError:
pass
else:
break
return path, video_title
def loop(self): def loop(self):
while not self.exit: raw_music = ""
while not self.exit and self.mumble.isAlive():
while self.mumble.sound_output.get_buffer_size() > 0.5: while self.mumble.sound_output.get_buffer_size() > 0.5 and not self.exit:
time.sleep(0.01) time.sleep(0.01)
if self.thread: if self.thread:
raw_music = self.thread.stdout.read(480) raw_music = self.thread.stdout.read(480)
@ -173,7 +252,7 @@ class MumbleBot:
if (self.thread is None or not raw_music) and len(var.playlist) != 0: if (self.thread is None or not raw_music) and len(var.playlist) != 0:
var.current_music = var.playlist[0] var.current_music = var.playlist[0]
var.playlist.pop(0) var.playlist.pop(0)
self.launch_play_file() self.launch_next()
while self.mumble.sound_output.get_buffer_size() > 0: while self.mumble.sound_output.get_buffer_size() > 0:
time.sleep(0.01) time.sleep(0.01)

View File

@ -1,4 +1,4 @@
current_music = "" current_music = ("", "")
playlist = [] playlist = []
user = "" user = ""
music_folder = "" music_folder = ""