New feature
This commit is contained in:
parent
54068a151e
commit
ad378a586c
@ -1,8 +1,9 @@
|
||||
[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
|
||||
admin = Azlux;AzMobile
|
||||
music_folder = /home/dmichel/botamusique/music/
|
||||
tmp_folder = /tmp/
|
||||
is_proxified = True
|
||||
|
||||
[debug]
|
||||
@ -10,14 +11,20 @@ ffmpeg = False
|
||||
mumbleConnection = False
|
||||
|
||||
[command]
|
||||
play_file = play
|
||||
play_file = file
|
||||
play_url = url
|
||||
play_radio = radio
|
||||
|
||||
help = help
|
||||
stop = stop
|
||||
list = list
|
||||
next = next
|
||||
current_music = np
|
||||
volume = v
|
||||
kill = kill
|
||||
stop_and_getout = oust
|
||||
joinme = joinme
|
||||
list = list
|
||||
|
||||
|
||||
[strings]
|
||||
current_volume = volume : %d%%
|
||||
@ -27,3 +34,14 @@ not_admin = T'es pas admin, patate !
|
||||
not_playing = Aucun stream en lecture
|
||||
bad_file = Bad file asked
|
||||
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
|
||||
|
@ -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))]
|
||||
|
||||
if request.method == 'POST':
|
||||
if 'add_music' in request.form and ".." not in request.form['add_music']:
|
||||
var.playlist.append(request.form['add_music'])
|
||||
if 'add_file' in request.form and ".." not in 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']:
|
||||
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)
|
||||
elif 'delete_music' in request.form:
|
||||
try:
|
||||
var.playlist.remove(request.form['delete_music'])
|
||||
var.playlist.remove("file", request.form['delete_music'])
|
||||
except ValueError:
|
||||
pass
|
||||
elif 'action' in request.form:
|
||||
|
67
media.py
Normal file
67
media.py
Normal 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'
|
119
mumbleBot.py
119
mumbleBot.py
@ -1,6 +1,8 @@
|
||||
#!/usr/bin/python3
|
||||
import threading
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
import threading
|
||||
import time
|
||||
import sys
|
||||
import signal
|
||||
@ -13,6 +15,9 @@ from os import listdir
|
||||
import pymumble.pymumble_py3 as pymumble
|
||||
import interface
|
||||
import variables as var
|
||||
import hashlib
|
||||
import youtube_dl
|
||||
import media
|
||||
|
||||
|
||||
class MumbleBot:
|
||||
@ -34,7 +39,24 @@ class MumbleBot:
|
||||
|
||||
self.channel = args.channel
|
||||
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.user = args.user
|
||||
var.music_folder = self.config.get('bot', 'music_folder')
|
||||
var.is_proxified = self.config.getboolean("bot", "is_proxified")
|
||||
@ -44,9 +66,9 @@ class MumbleBot:
|
||||
|
||||
interface.init_proxy()
|
||||
|
||||
t = threading.Thread(target=start_web_interface)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
# t = threading.Thread(target=start_web_interface)
|
||||
# t.daemon = True
|
||||
# t.start()
|
||||
|
||||
self.mumble = pymumble.Mumble(args.host, user=args.user, port=args.port, password=args.password,
|
||||
debug=self.config.getboolean('debug', 'mumbleConnection'))
|
||||
@ -91,10 +113,19 @@ class MumbleBot:
|
||||
if "/" in parameter:
|
||||
self.mumble.users[text.actor].send_message(self.config.get('strings', 'bad_file'))
|
||||
elif os.path.isfile(path):
|
||||
self.launch_play_file(path)
|
||||
var.playlist.append(["file", path])
|
||||
else:
|
||||
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'):
|
||||
self.stop()
|
||||
|
||||
@ -123,17 +154,17 @@ class MumbleBot:
|
||||
|
||||
elif command == self.config.get('command', 'current_music'):
|
||||
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:
|
||||
self.mumble.users[text.actor].send_message(self.config.get('strings', 'not_playing'))
|
||||
|
||||
elif command == self.config.get('command', 'list'):
|
||||
folder_path = self.config.get('bot', 'music_folder')
|
||||
files = [f for f in listdir(folder_path) if os.path.isfile(os.path.join(folder_path, f))]
|
||||
if files:
|
||||
self.mumble.users[text.actor].send_message('<br>'.join(files))
|
||||
else:
|
||||
self.mumble.users[text.actor].send_message(self.config.get('strings', 'no_file'))
|
||||
elif command == self.config.get('command', 'next'):
|
||||
var.current_music = var.playlist[0]
|
||||
var.playlist.pop(0)
|
||||
self.launch_next()
|
||||
else:
|
||||
self.mumble.users[text.actor].send_message(self.config.get('strings', 'bad_command'))
|
||||
|
||||
@ -145,21 +176,69 @@ class MumbleBot:
|
||||
else:
|
||||
return False
|
||||
|
||||
def launch_play_file(self, path=None):
|
||||
if not path:
|
||||
path = self.config.get('bot', 'music_folder') + var.current_music
|
||||
def launch_next(self):
|
||||
path = ""
|
||||
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'):
|
||||
ffmpeg_debug = "debug"
|
||||
else:
|
||||
ffmpeg_debug = "warning"
|
||||
|
||||
command = ["ffmpeg", '-v', ffmpeg_debug, '-nostdin', '-i', path, '-ac', '1', '-f', 's16le', '-ar', '48000', '-']
|
||||
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):
|
||||
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)
|
||||
if self.thread:
|
||||
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:
|
||||
var.current_music = var.playlist[0]
|
||||
var.playlist.pop(0)
|
||||
self.launch_play_file()
|
||||
self.launch_next()
|
||||
|
||||
while self.mumble.sound_output.get_buffer_size() > 0:
|
||||
time.sleep(0.01)
|
||||
|
@ -1,4 +1,4 @@
|
||||
current_music = ""
|
||||
current_music = ("", "")
|
||||
playlist = []
|
||||
user = ""
|
||||
music_folder = ""
|
||||
|
Loading…
x
Reference in New Issue
Block a user