commit
1a97d1c588
101
command.py
101
command.py
@ -1,6 +1,5 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
import logging
|
import logging
|
||||||
import os.path
|
|
||||||
import pymumble.pymumble_py3 as pymumble
|
import pymumble.pymumble_py3 as pymumble
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@ -10,15 +9,13 @@ import util
|
|||||||
import variables as var
|
import variables as var
|
||||||
from librb import radiobrowser
|
from librb import radiobrowser
|
||||||
from database import SettingsDatabase, MusicDatabase
|
from database import SettingsDatabase, MusicDatabase
|
||||||
from media.item import item_builders, item_loaders, item_id_generators, dict_to_item, dicts_to_items
|
from media.item import item_id_generators, dict_to_item, dicts_to_items
|
||||||
from media.cache import get_cached_wrapper_from_scrap, get_cached_wrapper_by_id, get_cached_wrappers_by_tags
|
from media.cache import get_cached_wrapper_from_scrap, get_cached_wrapper_by_id, get_cached_wrappers_by_tags
|
||||||
from media.file import FileItem
|
from media.url_from_playlist import get_playlist_info
|
||||||
from media.url_from_playlist import PlaylistURLItem, get_playlist_info
|
|
||||||
from media.url import URLItem
|
|
||||||
from media.radio import RadioItem
|
|
||||||
|
|
||||||
log = logging.getLogger("bot")
|
log = logging.getLogger("bot")
|
||||||
|
|
||||||
|
|
||||||
def register_all_commands(bot):
|
def register_all_commands(bot):
|
||||||
bot.register_command(constants.commands('joinme'), cmd_joinme, no_partial_match=False, access_outside_channel=True)
|
bot.register_command(constants.commands('joinme'), cmd_joinme, no_partial_match=False, access_outside_channel=True)
|
||||||
bot.register_command(constants.commands('user_ban'), cmd_user_ban, no_partial_match=True)
|
bot.register_command(constants.commands('user_ban'), cmd_user_ban, no_partial_match=True)
|
||||||
@ -71,6 +68,7 @@ def register_all_commands(bot):
|
|||||||
bot.register_command('loop', cmd_loop_state, True)
|
bot.register_command('loop', cmd_loop_state, True)
|
||||||
bot.register_command('item', cmd_item, True)
|
bot.register_command('item', cmd_item, True)
|
||||||
|
|
||||||
|
|
||||||
def send_multi_lines(bot, lines, text, linebreak="<br />"):
|
def send_multi_lines(bot, lines, text, linebreak="<br />"):
|
||||||
global log
|
global log
|
||||||
|
|
||||||
@ -79,19 +77,20 @@ def send_multi_lines(bot, lines, text, linebreak="<br />"):
|
|||||||
for newline in lines:
|
for newline in lines:
|
||||||
msg += br
|
msg += br
|
||||||
br = linebreak
|
br = linebreak
|
||||||
if (len(msg) + len(newline)) > (bot.mumble.get_max_message_length() - 4) != 0: # 4 == len("<br>")
|
if (len(msg) + len(newline)) > (bot.mumble.get_max_message_length() - 4) != 0: # 4 == len("<br>")
|
||||||
bot.send_msg(msg, text)
|
bot.send_msg(msg, text)
|
||||||
msg = ""
|
msg = ""
|
||||||
msg += newline
|
msg += newline
|
||||||
|
|
||||||
bot.send_msg(msg, text)
|
bot.send_msg(msg, text)
|
||||||
|
|
||||||
|
|
||||||
# ---------------- Variables -----------------
|
# ---------------- Variables -----------------
|
||||||
|
|
||||||
song_shortlist = []
|
song_shortlist = []
|
||||||
|
|
||||||
# ---------------- Commands ------------------
|
|
||||||
|
|
||||||
|
# ---------------- Commands ------------------
|
||||||
|
|
||||||
def cmd_joinme(bot, user, text, command, parameter):
|
def cmd_joinme(bot, user, text, command, parameter):
|
||||||
global log
|
global log
|
||||||
@ -146,6 +145,7 @@ def cmd_url_ban(bot, user, text, command, parameter):
|
|||||||
bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
|
bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def cmd_url_ban_list(bot, user, text, command, parameter):
|
def cmd_url_ban_list(bot, user, text, command, parameter):
|
||||||
if bot.is_admin(user):
|
if bot.is_admin(user):
|
||||||
bot.mumble.users[text.actor].send_text_message(util.get_url_ban())
|
bot.mumble.users[text.actor].send_text_message(util.get_url_ban())
|
||||||
@ -153,6 +153,7 @@ def cmd_url_ban_list(bot, user, text, command, parameter):
|
|||||||
bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
|
bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def cmd_url_unban(bot, user, text, command, parameter):
|
def cmd_url_unban(bot, user, text, command, parameter):
|
||||||
global log
|
global log
|
||||||
|
|
||||||
@ -170,9 +171,11 @@ def cmd_play(bot, user, text, command, parameter):
|
|||||||
if len(var.playlist) > 0:
|
if len(var.playlist) > 0:
|
||||||
if parameter:
|
if parameter:
|
||||||
if parameter.isdigit() and 1 <= int(parameter) <= len(var.playlist):
|
if parameter.isdigit() and 1 <= int(parameter) <= len(var.playlist):
|
||||||
var.playlist.point_to(int(parameter) - 1 - 1) # First "-1" transfer 12345 to 01234, second "-1"
|
# First "-1" transfer 12345 to 01234, second "-1"
|
||||||
# point to the previous item. the loop will next to
|
# point to the previous item. the loop will next to
|
||||||
# the one you want
|
# the one you want
|
||||||
|
var.playlist.point_to(int(parameter) - 1 - 1)
|
||||||
|
|
||||||
bot.interrupt()
|
bot.interrupt()
|
||||||
else:
|
else:
|
||||||
bot.send_msg(constants.strings('invalid_index', index=parameter), text)
|
bot.send_msg(constants.strings('invalid_index', index=parameter), text)
|
||||||
@ -241,7 +244,7 @@ def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=Fals
|
|||||||
else:
|
else:
|
||||||
# try to do a partial match
|
# try to do a partial match
|
||||||
files = var.cache.files
|
files = var.cache.files
|
||||||
matches = [ file for file in files if parameter.lower() in file.lower()]
|
matches = [file for file in files if parameter.lower() in file.lower()]
|
||||||
if len(matches) == 1:
|
if len(matches) == 1:
|
||||||
file = matches[0]
|
file = matches[0]
|
||||||
music_wrapper = get_cached_wrapper_by_id(bot, var.cache.file_id_lookup[file], user)
|
music_wrapper = get_cached_wrapper_by_id(bot, var.cache.file_id_lookup[file], user)
|
||||||
@ -250,7 +253,7 @@ def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=Fals
|
|||||||
bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()))
|
bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()))
|
||||||
return
|
return
|
||||||
elif len(matches) > 1:
|
elif len(matches) > 1:
|
||||||
msgs = [ constants.strings('multiple_matches') ]
|
msgs = [constants.strings('multiple_matches')]
|
||||||
song_shortlist = []
|
song_shortlist = []
|
||||||
for index, match in enumerate(matches):
|
for index, match in enumerate(matches):
|
||||||
id = var.cache.file_id_lookup[match]
|
id = var.cache.file_id_lookup[match]
|
||||||
@ -274,10 +277,9 @@ def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=Fals
|
|||||||
def cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cache=False):
|
def cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cache=False):
|
||||||
global log
|
global log
|
||||||
|
|
||||||
music_folder = var.music_folder
|
|
||||||
if parameter:
|
if parameter:
|
||||||
files = var.cache.files
|
files = var.cache.files
|
||||||
msgs = [ constants.strings('multiple_file_added') + "<ul>"]
|
msgs = [constants.strings('multiple_file_added') + "<ul>"]
|
||||||
count = 0
|
count = 0
|
||||||
try:
|
try:
|
||||||
music_wrappers = []
|
music_wrappers = []
|
||||||
@ -289,12 +291,12 @@ def cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cach
|
|||||||
music_wrappers.append(music_wrapper)
|
music_wrappers.append(music_wrapper)
|
||||||
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
|
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
|
||||||
msgs.append("<li><b>{}</b> ({})</li>".format(music_wrapper.item().title,
|
msgs.append("<li><b>{}</b> ({})</li>".format(music_wrapper.item().title,
|
||||||
file[:match.span()[0]]
|
file[:match.span()[0]]
|
||||||
+ "<b style='color:pink'>"
|
+ "<b style='color:pink'>"
|
||||||
+ file[match.span()[0]: match.span()[1]]
|
+ file[match.span()[0]: match.span()[1]]
|
||||||
+ "</b>"
|
+ "</b>"
|
||||||
+ file[match.span()[1]:]
|
+ file[match.span()[1]:]
|
||||||
))
|
))
|
||||||
|
|
||||||
if count != 0:
|
if count != 0:
|
||||||
msgs.append("</ul>")
|
msgs.append("</ul>")
|
||||||
@ -331,7 +333,6 @@ def cmd_play_url(bot, user, text, command, parameter):
|
|||||||
bot.send_msg(constants.strings('bad_parameter', command=command))
|
bot.send_msg(constants.strings('bad_parameter', command=command))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def cmd_play_playlist(bot, user, text, command, parameter):
|
def cmd_play_playlist(bot, user, text, command, parameter):
|
||||||
global log
|
global log
|
||||||
|
|
||||||
@ -480,8 +481,10 @@ def cmd_rb_play(bot, user, text, command, parameter):
|
|||||||
msg += "No playable url found for this station, please try another station."
|
msg += "No playable url found for this station, please try another station."
|
||||||
bot.send_msg(msg, text)
|
bot.send_msg(msg, text)
|
||||||
|
|
||||||
|
|
||||||
yt_last_result = []
|
yt_last_result = []
|
||||||
yt_last_page = 0 # TODO: if we keep adding global variables, we need to consider sealing all commands up into classes.
|
yt_last_page = 0 # TODO: if we keep adding global variables, we need to consider sealing all commands up into classes.
|
||||||
|
|
||||||
|
|
||||||
def cmd_yt_search(bot, user, text, command, parameter):
|
def cmd_yt_search(bot, user, text, command, parameter):
|
||||||
global log, yt_last_result, yt_last_page, song_shortlist
|
global log, yt_last_result, yt_last_page, song_shortlist
|
||||||
@ -516,6 +519,7 @@ def cmd_yt_search(bot, user, text, command, parameter):
|
|||||||
else:
|
else:
|
||||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||||
|
|
||||||
|
|
||||||
def _yt_format_result(results, start, count):
|
def _yt_format_result(results, start, count):
|
||||||
msg = '<table><tr><th width="10%">Index</th><th>Title</th><th width="20%">Uploader</th></tr>'
|
msg = '<table><tr><th width="10%">Index</th><th>Title</th><th width="20%">Uploader</th></tr>'
|
||||||
for index, item in enumerate(results[start:start+count]):
|
for index, item in enumerate(results[start:start+count]):
|
||||||
@ -602,7 +606,7 @@ def cmd_volume(bot, user, text, command, parameter):
|
|||||||
if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
|
if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
|
||||||
bot.volume_set = float(float(parameter) / 100)
|
bot.volume_set = float(float(parameter) / 100)
|
||||||
bot.send_msg(constants.strings('change_volume',
|
bot.send_msg(constants.strings('change_volume',
|
||||||
volume=int(bot.volume_set * 100), user=bot.mumble.users[text.actor]['name']))
|
volume=int(bot.volume_set * 100), user=bot.mumble.users[text.actor]['name']))
|
||||||
var.db.set('bot', 'volume', str(bot.volume_set))
|
var.db.set('bot', 'volume', str(bot.volume_set))
|
||||||
log.info('cmd: volume set to %d' % (bot.volume_set * 100))
|
log.info('cmd: volume set to %d' % (bot.volume_set * 100))
|
||||||
else:
|
else:
|
||||||
@ -652,7 +656,7 @@ def cmd_ducking_volume(bot, user, text, command, parameter):
|
|||||||
if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
|
if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
|
||||||
bot.ducking_volume = float(float(parameter) / 100)
|
bot.ducking_volume = float(float(parameter) / 100)
|
||||||
bot.send_msg(constants.strings('change_ducking_volume',
|
bot.send_msg(constants.strings('change_ducking_volume',
|
||||||
volume=int(bot.ducking_volume * 100), user=bot.mumble.users[text.actor]['name']), text)
|
volume=int(bot.ducking_volume * 100), user=bot.mumble.users[text.actor]['name']), text)
|
||||||
# var.db.set('bot', 'volume', str(bot.volume_set))
|
# var.db.set('bot', 'volume', str(bot.volume_set))
|
||||||
var.db.set('bot', 'ducking_volume', str(bot.ducking_volume))
|
var.db.set('bot', 'ducking_volume', str(bot.ducking_volume))
|
||||||
log.info('cmd: volume on ducking set to %d' % (bot.ducking_volume * 100))
|
log.info('cmd: volume on ducking set to %d' % (bot.ducking_volume * 100))
|
||||||
@ -692,12 +696,10 @@ def cmd_remove(bot, user, text, command, parameter):
|
|||||||
global log
|
global log
|
||||||
|
|
||||||
# Allow to remove specific music into the queue with a number
|
# Allow to remove specific music into the queue with a number
|
||||||
if parameter and parameter.isdigit() and int(parameter) > 0 \
|
if parameter and parameter.isdigit() and 0 < int(parameter) <= len(var.playlist):
|
||||||
and int(parameter) <= len(var.playlist):
|
|
||||||
|
|
||||||
index = int(parameter) - 1
|
index = int(parameter) - 1
|
||||||
|
|
||||||
removed = None
|
|
||||||
if index == var.playlist.current_index:
|
if index == var.playlist.current_index:
|
||||||
removed = var.playlist.remove(index)
|
removed = var.playlist.remove(index)
|
||||||
|
|
||||||
@ -707,7 +709,7 @@ def cmd_remove(bot, user, text, command, parameter):
|
|||||||
var.playlist.current_index -= 1
|
var.playlist.current_index -= 1
|
||||||
# then the bot will move to next item
|
# then the bot will move to next item
|
||||||
|
|
||||||
else: # if item deleted is the last item of the queue
|
else: # if item deleted is the last item of the queue
|
||||||
var.playlist.current_index -= 1
|
var.playlist.current_index -= 1
|
||||||
if not bot.is_pause:
|
if not bot.is_pause:
|
||||||
bot.interrupt()
|
bot.interrupt()
|
||||||
@ -715,7 +717,7 @@ def cmd_remove(bot, user, text, command, parameter):
|
|||||||
removed = var.playlist.remove(index)
|
removed = var.playlist.remove(index)
|
||||||
|
|
||||||
bot.send_msg(constants.strings('removing_item',
|
bot.send_msg(constants.strings('removing_item',
|
||||||
item=removed.format_short_string()), text)
|
item=removed.format_short_string()), text)
|
||||||
|
|
||||||
log.info("cmd: delete from playlist: " + removed.format_debug_string())
|
log.info("cmd: delete from playlist: " + removed.format_debug_string())
|
||||||
else:
|
else:
|
||||||
@ -726,7 +728,7 @@ def cmd_list_file(bot, user, text, command, parameter):
|
|||||||
global log
|
global log
|
||||||
|
|
||||||
files = var.cache.files
|
files = var.cache.files
|
||||||
msgs = [ constants.strings("multiple_file_found") ]
|
msgs = [constants.strings("multiple_file_found")]
|
||||||
try:
|
try:
|
||||||
count = 0
|
count = 0
|
||||||
for index, file in enumerate(files):
|
for index, file in enumerate(files):
|
||||||
@ -755,29 +757,30 @@ def cmd_queue(bot, user, text, command, parameter):
|
|||||||
msg = constants.strings('queue_empty')
|
msg = constants.strings('queue_empty')
|
||||||
bot.send_msg(msg, text)
|
bot.send_msg(msg, text)
|
||||||
else:
|
else:
|
||||||
msgs = [ constants.strings('queue_contents')]
|
msgs = [constants.strings('queue_contents')]
|
||||||
for i, music in enumerate(var.playlist):
|
for i, music in enumerate(var.playlist):
|
||||||
newline = ''
|
|
||||||
tags = ''
|
tags = ''
|
||||||
if len(music.item().tags) > 0:
|
if len(music.item().tags) > 0:
|
||||||
tags = "<sup>{}</sup>".format(", ".join(music.item().tags))
|
tags = "<sup>{}</sup>".format(", ".join(music.item().tags))
|
||||||
if i == var.playlist.current_index:
|
if i == var.playlist.current_index:
|
||||||
newline = "<b style='color:orange'>{} ({}) {} </b> {}".format(i + 1, music.display_type(),
|
newline = "<b style='color:orange'>{} ({}) {} </b> {}".format(i + 1, music.display_type(),
|
||||||
music.format_short_string(), tags)
|
music.format_short_string(), tags)
|
||||||
else:
|
else:
|
||||||
newline = '<b>{}</b> ({}) {} {}'.format(i + 1, music.display_type(),
|
newline = '<b>{}</b> ({}) {} {}'.format(i + 1, music.display_type(),
|
||||||
music.format_short_string(), tags)
|
music.format_short_string(), tags)
|
||||||
|
|
||||||
msgs.append(newline)
|
msgs.append(newline)
|
||||||
|
|
||||||
send_multi_lines(bot, msgs, text)
|
send_multi_lines(bot, msgs, text)
|
||||||
|
|
||||||
|
|
||||||
def cmd_random(bot, user, text, command, parameter):
|
def cmd_random(bot, user, text, command, parameter):
|
||||||
global log
|
global log
|
||||||
|
|
||||||
bot.interrupt()
|
bot.interrupt()
|
||||||
var.playlist.randomize()
|
var.playlist.randomize()
|
||||||
|
|
||||||
|
|
||||||
def cmd_repeat(bot, user, text, command, parameter):
|
def cmd_repeat(bot, user, text, command, parameter):
|
||||||
global log
|
global log
|
||||||
|
|
||||||
@ -795,13 +798,14 @@ def cmd_repeat(bot, user, text, command, parameter):
|
|||||||
|
|
||||||
bot.send_msg(constants.strings("repeat", song=music.format_song_string(), n=str(repeat)), text)
|
bot.send_msg(constants.strings("repeat", song=music.format_song_string(), n=str(repeat)), text)
|
||||||
|
|
||||||
|
|
||||||
def cmd_mode(bot, user, text, command, parameter):
|
def cmd_mode(bot, user, text, command, parameter):
|
||||||
global log
|
global log
|
||||||
|
|
||||||
if not parameter:
|
if not parameter:
|
||||||
bot.send_msg(constants.strings("current_mode", mode=var.playlist.mode), text)
|
bot.send_msg(constants.strings("current_mode", mode=var.playlist.mode), text)
|
||||||
return
|
return
|
||||||
if not parameter in ["one-shot", "repeat", "random", "autoplay"]:
|
if parameter not in ["one-shot", "repeat", "random", "autoplay"]:
|
||||||
bot.send_msg(constants.strings('unknown_mode', mode=parameter), text)
|
bot.send_msg(constants.strings('unknown_mode', mode=parameter), text)
|
||||||
else:
|
else:
|
||||||
var.db.set('playlist', 'playback_mode', parameter)
|
var.db.set('playlist', 'playback_mode', parameter)
|
||||||
@ -813,6 +817,7 @@ def cmd_mode(bot, user, text, command, parameter):
|
|||||||
bot.interrupt()
|
bot.interrupt()
|
||||||
bot.launch_music()
|
bot.launch_music()
|
||||||
|
|
||||||
|
|
||||||
def cmd_play_tags(bot, user, text, command, parameter):
|
def cmd_play_tags(bot, user, text, command, parameter):
|
||||||
if not parameter:
|
if not parameter:
|
||||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||||
@ -829,7 +834,6 @@ def cmd_play_tags(bot, user, text, command, parameter):
|
|||||||
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
|
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
|
||||||
msgs.append("<li><b>{}</b> (<i>{}</i>)</li>".format(music_wrapper.item().title, ", ".join(music_wrapper.item().tags)))
|
msgs.append("<li><b>{}</b> (<i>{}</i>)</li>".format(music_wrapper.item().title, ", ".join(music_wrapper.item().tags)))
|
||||||
|
|
||||||
|
|
||||||
if count != 0:
|
if count != 0:
|
||||||
msgs.append("</ul>")
|
msgs.append("</ul>")
|
||||||
var.playlist.extend(music_wrappers)
|
var.playlist.extend(music_wrappers)
|
||||||
@ -842,8 +846,6 @@ def cmd_add_tag(bot, user, text, command, parameter):
|
|||||||
global log
|
global log
|
||||||
|
|
||||||
params = parameter.split()
|
params = parameter.split()
|
||||||
index = ""
|
|
||||||
tags = []
|
|
||||||
if len(params) == 2:
|
if len(params) == 2:
|
||||||
index = params[0]
|
index = params[0]
|
||||||
tags = list(map(lambda t: t.strip(), params[1].split(",")))
|
tags = list(map(lambda t: t.strip(), params[1].split(",")))
|
||||||
@ -880,8 +882,6 @@ def cmd_remove_tag(bot, user, text, command, parameter):
|
|||||||
|
|
||||||
params = parameter.split()
|
params = parameter.split()
|
||||||
|
|
||||||
index = ""
|
|
||||||
tags = []
|
|
||||||
if len(params) == 2:
|
if len(params) == 2:
|
||||||
index = params[0]
|
index = params[0]
|
||||||
tags = list(map(lambda t: t.strip(), params[1].split(",")))
|
tags = list(map(lambda t: t.strip(), params[1].split(",")))
|
||||||
@ -897,7 +897,7 @@ def cmd_remove_tag(bot, user, text, command, parameter):
|
|||||||
if tags[0] != "*":
|
if tags[0] != "*":
|
||||||
var.playlist[int(index) - 1].remove_tags(tags)
|
var.playlist[int(index) - 1].remove_tags(tags)
|
||||||
log.info("cmd: remove tags %s from song %s" % (", ".join(tags),
|
log.info("cmd: remove tags %s from song %s" % (", ".join(tags),
|
||||||
var.playlist[int(index) - 1].format_debug_string()))
|
var.playlist[int(index) - 1].format_debug_string()))
|
||||||
bot.send_msg(constants.strings("removed_tags",
|
bot.send_msg(constants.strings("removed_tags",
|
||||||
tags=", ".join(tags),
|
tags=", ".join(tags),
|
||||||
song=var.playlist[int(index) - 1].format_short_string()), text)
|
song=var.playlist[int(index) - 1].format_short_string()), text)
|
||||||
@ -914,7 +914,7 @@ def cmd_remove_tag(bot, user, text, command, parameter):
|
|||||||
for item in var.playlist:
|
for item in var.playlist:
|
||||||
item.remove_tags(tags)
|
item.remove_tags(tags)
|
||||||
log.info("cmd: remove tags %s from song %s" % (", ".join(tags),
|
log.info("cmd: remove tags %s from song %s" % (", ".join(tags),
|
||||||
item.format_debug_string()))
|
item.format_debug_string()))
|
||||||
bot.send_msg(constants.strings("removed_tags_from_all", tags=", ".join(tags)), text)
|
bot.send_msg(constants.strings("removed_tags_from_all", tags=", ".join(tags)), text)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
@ -926,6 +926,7 @@ def cmd_remove_tag(bot, user, text, command, parameter):
|
|||||||
|
|
||||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||||
|
|
||||||
|
|
||||||
def cmd_find_tagged(bot, user, text, command, parameter):
|
def cmd_find_tagged(bot, user, text, command, parameter):
|
||||||
global song_shortlist
|
global song_shortlist
|
||||||
|
|
||||||
@ -954,6 +955,7 @@ def cmd_find_tagged(bot, user, text, command, parameter):
|
|||||||
else:
|
else:
|
||||||
bot.send_msg(constants.strings("no_file"), text)
|
bot.send_msg(constants.strings("no_file"), text)
|
||||||
|
|
||||||
|
|
||||||
def cmd_search_library(bot, user, text, command, parameter):
|
def cmd_search_library(bot, user, text, command, parameter):
|
||||||
global song_shortlist
|
global song_shortlist
|
||||||
if not parameter:
|
if not parameter:
|
||||||
@ -993,9 +995,8 @@ def cmd_search_library(bot, user, text, command, parameter):
|
|||||||
|
|
||||||
def cmd_shortlist(bot, user, text, command, parameter):
|
def cmd_shortlist(bot, user, text, command, parameter):
|
||||||
global song_shortlist, log
|
global song_shortlist, log
|
||||||
indexes = []
|
|
||||||
try:
|
try:
|
||||||
indexes = [ int(i) for i in parameter.split(" ") ]
|
indexes = [int(i) for i in parameter.split(" ")]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||||
return
|
return
|
||||||
@ -1033,9 +1034,8 @@ def cmd_shortlist(bot, user, text, command, parameter):
|
|||||||
|
|
||||||
def cmd_delete_from_library(bot, user, text, command, parameter):
|
def cmd_delete_from_library(bot, user, text, command, parameter):
|
||||||
global song_shortlist, log
|
global song_shortlist, log
|
||||||
indexes = []
|
|
||||||
try:
|
try:
|
||||||
indexes = [ int(i) for i in parameter.split(" ") ]
|
indexes = [int(i) for i in parameter.split(" ")]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||||
return
|
return
|
||||||
@ -1049,7 +1049,7 @@ def cmd_delete_from_library(bot, user, text, command, parameter):
|
|||||||
if 'id' in music_dict:
|
if 'id' in music_dict:
|
||||||
music_wrapper = get_cached_wrapper_by_id(bot, music_dict['id'], user)
|
music_wrapper = get_cached_wrapper_by_id(bot, music_dict['id'], user)
|
||||||
log.info("cmd: remove from library: " + music_wrapper.format_debug_string())
|
log.info("cmd: remove from library: " + music_wrapper.format_debug_string())
|
||||||
msgs.append("<li>[{}] <b>{}</b></li>".format(music_wrapper.item().type ,music_wrapper.item().title))
|
msgs.append("<li>[{}] <b>{}</b></li>".format(music_wrapper.item().type, music_wrapper.item().title))
|
||||||
var.playlist.remove_by_id(music_dict['id'])
|
var.playlist.remove_by_id(music_dict['id'])
|
||||||
var.cache.free_and_delete(music_dict['id'])
|
var.cache.free_and_delete(music_dict['id'])
|
||||||
count += 1
|
count += 1
|
||||||
@ -1078,6 +1078,7 @@ def cmd_delete_from_library(bot, user, text, command, parameter):
|
|||||||
|
|
||||||
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
bot.send_msg(constants.strings('bad_parameter', command=command), text)
|
||||||
|
|
||||||
|
|
||||||
def cmd_drop_database(bot, user, text, command, parameter):
|
def cmd_drop_database(bot, user, text, command, parameter):
|
||||||
global log
|
global log
|
||||||
|
|
||||||
@ -1091,6 +1092,7 @@ def cmd_drop_database(bot, user, text, command, parameter):
|
|||||||
else:
|
else:
|
||||||
bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
|
bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
|
||||||
|
|
||||||
|
|
||||||
def cmd_refresh_cache(bot, user, text, command, parameter):
|
def cmd_refresh_cache(bot, user, text, command, parameter):
|
||||||
global log
|
global log
|
||||||
if bot.is_admin(user):
|
if bot.is_admin(user):
|
||||||
@ -1100,13 +1102,16 @@ def cmd_refresh_cache(bot, user, text, command, parameter):
|
|||||||
else:
|
else:
|
||||||
bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
|
bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
|
||||||
|
|
||||||
|
|
||||||
# Just for debug use
|
# Just for debug use
|
||||||
def cmd_real_time_rms(bot, user, text, command, parameter):
|
def cmd_real_time_rms(bot, user, text, command, parameter):
|
||||||
bot._display_rms = not bot._display_rms
|
bot._display_rms = not bot._display_rms
|
||||||
|
|
||||||
|
|
||||||
def cmd_loop_state(bot, user, text, command, parameter):
|
def cmd_loop_state(bot, user, text, command, parameter):
|
||||||
print(bot._loop_status)
|
print(bot._loop_status)
|
||||||
|
|
||||||
|
|
||||||
def cmd_item(bot, user, text, command, parameter):
|
def cmd_item(bot, user, text, command, parameter):
|
||||||
print(bot.wait_for_downloading)
|
print(bot.wait_for_downloading)
|
||||||
print(var.playlist.current_item().to_dict())
|
print(var.playlist.current_item().to_dict())
|
||||||
|
26
constants.py
26
constants.py
@ -1,32 +1,32 @@
|
|||||||
import variables as var
|
import variables as var
|
||||||
|
|
||||||
|
|
||||||
def strings(option, *argv, **kwargs):
|
def strings(option, *argv, **kwargs):
|
||||||
string = ""
|
|
||||||
try:
|
try:
|
||||||
string = var.config.get("strings", option)
|
string = var.config.get("strings", option)
|
||||||
except KeyError as e:
|
except KeyError:
|
||||||
raise KeyError("Missed strings in configuration file: '{string}'. ".format(string=option) +
|
raise KeyError("Missed strings in configuration file: '{string}'. ".format(string=option)
|
||||||
"Please restore you configuration file back to default if necessary.")
|
+ "Please restore you configuration file back to default if necessary.")
|
||||||
if argv or kwargs:
|
if argv or kwargs:
|
||||||
try:
|
try:
|
||||||
formatted = string.format(*argv, **kwargs)
|
formatted = string.format(*argv, **kwargs)
|
||||||
return formatted
|
return formatted
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
raise KeyError(
|
raise KeyError(
|
||||||
"Missed/Unexpected placeholder {{{placeholder}}} in string '{string}'. ".format(placeholder=str(e).strip("'"), string=option) +
|
"Missed/Unexpected placeholder {{{placeholder}}} in string '{string}'. ".format(placeholder=str(e).strip("'"), string=option)
|
||||||
"Please restore you configuration file back to default if necessary.")
|
+ "Please restore you configuration file back to default if necessary.")
|
||||||
except TypeError as e:
|
except TypeError:
|
||||||
raise KeyError(
|
raise KeyError(
|
||||||
"Missed placeholder in string '{string}'. ".format(string=option) +
|
"Missed placeholder in string '{string}'. ".format(string=option)
|
||||||
"Please restore you configuration file back to default if necessary.")
|
+ "Please restore you configuration file back to default if necessary.")
|
||||||
else:
|
else:
|
||||||
return string
|
return string
|
||||||
|
|
||||||
|
|
||||||
def commands(command):
|
def commands(command):
|
||||||
string = ""
|
|
||||||
try:
|
try:
|
||||||
string = var.config.get("commands", command)
|
string = var.config.get("commands", command)
|
||||||
return string
|
return string
|
||||||
except KeyError as e:
|
except KeyError:
|
||||||
raise KeyError("Missed command in configuration file: '{string}'. ".format(string=command) +
|
raise KeyError("Missed command in configuration file: '{string}'. ".format(string=command)
|
||||||
"Please restore you configuration file back to default if necessary.")
|
+ "Please restore you configuration file back to default if necessary.")
|
||||||
|
18
database.py
18
database.py
@ -2,17 +2,19 @@ import sqlite3
|
|||||||
import json
|
import json
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
class DatabaseError(Exception):
|
class DatabaseError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SettingsDatabase:
|
class SettingsDatabase:
|
||||||
version = 1
|
version = 1
|
||||||
|
|
||||||
def __init__(self, db_path):
|
def __init__(self, db_path):
|
||||||
self.db_path = db_path
|
self.db_path = db_path
|
||||||
|
|
||||||
# connect
|
# connect
|
||||||
conn = sqlite3.connect(self.db_path)
|
conn = sqlite3.connect(self.db_path)
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
self.db_version_check_and_create()
|
self.db_version_check_and_create()
|
||||||
|
|
||||||
@ -57,9 +59,9 @@ class SettingsDatabase:
|
|||||||
"value TEXT, "
|
"value TEXT, "
|
||||||
"UNIQUE(section, option))")
|
"UNIQUE(section, option))")
|
||||||
cursor.execute("INSERT INTO botamusique (section, option, value) "
|
cursor.execute("INSERT INTO botamusique (section, option, value) "
|
||||||
"VALUES (?, ?, ?)" , ("bot", "db_version", "1"))
|
"VALUES (?, ?, ?)", ("bot", "db_version", "1"))
|
||||||
cursor.execute("INSERT INTO botamusique (section, option, value) "
|
cursor.execute("INSERT INTO botamusique (section, option, value) "
|
||||||
"VALUES (?, ?, ?)" , ("bot", "music_db_version", "0"))
|
"VALUES (?, ?, ?)", ("bot", "music_db_version", "0"))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
@ -90,7 +92,7 @@ class SettingsDatabase:
|
|||||||
conn = sqlite3.connect(self.db_path)
|
conn = sqlite3.connect(self.db_path)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("INSERT OR REPLACE INTO botamusique (section, option, value) "
|
cursor.execute("INSERT OR REPLACE INTO botamusique (section, option, value) "
|
||||||
"VALUES (?, ?, ?)" , (section, option, value))
|
"VALUES (?, ?, ?)", (section, option, value))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
@ -214,7 +216,7 @@ class MusicDatabase:
|
|||||||
conn = sqlite3.connect(self.db_path)
|
conn = sqlite3.connect(self.db_path)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
results = cursor.execute("SELECT id, type, title, metadata, tags FROM music "
|
results = cursor.execute("SELECT id, type, title, metadata, tags FROM music "
|
||||||
"WHERE %s" % condition_str, filler).fetchall()
|
"WHERE %s" % condition_str, filler).fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return self._result_to_dict(results)
|
return self._result_to_dict(results)
|
||||||
@ -227,7 +229,6 @@ class MusicDatabase:
|
|||||||
condition.append('LOWER(title) LIKE ?')
|
condition.append('LOWER(title) LIKE ?')
|
||||||
filler.append("%{:s}%".format(keyword.lower()))
|
filler.append("%{:s}%".format(keyword.lower()))
|
||||||
|
|
||||||
|
|
||||||
condition_str = " AND ".join(condition)
|
condition_str = " AND ".join(condition)
|
||||||
|
|
||||||
conn = sqlite3.connect(self.db_path)
|
conn = sqlite3.connect(self.db_path)
|
||||||
@ -246,7 +247,6 @@ class MusicDatabase:
|
|||||||
condition.append('LOWER(tags) LIKE ?')
|
condition.append('LOWER(tags) LIKE ?')
|
||||||
filler.append("%{:s},%".format(tag.lower()))
|
filler.append("%{:s},%".format(tag.lower()))
|
||||||
|
|
||||||
|
|
||||||
condition_str = " AND ".join(condition)
|
condition_str = " AND ".join(condition)
|
||||||
|
|
||||||
conn = sqlite3.connect(self.db_path)
|
conn = sqlite3.connect(self.db_path)
|
||||||
@ -284,7 +284,6 @@ class MusicDatabase:
|
|||||||
|
|
||||||
return self._result_to_dict(results)
|
return self._result_to_dict(results)
|
||||||
|
|
||||||
|
|
||||||
def _result_to_dict(self, results):
|
def _result_to_dict(self, results):
|
||||||
if len(results) > 0:
|
if len(results) > 0:
|
||||||
music_dicts = []
|
music_dicts = []
|
||||||
@ -320,11 +319,10 @@ class MusicDatabase:
|
|||||||
conn = sqlite3.connect(self.db_path)
|
conn = sqlite3.connect(self.db_path)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("DELETE FROM music "
|
cursor.execute("DELETE FROM music "
|
||||||
"WHERE %s" % condition_str, filler)
|
"WHERE %s" % condition_str, filler)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
def drop_table(self):
|
def drop_table(self):
|
||||||
conn = sqlite3.connect(self.db_path)
|
conn = sqlite3.connect(self.db_path)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
45
interface.py
45
interface.py
@ -16,7 +16,7 @@ import time
|
|||||||
|
|
||||||
|
|
||||||
class ReverseProxied(object):
|
class ReverseProxied(object):
|
||||||
'''Wrap the application in this middleware and configure the
|
"""Wrap the application in this middleware and configure the
|
||||||
front-end server to add these headers, to let you quietly bind
|
front-end server to add these headers, to let you quietly bind
|
||||||
this to a URL other than / and to an HTTP scheme that is
|
this to a URL other than / and to an HTTP scheme that is
|
||||||
different than what is used locally.
|
different than what is used locally.
|
||||||
@ -31,7 +31,7 @@ class ReverseProxied(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
:param app: the WSGI application
|
:param app: the WSGI application
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
self.app = app
|
self.app = app
|
||||||
@ -57,6 +57,7 @@ web = Flask(__name__)
|
|||||||
log = logging.getLogger("bot")
|
log = logging.getLogger("bot")
|
||||||
user = 'Remote Control'
|
user = 'Remote Control'
|
||||||
|
|
||||||
|
|
||||||
def init_proxy():
|
def init_proxy():
|
||||||
global web
|
global web
|
||||||
if var.is_proxified:
|
if var.is_proxified:
|
||||||
@ -64,19 +65,21 @@ def init_proxy():
|
|||||||
|
|
||||||
# https://stackoverflow.com/questions/29725217/password-protect-one-webpage-in-flask-app
|
# https://stackoverflow.com/questions/29725217/password-protect-one-webpage-in-flask-app
|
||||||
|
|
||||||
|
|
||||||
def check_auth(username, password):
|
def check_auth(username, password):
|
||||||
"""This function is called to check if a username /
|
"""This function is called to check if a username /
|
||||||
password combination is valid.
|
password combination is valid.
|
||||||
"""
|
"""
|
||||||
return username == var.config.get("webinterface", "user") and password == var.config.get("webinterface", "password")
|
return username == var.config.get("webinterface", "user") and password == var.config.get("webinterface", "password")
|
||||||
|
|
||||||
|
|
||||||
def authenticate():
|
def authenticate():
|
||||||
"""Sends a 401 response that enables basic auth"""
|
"""Sends a 401 response that enables basic auth"""
|
||||||
global log
|
global log
|
||||||
return Response(
|
return Response('Could not verify your access level for that URL.\n'
|
||||||
'Could not verify your access level for that URL.\n'
|
'You have to login with proper credentials', 401,
|
||||||
'You have to login with proper credentials', 401,
|
{'WWW-Authenticate': 'Basic realm="Login Required"'})
|
||||||
{'WWW-Authenticate': 'Basic realm="Login Required"'})
|
|
||||||
|
|
||||||
def requires_auth(f):
|
def requires_auth(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
@ -90,6 +93,7 @@ def requires_auth(f):
|
|||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
return decorated
|
return decorated
|
||||||
|
|
||||||
|
|
||||||
def tag_color(tag):
|
def tag_color(tag):
|
||||||
num = hash(tag) % 8
|
num = hash(tag) % 8
|
||||||
if num == 0:
|
if num == 0:
|
||||||
@ -109,14 +113,15 @@ def tag_color(tag):
|
|||||||
elif num == 7:
|
elif num == 7:
|
||||||
return "dark"
|
return "dark"
|
||||||
|
|
||||||
|
|
||||||
def build_tags_color_lookup():
|
def build_tags_color_lookup():
|
||||||
color_lookup = {}
|
color_lookup = {}
|
||||||
for tag in var.music_db.query_all_tags():
|
for tag in var.music_db.query_all_tags():
|
||||||
color_lookup[tag] = tag_color(tag)
|
color_lookup[tag] = tag_color(tag)
|
||||||
|
|
||||||
|
|
||||||
return color_lookup
|
return color_lookup
|
||||||
|
|
||||||
|
|
||||||
def build_path_tags_lookup():
|
def build_path_tags_lookup():
|
||||||
path_tags_lookup = {}
|
path_tags_lookup = {}
|
||||||
ids = list(var.cache.file_id_lookup.values())
|
ids = list(var.cache.file_id_lookup.values())
|
||||||
@ -128,11 +133,13 @@ def build_path_tags_lookup():
|
|||||||
|
|
||||||
return path_tags_lookup
|
return path_tags_lookup
|
||||||
|
|
||||||
|
|
||||||
def recur_dir(dirobj):
|
def recur_dir(dirobj):
|
||||||
for name, dir in dirobj.get_subdirs().items():
|
for name, dir in dirobj.get_subdirs().items():
|
||||||
print(dirobj.fullpath + "/" + name)
|
print(dirobj.fullpath + "/" + name)
|
||||||
recur_dir(dir)
|
recur_dir(dir)
|
||||||
|
|
||||||
|
|
||||||
@web.route("/", methods=['GET'])
|
@web.route("/", methods=['GET'])
|
||||||
@requires_auth
|
@requires_auth
|
||||||
def index():
|
def index():
|
||||||
@ -153,21 +160,22 @@ def index():
|
|||||||
paused=var.bot.is_pause,
|
paused=var.bot.is_pause,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@web.route("/playlist", methods=['GET'])
|
@web.route("/playlist", methods=['GET'])
|
||||||
@requires_auth
|
@requires_auth
|
||||||
def playlist():
|
def playlist():
|
||||||
if len(var.playlist) == 0:
|
if len(var.playlist) == 0:
|
||||||
return jsonify({'items': [render_template('playlist.html',
|
return jsonify({'items': [render_template('playlist.html',
|
||||||
m=False,
|
m=False,
|
||||||
index=-1
|
index=-1
|
||||||
)]
|
)]
|
||||||
})
|
})
|
||||||
|
|
||||||
tags_color_lookup = build_tags_color_lookup()
|
tags_color_lookup = build_tags_color_lookup()
|
||||||
items = []
|
items = []
|
||||||
|
|
||||||
for index, item_wrapper in enumerate(var.playlist):
|
for index, item_wrapper in enumerate(var.playlist):
|
||||||
items.append(render_template('playlist.html',
|
items.append(render_template('playlist.html',
|
||||||
index=index,
|
index=index,
|
||||||
tags_color_lookup=tags_color_lookup,
|
tags_color_lookup=tags_color_lookup,
|
||||||
m=item_wrapper.item(),
|
m=item_wrapper.item(),
|
||||||
@ -175,7 +183,8 @@ def playlist():
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return jsonify({ 'items': items })
|
return jsonify({'items': items})
|
||||||
|
|
||||||
|
|
||||||
def status():
|
def status():
|
||||||
if len(var.playlist) > 0:
|
if len(var.playlist) > 0:
|
||||||
@ -197,7 +206,7 @@ def post():
|
|||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if request.form:
|
if request.form:
|
||||||
log.debug("web: Post request from %s: %s" % ( request.remote_addr, str(request.form)))
|
log.debug("web: Post request from %s: %s" % (request.remote_addr, str(request.form)))
|
||||||
if 'add_file_bottom' in request.form and ".." not in request.form['add_file_bottom']:
|
if 'add_file_bottom' in request.form and ".." not in request.form['add_file_bottom']:
|
||||||
path = var.music_folder + request.form['add_file_bottom']
|
path = var.music_folder + request.form['add_file_bottom']
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
@ -231,15 +240,13 @@ def post():
|
|||||||
|
|
||||||
music_wrappers = list(map(
|
music_wrappers = list(map(
|
||||||
lambda file:
|
lambda file:
|
||||||
get_cached_wrapper_by_id(var.bot, var.cache.file_id_lookup[folder + file], user),
|
get_cached_wrapper_by_id(var.bot, var.cache.file_id_lookup[folder + file], user), files))
|
||||||
files))
|
|
||||||
|
|
||||||
var.playlist.extend(music_wrappers)
|
var.playlist.extend(music_wrappers)
|
||||||
|
|
||||||
for music_wrapper in music_wrappers:
|
for music_wrapper in music_wrappers:
|
||||||
log.info('web: add to playlist: ' + music_wrapper.format_debug_string())
|
log.info('web: add to playlist: ' + music_wrapper.format_debug_string())
|
||||||
|
|
||||||
|
|
||||||
elif 'add_url' in request.form:
|
elif 'add_url' in request.form:
|
||||||
music_wrapper = get_cached_wrapper_from_scrap(var.bot, type='url', url=request.form['add_url'], user=user)
|
music_wrapper = get_cached_wrapper_from_scrap(var.bot, type='url', url=request.form['add_url'], user=user)
|
||||||
var.playlist.append(music_wrapper)
|
var.playlist.append(music_wrapper)
|
||||||
@ -279,7 +286,6 @@ def post():
|
|||||||
else:
|
else:
|
||||||
var.playlist.remove(index)
|
var.playlist.remove(index)
|
||||||
|
|
||||||
|
|
||||||
elif 'play_music' in request.form:
|
elif 'play_music' in request.form:
|
||||||
music_wrapper = var.playlist[int(request.form['play_music'])]
|
music_wrapper = var.playlist[int(request.form['play_music'])]
|
||||||
log.info("web: jump to: " + music_wrapper.format_debug_string())
|
log.info("web: jump to: " + music_wrapper.format_debug_string())
|
||||||
@ -358,6 +364,7 @@ def post():
|
|||||||
|
|
||||||
return status()
|
return status()
|
||||||
|
|
||||||
|
|
||||||
@web.route('/upload', methods=["POST"])
|
@web.route('/upload', methods=["POST"])
|
||||||
def upload():
|
def upload():
|
||||||
global log
|
global log
|
||||||
@ -366,7 +373,7 @@ def upload():
|
|||||||
if not files:
|
if not files:
|
||||||
return redirect("./", code=406)
|
return redirect("./", code=406)
|
||||||
|
|
||||||
#filename = secure_filename(file.filename).strip()
|
# filename = secure_filename(file.filename).strip()
|
||||||
for file in files:
|
for file in files:
|
||||||
filename = file.filename
|
filename = file.filename
|
||||||
if filename == '':
|
if filename == '':
|
||||||
@ -385,7 +392,7 @@ def upload():
|
|||||||
|
|
||||||
if "audio" in file.mimetype:
|
if "audio" in file.mimetype:
|
||||||
storagepath = os.path.abspath(os.path.join(var.music_folder, targetdir))
|
storagepath = os.path.abspath(os.path.join(var.music_folder, targetdir))
|
||||||
print('storagepath:',storagepath)
|
print('storagepath:', storagepath)
|
||||||
if not storagepath.startswith(os.path.abspath(var.music_folder)):
|
if not storagepath.startswith(os.path.abspath(var.music_folder)):
|
||||||
return redirect("./", code=406)
|
return redirect("./", code=406)
|
||||||
|
|
||||||
|
@ -2,28 +2,32 @@ from librb.rbRadios import RadioBrowser
|
|||||||
|
|
||||||
rb = RadioBrowser()
|
rb = RadioBrowser()
|
||||||
|
|
||||||
|
|
||||||
def getstations_byname(query):
|
def getstations_byname(query):
|
||||||
results = rb.stations_byname(query)
|
results = rb.stations_byname(query)
|
||||||
stations = []
|
stations = []
|
||||||
for st in results:
|
for st in results:
|
||||||
try:
|
try:
|
||||||
station = {'stationname': st['name'], 'id':st['id'], 'codec':st['codec'], 'bitrate':st['bitrate'], 'country':st['country'], 'homepage':st['homepage'], 'genre':st['tags']}
|
station = {'stationname': st['name'], 'id': st['id'], 'codec': st['codec'], 'bitrate': st['bitrate'], 'country': st['country'], 'homepage': st['homepage'], 'genre': st['tags']}
|
||||||
stations.append(station)
|
stations.append(station)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
return stations
|
return stations
|
||||||
|
|
||||||
|
|
||||||
def geturl_byid(id):
|
def geturl_byid(id):
|
||||||
url = rb.playable_station(id)['url']
|
url = rb.playable_station(id)['url']
|
||||||
if url != None:
|
if url is not None:
|
||||||
return url
|
return url
|
||||||
else:
|
else:
|
||||||
return "-1"
|
return "-1"
|
||||||
|
|
||||||
|
|
||||||
def getstationname_byid(id):
|
def getstationname_byid(id):
|
||||||
return rb.stations_byid(id)
|
return rb.stations_byid(id)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
r = getstations_byname('r.sh')
|
r = getstations_byname('r.sh')
|
||||||
stationinfo = getstationname_byid(96748)
|
stationinfo = getstationname_byid(96748)
|
||||||
pass
|
pass
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
from xml.etree import ElementTree
|
|
||||||
from urllib.parse import urljoin
|
|
||||||
|
|
||||||
from librb.rbConstants import endpoints, BASE_URL
|
from librb.rbConstants import endpoints, BASE_URL
|
||||||
|
|
||||||
|
|
||||||
@ -179,5 +176,4 @@ class RadioBrowser:
|
|||||||
assert isinstance(params, dict), "params is not a dict"
|
assert isinstance(params, dict), "params is not a dict"
|
||||||
kwargs["params"] = params
|
kwargs["params"] = params
|
||||||
endpoint = self.builder.produce_endpoint(endpoint="station_search")
|
endpoint = self.builder.produce_endpoint(endpoint="station_search")
|
||||||
return request(endpoint, **kwargs)
|
return request(endpoint, **kwargs)
|
||||||
|
|
@ -1,11 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from database import MusicDatabase
|
|
||||||
import json
|
import json
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from media.item import item_builders, item_loaders, item_id_generators, dict_to_item, dicts_to_items
|
from media.item import item_builders, item_id_generators, dict_to_item
|
||||||
from database import MusicDatabase
|
from database import MusicDatabase
|
||||||
import variables as var
|
import variables as var
|
||||||
import util
|
import util
|
||||||
@ -18,9 +17,10 @@ class MusicCache(dict):
|
|||||||
self.log = logging.getLogger("bot")
|
self.log = logging.getLogger("bot")
|
||||||
self.dir = None
|
self.dir = None
|
||||||
self.files = []
|
self.files = []
|
||||||
|
self.file_id_lookup = {}
|
||||||
self.dir_lock = threading.Lock()
|
self.dir_lock = threading.Lock()
|
||||||
|
|
||||||
def get_item_by_id(self, bot, id): # Why all these functions need a bot? Because it need the bot to send message!
|
def get_item_by_id(self, bot, id): # Why all these functions need a bot? Because it need the bot to send message!
|
||||||
if id in self:
|
if id in self:
|
||||||
return self[id]
|
return self[id]
|
||||||
|
|
||||||
@ -32,9 +32,8 @@ class MusicCache(dict):
|
|||||||
return item
|
return item
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
#print(id)
|
# print(id)
|
||||||
#raise KeyError("Unable to fetch item from the database! Please try to refresh the cache by !recache.")
|
# raise KeyError("Unable to fetch item from the database! Please try to refresh the cache by !recache.")
|
||||||
|
|
||||||
|
|
||||||
def get_item(self, bot, **kwargs):
|
def get_item(self, bot, **kwargs):
|
||||||
# kwargs should provide type and id, and parameters to build the item if not existed in the library.
|
# kwargs should provide type and id, and parameters to build the item if not existed in the library.
|
||||||
@ -55,7 +54,7 @@ class MusicCache(dict):
|
|||||||
return item
|
return item
|
||||||
|
|
||||||
# if not in the database, build one
|
# if not in the database, build one
|
||||||
self[id] = item_builders[kwargs['type']](bot, **kwargs) # newly built item will not be saved immediately
|
self[id] = item_builders[kwargs['type']](bot, **kwargs) # newly built item will not be saved immediately
|
||||||
return self[id]
|
return self[id]
|
||||||
|
|
||||||
def get_items_by_tags(self, bot, tags):
|
def get_items_by_tags(self, bot, tags):
|
||||||
@ -112,7 +111,6 @@ class MusicCache(dict):
|
|||||||
self.dir_lock.acquire()
|
self.dir_lock.acquire()
|
||||||
self.log.info("library: rebuild directory cache")
|
self.log.info("library: rebuild directory cache")
|
||||||
self.files = []
|
self.files = []
|
||||||
self.file_id_lookup = {}
|
|
||||||
files = util.get_recursive_file_list_sorted(var.music_folder)
|
files = util.get_recursive_file_list_sorted(var.music_folder)
|
||||||
self.dir = util.Dir(var.music_folder)
|
self.dir = util.Dir(var.music_folder)
|
||||||
for file in files:
|
for file in files:
|
||||||
@ -254,4 +252,4 @@ def get_cached_wrappers_by_tags(bot, tags, user):
|
|||||||
ret = []
|
ret = []
|
||||||
for item in items:
|
for item in items:
|
||||||
ret.append(CachedItemWrapper(var.cache, item.id, item.type, user))
|
ret.append(CachedItemWrapper(var.cache, item.id, item.type, user))
|
||||||
return ret
|
return ret
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
@ -6,9 +5,7 @@ import base64
|
|||||||
import hashlib
|
import hashlib
|
||||||
import mutagen
|
import mutagen
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import json
|
|
||||||
|
|
||||||
import util
|
|
||||||
import variables as var
|
import variables as var
|
||||||
from media.item import BaseItem, item_builders, item_loaders, item_id_generators
|
from media.item import BaseItem, item_builders, item_loaders, item_id_generators
|
||||||
import constants
|
import constants
|
||||||
@ -24,15 +21,19 @@ type : file
|
|||||||
user
|
user
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
def file_item_builder(bot, **kwargs):
|
def file_item_builder(bot, **kwargs):
|
||||||
return FileItem(bot, kwargs['path'])
|
return FileItem(bot, kwargs['path'])
|
||||||
|
|
||||||
|
|
||||||
def file_item_loader(bot, _dict):
|
def file_item_loader(bot, _dict):
|
||||||
return FileItem(bot, "", _dict)
|
return FileItem(bot, "", _dict)
|
||||||
|
|
||||||
|
|
||||||
def file_item_id_generator(**kwargs):
|
def file_item_id_generator(**kwargs):
|
||||||
return hashlib.md5(kwargs['path'].encode()).hexdigest()
|
return hashlib.md5(kwargs['path'].encode()).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
item_builders['file'] = file_item_builder
|
item_builders['file'] = file_item_builder
|
||||||
item_loaders['file'] = file_item_loader
|
item_loaders['file'] = file_item_loader
|
||||||
item_id_generators['file'] = file_item_id_generator
|
item_id_generators['file'] = file_item_id_generator
|
||||||
@ -74,12 +75,12 @@ class FileItem(BaseItem):
|
|||||||
self.send_client_message(constants.strings('file_missed', file=self.path))
|
self.send_client_message(constants.strings('file_missed', file=self.path))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#self.version += 1 # 0 -> 1, notify the wrapper to save me when validate() is visited the first time
|
# self.version += 1 # 0 -> 1, notify the wrapper to save me when validate() is visited the first time
|
||||||
self.ready = "yes"
|
self.ready = "yes"
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _get_info_from_tag(self):
|
def _get_info_from_tag(self):
|
||||||
match = re.search("(.+)\.(.+)", self.uri())
|
match = re.search(r"(.+)\.(.+)", self.uri())
|
||||||
assert match is not None
|
assert match is not None
|
||||||
|
|
||||||
file_no_ext = match[1]
|
file_no_ext = match[1]
|
||||||
@ -153,17 +154,17 @@ class FileItem(BaseItem):
|
|||||||
|
|
||||||
def format_song_string(self, user):
|
def format_song_string(self, user):
|
||||||
return constants.strings("file_item",
|
return constants.strings("file_item",
|
||||||
title=self.title,
|
title=self.title,
|
||||||
artist=self.artist if self.artist else '??',
|
artist=self.artist if self.artist else '??',
|
||||||
user=user
|
user=user
|
||||||
)
|
)
|
||||||
|
|
||||||
def format_current_playing(self, user):
|
def format_current_playing(self, user):
|
||||||
display = constants.strings("now_playing", item=self.format_song_string(user))
|
display = constants.strings("now_playing", item=self.format_song_string(user))
|
||||||
if self.thumbnail:
|
if self.thumbnail:
|
||||||
thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
|
thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
|
||||||
self.thumbnail + '"/>'
|
self.thumbnail + '"/>'
|
||||||
display += "<br />" + thumbnail_html
|
display += "<br />" + thumbnail_html
|
||||||
|
|
||||||
return display
|
return display
|
||||||
|
|
||||||
|
@ -1,33 +1,27 @@
|
|||||||
import logging
|
import logging
|
||||||
import threading
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
from io import BytesIO
|
|
||||||
import base64
|
|
||||||
import hashlib
|
|
||||||
import mutagen
|
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
import util
|
|
||||||
import variables as var
|
|
||||||
|
|
||||||
item_builders = {}
|
item_builders = {}
|
||||||
item_loaders = {}
|
item_loaders = {}
|
||||||
item_id_generators = {}
|
item_id_generators = {}
|
||||||
|
|
||||||
|
|
||||||
def example_builder(bot, **kwargs):
|
def example_builder(bot, **kwargs):
|
||||||
return BaseItem(bot)
|
return BaseItem(bot)
|
||||||
|
|
||||||
|
|
||||||
def example_loader(bot, _dict):
|
def example_loader(bot, _dict):
|
||||||
return BaseItem(bot, from_dict=_dict)
|
return BaseItem(bot, from_dict=_dict)
|
||||||
|
|
||||||
|
|
||||||
def example_id_generator(**kwargs):
|
def example_id_generator(**kwargs):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
item_builders['base'] = example_builder
|
item_builders['base'] = example_builder
|
||||||
item_loaders['base'] = example_loader
|
item_loaders['base'] = example_loader
|
||||||
item_id_generators['base'] = example_id_generator
|
item_id_generators['base'] = example_id_generator
|
||||||
|
|
||||||
|
|
||||||
def dicts_to_items(bot, music_dicts):
|
def dicts_to_items(bot, music_dicts):
|
||||||
items = []
|
items = []
|
||||||
for music_dict in music_dicts:
|
for music_dict in music_dicts:
|
||||||
@ -35,6 +29,7 @@ def dicts_to_items(bot, music_dicts):
|
|||||||
items.append(item_loaders[type](bot, music_dict))
|
items.append(item_loaders[type](bot, music_dict))
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
def dict_to_item(bot, music_dict):
|
def dict_to_item(bot, music_dict):
|
||||||
type = music_dict['type']
|
type = music_dict['type']
|
||||||
return item_loaders[type](bot, music_dict)
|
return item_loaders[type](bot, music_dict)
|
||||||
@ -48,11 +43,11 @@ class BaseItem:
|
|||||||
self.title = ""
|
self.title = ""
|
||||||
self.path = ""
|
self.path = ""
|
||||||
self.tags = []
|
self.tags = []
|
||||||
self.version = 0 # if version increase, wrapper will re-save this item
|
self.version = 0 # if version increase, wrapper will re-save this item
|
||||||
|
|
||||||
if from_dict is None:
|
if from_dict is None:
|
||||||
self.id = ""
|
self.id = ""
|
||||||
self.ready = "pending" # pending - is_valid() -> validated - prepare() -> yes, failed
|
self.ready = "pending" # pending - is_valid() -> validated - prepare() -> yes, failed
|
||||||
else:
|
else:
|
||||||
self.id = from_dict['id']
|
self.id = from_dict['id']
|
||||||
self.ready = from_dict['ready']
|
self.ready = from_dict['ready']
|
||||||
@ -110,6 +105,4 @@ class BaseItem:
|
|||||||
self.bot.send_msg(msg)
|
self.bot.send_msg(msg)
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {"type" : self.type, "id": self.id, "ready": self.ready, "path": self.path, "tags": self.tags}
|
return {"type": self.type, "id": self.id, "ready": self.ready, "path": self.path, "tags": self.tags}
|
||||||
|
|
||||||
|
|
||||||
|
@ -162,12 +162,12 @@ class BasePlaylist(list):
|
|||||||
|
|
||||||
def randomize(self):
|
def randomize(self):
|
||||||
# current_index will lose track after shuffling, thus we take current music out before shuffling
|
# current_index will lose track after shuffling, thus we take current music out before shuffling
|
||||||
#current = self.current_item()
|
# current = self.current_item()
|
||||||
#del self[self.current_index]
|
# del self[self.current_index]
|
||||||
|
|
||||||
random.shuffle(self)
|
random.shuffle(self)
|
||||||
|
|
||||||
#self.insert(0, current)
|
# self.insert(0, current)
|
||||||
self.current_index = -1
|
self.current_index = -1
|
||||||
self.version += 1
|
self.version += 1
|
||||||
|
|
||||||
@ -183,7 +183,7 @@ class BasePlaylist(list):
|
|||||||
var.db.set("playlist", "current_index", self.current_index)
|
var.db.set("playlist", "current_index", self.current_index)
|
||||||
|
|
||||||
for index, music in enumerate(self):
|
for index, music in enumerate(self):
|
||||||
var.db.set("playlist_item", str(index), json.dumps({'id': music.id, 'user': music.user }))
|
var.db.set("playlist_item", str(index), json.dumps({'id': music.id, 'user': music.user}))
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
current_index = var.db.getint("playlist", "current_index", fallback=-1)
|
current_index = var.db.getint("playlist", "current_index", fallback=-1)
|
||||||
@ -212,7 +212,7 @@ class BasePlaylist(list):
|
|||||||
|
|
||||||
def start_async_validating(self):
|
def start_async_validating(self):
|
||||||
if not self.validating_thread_lock.locked():
|
if not self.validating_thread_lock.locked():
|
||||||
time.sleep(0.1) # Just avoid validation finishes too fast and delete songs while something is reading it.
|
time.sleep(0.1) # Just avoid validation finishes too fast and delete songs while something is reading it.
|
||||||
th = threading.Thread(target=self._check_valid, name="Validating")
|
th = threading.Thread(target=self._check_valid, name="Validating")
|
||||||
th.daemon = True
|
th.daemon = True
|
||||||
th.start()
|
th.start()
|
||||||
@ -359,4 +359,3 @@ class AutoPlaylist(OneshotPlaylist):
|
|||||||
if len(self) == 0:
|
if len(self) == 0:
|
||||||
self.refresh()
|
self.refresh()
|
||||||
return super().next()
|
return super().next()
|
||||||
|
|
||||||
|
@ -11,16 +11,16 @@ import constants
|
|||||||
|
|
||||||
log = logging.getLogger("bot")
|
log = logging.getLogger("bot")
|
||||||
|
|
||||||
|
|
||||||
def get_radio_server_description(url):
|
def get_radio_server_description(url):
|
||||||
global log
|
global log
|
||||||
|
|
||||||
log.debug("radio: fetching radio server description")
|
log.debug("radio: fetching radio server description")
|
||||||
p = re.compile('(https?\:\/\/[^\/]*)', re.IGNORECASE)
|
p = re.compile('(https?://[^/]*)', re.IGNORECASE)
|
||||||
res = re.search(p, url)
|
res = re.search(p, url)
|
||||||
base_url = res.group(1)
|
base_url = res.group(1)
|
||||||
url_icecast = base_url + '/status-json.xsl'
|
url_icecast = base_url + '/status-json.xsl'
|
||||||
url_shoutcast = base_url + '/stats?json=1'
|
url_shoutcast = base_url + '/stats?json=1'
|
||||||
title_server = None
|
|
||||||
try:
|
try:
|
||||||
r = requests.get(url_shoutcast, timeout=10)
|
r = requests.get(url_shoutcast, timeout=10)
|
||||||
data = r.json()
|
data = r.json()
|
||||||
@ -30,7 +30,7 @@ def get_radio_server_description(url):
|
|||||||
except (requests.exceptions.ConnectionError,
|
except (requests.exceptions.ConnectionError,
|
||||||
requests.exceptions.HTTPError,
|
requests.exceptions.HTTPError,
|
||||||
requests.exceptions.ReadTimeout,
|
requests.exceptions.ReadTimeout,
|
||||||
requests.exceptions.Timeout) as e:
|
requests.exceptions.Timeout):
|
||||||
error_traceback = traceback.format_exc()
|
error_traceback = traceback.format_exc()
|
||||||
error = error_traceback.rstrip().split("\n")[-1]
|
error = error_traceback.rstrip().split("\n")[-1]
|
||||||
log.debug("radio: unsuccessful attempts on fetching radio description (shoutcast): " + error)
|
log.debug("radio: unsuccessful attempts on fetching radio description (shoutcast): " + error)
|
||||||
@ -51,7 +51,7 @@ def get_radio_server_description(url):
|
|||||||
except (requests.exceptions.ConnectionError,
|
except (requests.exceptions.ConnectionError,
|
||||||
requests.exceptions.HTTPError,
|
requests.exceptions.HTTPError,
|
||||||
requests.exceptions.ReadTimeout,
|
requests.exceptions.ReadTimeout,
|
||||||
requests.exceptions.Timeout) as e:
|
requests.exceptions.Timeout):
|
||||||
error_traceback = traceback.format_exc()
|
error_traceback = traceback.format_exc()
|
||||||
error = error_traceback.rstrip().split("\n")[-1]
|
error = error_traceback.rstrip().split("\n")[-1]
|
||||||
log.debug("radio: unsuccessful attempts on fetching radio description (icecast): " + error)
|
log.debug("radio: unsuccessful attempts on fetching radio description (icecast): " + error)
|
||||||
@ -81,7 +81,7 @@ def get_radio_title(url):
|
|||||||
requests.exceptions.HTTPError,
|
requests.exceptions.HTTPError,
|
||||||
requests.exceptions.ReadTimeout,
|
requests.exceptions.ReadTimeout,
|
||||||
requests.exceptions.Timeout,
|
requests.exceptions.Timeout,
|
||||||
KeyError) as e:
|
KeyError):
|
||||||
log.debug("radio: unsuccessful attempts on fetching radio title (icy)")
|
log.debug("radio: unsuccessful attempts on fetching radio title (icy)")
|
||||||
return url
|
return url
|
||||||
|
|
||||||
@ -92,12 +92,15 @@ def radio_item_builder(bot, **kwargs):
|
|||||||
else:
|
else:
|
||||||
return RadioItem(bot, kwargs['url'], '')
|
return RadioItem(bot, kwargs['url'], '')
|
||||||
|
|
||||||
|
|
||||||
def radio_item_loader(bot, _dict):
|
def radio_item_loader(bot, _dict):
|
||||||
return RadioItem(bot, "", "", _dict)
|
return RadioItem(bot, "", "", _dict)
|
||||||
|
|
||||||
|
|
||||||
def radio_item_id_generator(**kwargs):
|
def radio_item_id_generator(**kwargs):
|
||||||
return hashlib.md5(kwargs['url'].encode()).hexdigest()
|
return hashlib.md5(kwargs['url'].encode()).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
item_builders['radio'] = radio_item_builder
|
item_builders['radio'] = radio_item_builder
|
||||||
item_loaders['radio'] = radio_item_loader
|
item_loaders['radio'] = radio_item_loader
|
||||||
item_id_generators['radio'] = radio_item_id_generator
|
item_id_generators['radio'] = radio_item_id_generator
|
||||||
@ -109,7 +112,7 @@ class RadioItem(BaseItem):
|
|||||||
super().__init__(bot)
|
super().__init__(bot)
|
||||||
self.url = url
|
self.url = url
|
||||||
if not name:
|
if not name:
|
||||||
self.title = get_radio_server_description(self.url) # The title of the radio station
|
self.title = get_radio_server_description(self.url) # The title of the radio station
|
||||||
else:
|
else:
|
||||||
self.title = name
|
self.title = name
|
||||||
self.id = hashlib.md5(url.encode()).hexdigest()
|
self.id = hashlib.md5(url.encode()).hexdigest()
|
||||||
@ -121,7 +124,7 @@ class RadioItem(BaseItem):
|
|||||||
self.type = "radio"
|
self.type = "radio"
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.version += 1 # 0 -> 1, notify the wrapper to save me when validate() is visited the first time
|
self.version += 1 # 0 -> 1, notify the wrapper to save me when validate() is visited the first time
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def is_ready(self):
|
def is_ready(self):
|
||||||
@ -146,8 +149,8 @@ class RadioItem(BaseItem):
|
|||||||
def format_song_string(self, user):
|
def format_song_string(self, user):
|
||||||
return constants.strings("radio_item",
|
return constants.strings("radio_item",
|
||||||
url=self.url,
|
url=self.url,
|
||||||
title=get_radio_title(self.url), # the title of current song
|
title=get_radio_title(self.url), # the title of current song
|
||||||
name=self.title, # the title of radio station
|
name=self.title, # the title of radio station
|
||||||
user=user
|
user=user
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -159,6 +162,3 @@ class RadioItem(BaseItem):
|
|||||||
|
|
||||||
def display_type(self):
|
def display_type(self):
|
||||||
return constants.strings("radio")
|
return constants.strings("radio")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
21
media/url.py
21
media/url.py
@ -17,15 +17,19 @@ import media.system
|
|||||||
|
|
||||||
log = logging.getLogger("bot")
|
log = logging.getLogger("bot")
|
||||||
|
|
||||||
|
|
||||||
def url_item_builder(bot, **kwargs):
|
def url_item_builder(bot, **kwargs):
|
||||||
return URLItem(bot, kwargs['url'])
|
return URLItem(bot, kwargs['url'])
|
||||||
|
|
||||||
|
|
||||||
def url_item_loader(bot, _dict):
|
def url_item_loader(bot, _dict):
|
||||||
return URLItem(bot, "", _dict)
|
return URLItem(bot, "", _dict)
|
||||||
|
|
||||||
|
|
||||||
def url_item_id_generator(**kwargs):
|
def url_item_id_generator(**kwargs):
|
||||||
return hashlib.md5(kwargs['url'].encode()).hexdigest()
|
return hashlib.md5(kwargs['url'].encode()).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
item_builders['url'] = url_item_builder
|
item_builders['url'] = url_item_builder
|
||||||
item_loaders['url'] = url_item_loader
|
item_loaders['url'] = url_item_loader
|
||||||
item_id_generators['url'] = url_item_id_generator
|
item_id_generators['url'] = url_item_id_generator
|
||||||
@ -97,7 +101,7 @@ class URLItem(BaseItem):
|
|||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
self.ready = "validated"
|
self.ready = "validated"
|
||||||
self.version += 1 # notify wrapper to save me
|
self.version += 1 # notify wrapper to save me
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Run in a other thread
|
# Run in a other thread
|
||||||
@ -145,8 +149,6 @@ class URLItem(BaseItem):
|
|||||||
self.ready = "preparing"
|
self.ready = "preparing"
|
||||||
|
|
||||||
self.log.info("bot: downloading url (%s) %s " % (self.title, self.url))
|
self.log.info("bot: downloading url (%s) %s " % (self.title, self.url))
|
||||||
ydl_opts = ""
|
|
||||||
|
|
||||||
ydl_opts = {
|
ydl_opts = {
|
||||||
'format': 'bestaudio/best',
|
'format': 'bestaudio/best',
|
||||||
'outtmpl': save_path,
|
'outtmpl': save_path,
|
||||||
@ -166,7 +168,7 @@ class URLItem(BaseItem):
|
|||||||
for i in range(attempts):
|
for i in range(attempts):
|
||||||
self.log.info("bot: download attempts %d / %d" % (i+1, attempts))
|
self.log.info("bot: download attempts %d / %d" % (i+1, attempts))
|
||||||
try:
|
try:
|
||||||
info = ydl.extract_info(self.url)
|
ydl.extract_info(self.url)
|
||||||
download_succeed = True
|
download_succeed = True
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
@ -181,7 +183,7 @@ class URLItem(BaseItem):
|
|||||||
"bot: finished downloading url (%s) %s, saved to %s." % (self.title, self.url, self.path))
|
"bot: finished downloading url (%s) %s, saved to %s." % (self.title, self.url, self.path))
|
||||||
self.downloading = False
|
self.downloading = False
|
||||||
self._read_thumbnail_from_file(base_path + ".jpg")
|
self._read_thumbnail_from_file(base_path + ".jpg")
|
||||||
self.version += 1 # notify wrapper to save me
|
self.version += 1 # notify wrapper to save me
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
for f in glob.glob(base_path + "*"):
|
for f in glob.glob(base_path + "*"):
|
||||||
@ -214,7 +216,6 @@ class URLItem(BaseItem):
|
|||||||
|
|
||||||
return dict
|
return dict
|
||||||
|
|
||||||
|
|
||||||
def format_debug_string(self):
|
def format_debug_string(self):
|
||||||
return "[url] {title} ({url})".format(
|
return "[url] {title} ({url})".format(
|
||||||
title=self.title,
|
title=self.title,
|
||||||
@ -224,9 +225,9 @@ class URLItem(BaseItem):
|
|||||||
def format_song_string(self, user):
|
def format_song_string(self, user):
|
||||||
if self.ready in ['validated', 'yes']:
|
if self.ready in ['validated', 'yes']:
|
||||||
return constants.strings("url_item",
|
return constants.strings("url_item",
|
||||||
title=self.title if self.title else "??",
|
title=self.title if self.title else "??",
|
||||||
url=self.url,
|
url=self.url,
|
||||||
user=user)
|
user=user)
|
||||||
return self.url
|
return self.url
|
||||||
|
|
||||||
def format_current_playing(self, user):
|
def format_current_playing(self, user):
|
||||||
@ -235,7 +236,7 @@ class URLItem(BaseItem):
|
|||||||
if self.thumbnail:
|
if self.thumbnail:
|
||||||
thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
|
thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
|
||||||
self.thumbnail + '"/>'
|
self.thumbnail + '"/>'
|
||||||
display += "<br />" + thumbnail_html
|
display += "<br />" + thumbnail_html
|
||||||
|
|
||||||
return display
|
return display
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import youtube_dl
|
import youtube_dl
|
||||||
import constants
|
import constants
|
||||||
import media
|
|
||||||
import variables as var
|
import variables as var
|
||||||
import hashlib
|
|
||||||
from media.item import item_builders, item_loaders, item_id_generators
|
from media.item import item_builders, item_loaders, item_id_generators
|
||||||
from media.url import URLItem, url_item_id_generator
|
from media.url import URLItem, url_item_id_generator
|
||||||
|
|
||||||
|
|
||||||
def get_playlist_info(url, start_index=0, user=""):
|
def get_playlist_info(url, start_index=0, user=""):
|
||||||
items = []
|
items = []
|
||||||
ydl_opts = {
|
ydl_opts = {
|
||||||
@ -63,6 +62,7 @@ def playlist_url_item_builder(bot, **kwargs):
|
|||||||
def playlist_url_item_loader(bot, _dict):
|
def playlist_url_item_loader(bot, _dict):
|
||||||
return PlaylistURLItem(bot, "", "", "", "", _dict)
|
return PlaylistURLItem(bot, "", "", "", "", _dict)
|
||||||
|
|
||||||
|
|
||||||
item_builders['url_from_playlist'] = playlist_url_item_builder
|
item_builders['url_from_playlist'] = playlist_url_item_builder
|
||||||
item_loaders['url_from_playlist'] = playlist_url_item_loader
|
item_loaders['url_from_playlist'] = playlist_url_item_loader
|
||||||
item_id_generators['url_from_playlist'] = url_item_id_generator
|
item_id_generators['url_from_playlist'] = url_item_id_generator
|
||||||
@ -98,11 +98,11 @@ class PlaylistURLItem(URLItem):
|
|||||||
|
|
||||||
def format_song_string(self, user):
|
def format_song_string(self, user):
|
||||||
return constants.strings("url_from_playlist_item",
|
return constants.strings("url_from_playlist_item",
|
||||||
title=self.title,
|
title=self.title,
|
||||||
url=self.url,
|
url=self.url,
|
||||||
playlist_url=self.playlist_url,
|
playlist_url=self.playlist_url,
|
||||||
playlist=self.playlist_title,
|
playlist=self.playlist_title,
|
||||||
user=user)
|
user=user)
|
||||||
|
|
||||||
def format_current_playing(self, user):
|
def format_current_playing(self, user):
|
||||||
display = constants.strings("now_playing", item=self.format_song_string(user))
|
display = constants.strings("now_playing", item=self.format_song_string(user))
|
||||||
@ -110,7 +110,7 @@ class PlaylistURLItem(URLItem):
|
|||||||
if self.thumbnail:
|
if self.thumbnail:
|
||||||
thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
|
thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
|
||||||
self.thumbnail + '"/>'
|
self.thumbnail + '"/>'
|
||||||
display += "<br />" + thumbnail_html
|
display += "<br />" + thumbnail_html
|
||||||
|
|
||||||
return display
|
return display
|
||||||
|
|
||||||
|
41
mumbleBot.py
41
mumbleBot.py
@ -14,8 +14,6 @@ import os
|
|||||||
import os.path
|
import os.path
|
||||||
import pymumble.pymumble_py3 as pymumble
|
import pymumble.pymumble_py3 as pymumble
|
||||||
import variables as var
|
import variables as var
|
||||||
import hashlib
|
|
||||||
import youtube_dl
|
|
||||||
import logging
|
import logging
|
||||||
import logging.handlers
|
import logging.handlers
|
||||||
import traceback
|
import traceback
|
||||||
@ -25,9 +23,6 @@ import util
|
|||||||
import command
|
import command
|
||||||
import constants
|
import constants
|
||||||
from database import SettingsDatabase, MusicDatabase
|
from database import SettingsDatabase, MusicDatabase
|
||||||
import media.url
|
|
||||||
import media.file
|
|
||||||
import media.radio
|
|
||||||
import media.system
|
import media.system
|
||||||
from media.playlist import BasePlaylist
|
from media.playlist import BasePlaylist
|
||||||
from media.cache import MusicCache
|
from media.cache import MusicCache
|
||||||
@ -74,8 +69,8 @@ class MumbleBot:
|
|||||||
self.song_start_at = -1
|
self.song_start_at = -1
|
||||||
self.last_ffmpeg_err = ""
|
self.last_ffmpeg_err = ""
|
||||||
self.read_pcm_size = 0
|
self.read_pcm_size = 0
|
||||||
#self.download_threads = []
|
# self.download_threads = []
|
||||||
self.wait_for_downloading = False # flag for the loop are waiting for download to complete in the other thread
|
self.wait_for_downloading = False # flag for the loop are waiting for download to complete in the other thread
|
||||||
|
|
||||||
if var.config.getboolean("webinterface", "enabled"):
|
if var.config.getboolean("webinterface", "enabled"):
|
||||||
wi_addr = var.config.get("webinterface", "listening_addr")
|
wi_addr = var.config.get("webinterface", "listening_addr")
|
||||||
@ -151,7 +146,7 @@ class MumbleBot:
|
|||||||
self.ducking_volume = var.db.getfloat("bot", "ducking_volume", fallback=self.ducking_volume)
|
self.ducking_volume = var.db.getfloat("bot", "ducking_volume", fallback=self.ducking_volume)
|
||||||
self.ducking_threshold = var.config.getfloat("bot", "ducking_threshold", fallback=5000)
|
self.ducking_threshold = var.config.getfloat("bot", "ducking_threshold", fallback=5000)
|
||||||
self.ducking_threshold = var.db.getfloat("bot", "ducking_threshold", fallback=self.ducking_threshold)
|
self.ducking_threshold = var.db.getfloat("bot", "ducking_threshold", fallback=self.ducking_threshold)
|
||||||
self.mumble.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED, \
|
self.mumble.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED,
|
||||||
self.ducking_sound_received)
|
self.ducking_sound_received)
|
||||||
self.mumble.set_receive_sound(True)
|
self.mumble.set_receive_sound(True)
|
||||||
|
|
||||||
@ -173,7 +168,7 @@ class MumbleBot:
|
|||||||
self.nb_exit += 1
|
self.nb_exit += 1
|
||||||
|
|
||||||
if var.config.getboolean('bot', 'save_playlist', fallback=True) \
|
if var.config.getboolean('bot', 'save_playlist', fallback=True) \
|
||||||
and var.config.get("bot", "save_music_library", fallback=True):
|
and var.config.get("bot", "save_music_library", fallback=True):
|
||||||
self.log.info("bot: save playlist into database")
|
self.log.info("bot: save playlist into database")
|
||||||
var.playlist.save()
|
var.playlist.save()
|
||||||
|
|
||||||
@ -191,9 +186,9 @@ class MumbleBot:
|
|||||||
for command in cmds:
|
for command in cmds:
|
||||||
command = command.strip()
|
command = command.strip()
|
||||||
if command:
|
if command:
|
||||||
self.cmd_handle[command] = { 'handle': handle,
|
self.cmd_handle[command] = {'handle': handle,
|
||||||
'partial_match': not no_partial_match,
|
'partial_match': not no_partial_match,
|
||||||
'access_outside_channel': access_outside_channel}
|
'access_outside_channel': access_outside_channel}
|
||||||
self.log.debug("bot: command added: " + command)
|
self.log.debug("bot: command added: " + command)
|
||||||
|
|
||||||
def set_comment(self):
|
def set_comment(self):
|
||||||
@ -247,7 +242,6 @@ class MumbleBot:
|
|||||||
constants.strings('url_ban'))
|
constants.strings('url_ban'))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
command_exc = ""
|
command_exc = ""
|
||||||
try:
|
try:
|
||||||
if command in self.cmd_handle:
|
if command in self.cmd_handle:
|
||||||
@ -278,7 +272,7 @@ class MumbleBot:
|
|||||||
and not self.is_admin(user) \
|
and not self.is_admin(user) \
|
||||||
and not var.config.getboolean('bot', 'allow_other_channel_message') \
|
and not var.config.getboolean('bot', 'allow_other_channel_message') \
|
||||||
and self.mumble.users[text.actor]['channel_id'] != self.mumble.users.myself[
|
and self.mumble.users[text.actor]['channel_id'] != self.mumble.users.myself[
|
||||||
'channel_id']:
|
'channel_id']:
|
||||||
self.mumble.users[text.actor].send_text_message(
|
self.mumble.users[text.actor].send_text_message(
|
||||||
constants.strings('not_in_my_channel'))
|
constants.strings('not_in_my_channel'))
|
||||||
return
|
return
|
||||||
@ -319,7 +313,7 @@ class MumbleBot:
|
|||||||
def launch_music(self):
|
def launch_music(self):
|
||||||
if var.playlist.is_empty():
|
if var.playlist.is_empty():
|
||||||
return
|
return
|
||||||
assert self.wait_for_downloading == False
|
assert self.wait_for_downloading is False
|
||||||
|
|
||||||
music_wrapper = var.playlist.current_item()
|
music_wrapper = var.playlist.current_item()
|
||||||
uri = music_wrapper.uri()
|
uri = music_wrapper.uri()
|
||||||
@ -341,7 +335,7 @@ class MumbleBot:
|
|||||||
# The ffmpeg process is a thread
|
# The ffmpeg process is a thread
|
||||||
# prepare pipe for catching stderr of ffmpeg
|
# prepare pipe for catching stderr of ffmpeg
|
||||||
pipe_rd, pipe_wd = os.pipe()
|
pipe_rd, pipe_wd = os.pipe()
|
||||||
util.pipe_no_wait(pipe_rd) # Let the pipe work in non-blocking mode
|
util.pipe_no_wait(pipe_rd) # Let the pipe work in non-blocking mode
|
||||||
self.thread_stderr = os.fdopen(pipe_rd)
|
self.thread_stderr = os.fdopen(pipe_rd)
|
||||||
self.thread = sp.Popen(command, stdout=sp.PIPE, stderr=pipe_wd, bufsize=480)
|
self.thread = sp.Popen(command, stdout=sp.PIPE, stderr=pipe_wd, bufsize=480)
|
||||||
self.is_pause = False
|
self.is_pause = False
|
||||||
@ -367,7 +361,6 @@ class MumbleBot:
|
|||||||
var.playlist.remove_by_id(next.id)
|
var.playlist.remove_by_id(next.id)
|
||||||
var.cache.free_and_delete(next.id)
|
var.cache.free_and_delete(next.id)
|
||||||
|
|
||||||
|
|
||||||
# =======================
|
# =======================
|
||||||
# Loop
|
# Loop
|
||||||
# =======================
|
# =======================
|
||||||
@ -490,14 +483,14 @@ class MumbleBot:
|
|||||||
if rms < self.ducking_threshold:
|
if rms < self.ducking_threshold:
|
||||||
print('%6d/%6d ' % (rms, self._max_rms) + '-'*int(rms/200), end='\r')
|
print('%6d/%6d ' % (rms, self._max_rms) + '-'*int(rms/200), end='\r')
|
||||||
else:
|
else:
|
||||||
print('%6d/%6d ' % (rms, self._max_rms) + '-'*int(self.ducking_threshold/200) \
|
print('%6d/%6d ' % (rms, self._max_rms) + '-'*int(self.ducking_threshold/200)
|
||||||
+ '+'*int((rms - self.ducking_threshold)/200), end='\r')
|
+ '+'*int((rms - self.ducking_threshold)/200), end='\r')
|
||||||
|
|
||||||
if rms > self.ducking_threshold:
|
if rms > self.ducking_threshold:
|
||||||
if self.on_ducking is False:
|
if self.on_ducking is False:
|
||||||
self.log.debug("bot: ducking triggered")
|
self.log.debug("bot: ducking triggered")
|
||||||
self.on_ducking = True
|
self.on_ducking = True
|
||||||
self.ducking_release = time.time() + 1 # ducking release after 1s
|
self.ducking_release = time.time() + 1 # ducking release after 1s
|
||||||
|
|
||||||
# =======================
|
# =======================
|
||||||
# Play Control
|
# Play Control
|
||||||
@ -558,7 +551,6 @@ class MumbleBot:
|
|||||||
command = ("ffmpeg", '-v', ffmpeg_debug, '-nostdin', '-ss', "%f" % self.playhead, '-i',
|
command = ("ffmpeg", '-v', ffmpeg_debug, '-nostdin', '-ss', "%f" % self.playhead, '-i',
|
||||||
uri, '-ac', '1', '-f', 's16le', '-ar', '48000', '-')
|
uri, '-ac', '1', '-f', 's16le', '-ar', '48000', '-')
|
||||||
|
|
||||||
|
|
||||||
if var.config.getboolean('bot', 'announce_current_music'):
|
if var.config.getboolean('bot', 'announce_current_music'):
|
||||||
self.send_msg(var.playlist.current_item().format_current_playing())
|
self.send_msg(var.playlist.current_item().format_current_playing())
|
||||||
|
|
||||||
@ -566,13 +558,12 @@ class MumbleBot:
|
|||||||
# The ffmpeg process is a thread
|
# The ffmpeg process is a thread
|
||||||
# prepare pipe for catching stderr of ffmpeg
|
# prepare pipe for catching stderr of ffmpeg
|
||||||
pipe_rd, pipe_wd = os.pipe()
|
pipe_rd, pipe_wd = os.pipe()
|
||||||
util.pipe_no_wait(pipe_rd) # Let the pipe work in non-blocking mode
|
util.pipe_no_wait(pipe_rd) # Let the pipe work in non-blocking mode
|
||||||
self.thread_stderr = os.fdopen(pipe_rd)
|
self.thread_stderr = os.fdopen(pipe_rd)
|
||||||
self.thread = sp.Popen(command, stdout=sp.PIPE, stderr=pipe_wd, bufsize=480)
|
self.thread = sp.Popen(command, stdout=sp.PIPE, stderr=pipe_wd, bufsize=480)
|
||||||
self.last_volume_cycle_time = time.time()
|
self.last_volume_cycle_time = time.time()
|
||||||
self.pause_at_id = ""
|
self.pause_at_id = ""
|
||||||
|
|
||||||
|
|
||||||
# TODO: this is a temporary workaround for issue #44 of pymumble.
|
# TODO: this is a temporary workaround for issue #44 of pymumble.
|
||||||
def _clear_pymumble_soundqueue(self):
|
def _clear_pymumble_soundqueue(self):
|
||||||
for id, user in self.mumble.users.items():
|
for id, user in self.mumble.users.items():
|
||||||
@ -582,7 +573,6 @@ class MumbleBot:
|
|||||||
self.log.debug("bot: pymumble soundqueue cleared.")
|
self.log.debug("bot: pymumble soundqueue cleared.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def start_web_interface(addr, port):
|
def start_web_interface(addr, port):
|
||||||
global formatter
|
global formatter
|
||||||
import interface
|
import interface
|
||||||
@ -590,9 +580,8 @@ def start_web_interface(addr, port):
|
|||||||
# setup logger
|
# setup logger
|
||||||
werkzeug_logger = logging.getLogger('werkzeug')
|
werkzeug_logger = logging.getLogger('werkzeug')
|
||||||
logfile = util.solve_filepath(var.config.get('webinterface', 'web_logfile'))
|
logfile = util.solve_filepath(var.config.get('webinterface', 'web_logfile'))
|
||||||
handler = None
|
|
||||||
if logfile:
|
if logfile:
|
||||||
handler = logging.handlers.RotatingFileHandler(logfile, mode='a', maxBytes=10240) # Rotate after 10KB
|
handler = logging.handlers.RotatingFileHandler(logfile, mode='a', maxBytes=10240) # Rotate after 10KB
|
||||||
else:
|
else:
|
||||||
handler = logging.StreamHandler()
|
handler = logging.StreamHandler()
|
||||||
|
|
||||||
@ -657,7 +646,7 @@ if __name__ == '__main__':
|
|||||||
logfile = util.solve_filepath(var.config.get('bot', 'logfile'))
|
logfile = util.solve_filepath(var.config.get('bot', 'logfile'))
|
||||||
handler = None
|
handler = None
|
||||||
if logfile:
|
if logfile:
|
||||||
handler = logging.handlers.RotatingFileHandler(logfile, mode='a', maxBytes=10240) # Rotate after 10KB
|
handler = logging.handlers.RotatingFileHandler(logfile, mode='a', maxBytes=10240) # Rotate after 10KB
|
||||||
else:
|
else:
|
||||||
handler = logging.StreamHandler()
|
handler = logging.StreamHandler()
|
||||||
|
|
||||||
|
19
util.py
19
util.py
@ -6,23 +6,16 @@ import magic
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import variables as var
|
import variables as var
|
||||||
import constants
|
|
||||||
import zipfile
|
import zipfile
|
||||||
import requests
|
import requests
|
||||||
import mutagen
|
|
||||||
import re
|
import re
|
||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
import logging
|
import logging
|
||||||
import youtube_dl
|
import youtube_dl
|
||||||
from importlib import reload
|
from importlib import reload
|
||||||
from PIL import Image
|
|
||||||
from io import BytesIO
|
|
||||||
from sys import platform
|
from sys import platform
|
||||||
import traceback
|
import traceback
|
||||||
import urllib.parse, urllib.request, urllib.error
|
import urllib.request
|
||||||
import base64
|
|
||||||
import media
|
|
||||||
import media.radio
|
|
||||||
from packaging import version
|
from packaging import version
|
||||||
|
|
||||||
log = logging.getLogger("bot")
|
log = logging.getLogger("bot")
|
||||||
@ -65,6 +58,7 @@ def get_recursive_file_list_sorted(path):
|
|||||||
filelist.sort()
|
filelist.sort()
|
||||||
return filelist
|
return filelist
|
||||||
|
|
||||||
|
|
||||||
# - zips all files of the given zippath (must be a directory)
|
# - zips all files of the given zippath (must be a directory)
|
||||||
# - returns the absolute path of the created zip file
|
# - returns the absolute path of the created zip file
|
||||||
# - zip file will be in the applications tmp folder (according to configuration)
|
# - zip file will be in the applications tmp folder (according to configuration)
|
||||||
@ -172,7 +166,7 @@ def url_unban(url):
|
|||||||
|
|
||||||
|
|
||||||
def pipe_no_wait(pipefd):
|
def pipe_no_wait(pipefd):
|
||||||
''' Used to fetch the STDERR of ffmpeg. pipefd is the file descriptor returned from os.pipe()'''
|
""" Used to fetch the STDERR of ffmpeg. pipefd is the file descriptor returned from os.pipe()"""
|
||||||
if platform == "linux" or platform == "linux2" or platform == "darwin":
|
if platform == "linux" or platform == "linux2" or platform == "darwin":
|
||||||
import fcntl
|
import fcntl
|
||||||
import os
|
import os
|
||||||
@ -309,18 +303,19 @@ def get_url_from_input(string):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def youtube_search(query):
|
def youtube_search(query):
|
||||||
global log
|
global log
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = requests.get("https://www.youtube.com/results", params={'search_query': query}, timeout=5)
|
r = requests.get("https://www.youtube.com/results", params={'search_query': query}, timeout=5)
|
||||||
results = re.findall("watch\?v=(.*?)\".*?title=\"(.*?)\".*?"
|
results = re.findall(r"watch\?v=(.*?)\".*?title=\"(.*?)\".*?"
|
||||||
"(?:user|channel).*?>(.*?)<", r.text) # (id, title, uploader)
|
"(?:user|channel).*?>(.*?)<", r.text) # (id, title, uploader)
|
||||||
|
|
||||||
if len(results) > 0:
|
if len(results) > 0:
|
||||||
return results
|
return results
|
||||||
|
|
||||||
except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError, requests.exceptions.Timeout) as e:
|
except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError, requests.exceptions.Timeout):
|
||||||
error_traceback = traceback.format_exc().split("During")[0]
|
error_traceback = traceback.format_exc().split("During")[0]
|
||||||
log.error("util: youtube query failed with error:\n %s" % error_traceback)
|
log.error("util: youtube query failed with error:\n %s" % error_traceback)
|
||||||
return False
|
return False
|
||||||
|
Loading…
x
Reference in New Issue
Block a user