diff --git a/command.py b/command.py
index 704f908..f868eb6 100644
--- a/command.py
+++ b/command.py
@@ -14,7 +14,7 @@ import util
import variables as var
from pyradios import RadioBrowser
from database import SettingsDatabase, MusicDatabase, Condition
-from media.item import item_id_generators, dict_to_item, dicts_to_items
+from media.item import item_id_generators, dict_to_item, dicts_to_items, ValidationFailedError
from media.cache import get_cached_wrapper_from_scrap, get_cached_wrapper_by_id, get_cached_wrappers_by_tags, \
get_cached_wrapper, get_cached_wrappers, get_cached_wrapper_from_dict, get_cached_wrappers_from_dicts
from media.url_from_playlist import get_playlist_info
@@ -74,6 +74,9 @@ def register_all_commands(bot):
bot.register_command(commands('url_ban'), cmd_url_ban, no_partial_match=True, admin=True)
bot.register_command(commands('url_ban_list'), cmd_url_ban_list, no_partial_match=True, admin=True)
bot.register_command(commands('url_unban'), cmd_url_unban, no_partial_match=True, admin=True)
+ bot.register_command(commands('url_unwhitelist'), cmd_url_unwhitelist, no_partial_match=True, admin=True)
+ bot.register_command(commands('url_whitelist'), cmd_url_whitelist, no_partial_match=True, admin=True)
+ bot.register_command(commands('url_whitelist_list'), cmd_url_whitelist_list, no_partial_match=True, admin=True)
bot.register_command(commands('user_ban'), cmd_user_ban, no_partial_match=True, admin=True)
bot.register_command(commands('user_unban'), cmd_user_unban, no_partial_match=True, admin=True)
@@ -149,46 +152,102 @@ def cmd_user_ban(bot, user, text, command, parameter):
global log
if parameter:
- bot.mumble.users[text.actor].send_text_message(util.user_ban(parameter))
+ var.db.set("user_ban", parameter, None)
+ bot.send_msg(tr("user_ban_success", user=parameter), text)
else:
- bot.mumble.users[text.actor].send_text_message(util.get_user_ban())
+ ban_list = "
"
+ for i in var.db.items("url_ban"):
+ ban_list += "- " + i[0] + "
"
+ ban_list += "
"
+ bot.send_msg(tr("user_ban_list", list=ban_list), text)
def cmd_user_unban(bot, user, text, command, parameter):
global log
- if parameter:
- bot.mumble.users[text.actor].send_text_message(util.user_unban(parameter))
+ if parameter and var.db.has_option("user_ban", parameter):
+ var.db.remove_option("user_ban", parameter)
+ bot.send_msg(tr("user_unban_success", user=parameter), text)
def cmd_url_ban(bot, user, text, command, parameter):
global log
- if parameter:
- bot.mumble.users[text.actor].send_text_message(util.url_ban(util.get_url_from_input(parameter)))
-
- id = item_id_generators['url'](url=parameter)
- var.cache.free_and_delete(id)
- var.playlist.remove_by_id(id)
+ url = util.get_url_from_input(parameter)
+ if url:
+ _id = item_id_generators['url'](url=url)
+ var.cache.free_and_delete(_id)
+ var.playlist.remove_by_id(_id)
else:
if var.playlist.current_item() and var.playlist.current_item().type == 'url':
item = var.playlist.current_item().item()
- bot.mumble.users[text.actor].send_text_message(util.url_ban(util.get_url_from_input(item.url)))
+ url = item.url
var.cache.free_and_delete(item.id)
var.playlist.remove_by_id(item.id)
else:
bot.send_msg(tr('bad_parameter', command=command), text)
+ return
+
+ # Remove from the whitelist first
+ if var.db.has_option('url_whitelist', url):
+ var.db.remove_option("url_whitelist", url)
+ bot.send_msg(tr("url_unwhitelist_success", url=url), text)
+
+ if not var.db.has_option('url_ban', url):
+ var.db.set("url_ban", url, None)
+ bot.send_msg(tr("url_ban_success", url=url), text)
def cmd_url_ban_list(bot, user, text, command, parameter):
- bot.mumble.users[text.actor].send_text_message(util.get_url_ban())
+ ban_list = ""
+ for i in var.db.items("url_ban"):
+ ban_list += "- " + i[0] + "
"
+ ban_list += "
"
+
+ bot.send_msg(tr("url_ban_list", list=ban_list), text)
def cmd_url_unban(bot, user, text, command, parameter):
- global log
+ url = util.get_url_from_input(parameter)
+ if url:
+ var.db.remove_option("url_ban", url)
+ bot.send_msg(tr("url_unban_success", url=url), text)
+ else:
+ bot.send_msg(tr('bad_parameter', command=command), text)
- if parameter:
- bot.mumble.users[text.actor].send_text_message(util.url_unban(util.get_url_from_input(parameter)))
+
+def cmd_url_whitelist(bot, user, text, command, parameter):
+ url = util.get_url_from_input(parameter)
+ if url:
+ # Unban first
+ if var.db.has_option('url_ban', url):
+ var.db.remove_option("url_ban", url)
+ bot.send_msg(tr("url_unban_success"), text)
+
+ # Then add to whitelist
+ if not var.db.has_option('url_whitelist', url):
+ var.db.set("url_whitelist", url, None)
+ bot.send_msg(tr("url_whitelist_success", url=url), text)
+ else:
+ bot.send_msg(tr('bad_parameter', command=command), text)
+
+
+def cmd_url_whitelist_list(bot, user, text, command, parameter):
+ ban_list = ""
+ for i in var.db.items("url_whitelist"):
+ ban_list += "- " + i[0] + "
"
+ ban_list += "
"
+
+ bot.send_msg(tr("url_whitelist_list", list=ban_list), text)
+
+
+def cmd_url_unwhitelist(bot, user, text, command, parameter):
+ url = util.get_url_from_input(parameter)
+ if url:
+ var.db.remove_option("url_whitelist", url)
+ bot.send_msg(tr("url_unwhitelist_success"), text)
+ else:
+ bot.send_msg(tr('bad_parameter', command=command), text)
def cmd_play(bot, user, text, command, parameter):
@@ -338,6 +397,7 @@ def cmd_play_url(bot, user, text, command, parameter):
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
send_item_added_message(bot, music_wrapper, len(var.playlist) - 1, text)
+
if len(var.playlist) == 2:
# If I am the second item on the playlist. (I am the next one!)
bot.async_download_next()
diff --git a/configuration.default.ini b/configuration.default.ini
index 4ead2bf..35e818d 100644
--- a/configuration.default.ini
+++ b/configuration.default.ini
@@ -183,6 +183,9 @@ user_unban = userunban
url_ban = urlban
url_ban_list = urlbanlist
url_unban = urlunban
+url_whitelist = urlwhitelist, urlw
+url_unwhitelist = urlunwhitelist, urlunw
+url_whitelist_list = urlwhitelistlist, urlwls
ducking = duck
ducking_threshold = duckthres
diff --git a/lang/en_US.json b/lang/en_US.json
index 10770ff..cc69062 100644
--- a/lang/en_US.json
+++ b/lang/en_US.json
@@ -61,17 +61,26 @@
"shortlist_instruction": "Use !sl {indexes} to play the item you want.",
"start_updating": "Start updating...",
"stopped": "Music stopped.",
- "too_long": "{song} is too long, removed from playlist!",
+ "too_long": "{song} is too long ({duration} > {max_duration}), removed from playlist!",
"unable_download": "Unable to download {item}. Removed from the library.",
"unable_play": "Unable to play {item}. Removed from the library.",
"unknown_mode": "Unknown playback mode '{mode}'. It should be one of one-shot, repeat, random.",
"update_successful": "botamusique v{version} Installed!
\nChangelog
{changelog}
Visit our github repo for more details!",
"url": "URL",
- "url_ban": "This URL is banned!",
+ "url_ban": "The URL {url} is banned! Removed from playlist!",
+ "url_ban_success": "The following URL is banned: {url}.",
+ "url_ban_list": "List of banned URL:
{list}",
"url_from_playlist": "URL",
"url_from_playlist_item": "{title} from playlist {playlist} added by {user}",
"url_item": "{title} added by {user}",
+ "url_unban_success": "The following URL is unbanned: {url}.",
+ "url_unwhitelist_success": "The following URL is un-whitelisted: {url}.",
+ "url_whitelist_success": "The following URL is whitelisted: {url}.",
+ "url_whitelist_list": "List of whitelisted URL:
{list}",
"user_ban": "You are banned, not allowed to do that!",
+ "user_ban_success": "User {user} is banned.",
+ "user_ban_list": "List of banned user:
{list}",
+ "user_unban_success": "User {user} is unbanned.",
"user_password_set": "Your password has been updated.",
"web_user_list": "Following users have the privilege to access the web interface:
{users}",
"webpage_address": "Your own address to access the web interface is {address}",
diff --git a/lang/es_ES.json b/lang/es_ES.json
index 91127f2..d5028ca 100644
--- a/lang/es_ES.json
+++ b/lang/es_ES.json
@@ -61,13 +61,13 @@
"shortlist_instruction": "Use !sl {índices} para reproducir los elementos que usted desea.",
"start_updating": "Empezando la actualización...",
"stopped": "Música fue detenida.",
- "too_long": "{song} es muy larga. Eliminada de la lista de reproducción!",
+ "too_long": "{song} es muy larga ({duration} > {max_duration}). Eliminada de la lista de reproducción!",
"unable_download": "No fue posible descargar {item}. Eliminado de la biblioteca.",
"unable_play": "No fue posible reproducir {item}. Eliminado de la biblioteca.",
"unknown_mode": "Modo de reproducción '{mode}' desconocido. Debiera ser o bien one-shot, repeat o random.",
"update_successful": "botamusique v{version} instalado!
\nLista de cambios
{changelog}
Visite nuestro repositorio en Github para más detalles!",
"url": "URL",
- "url_ban": "Esta URL está baneada!",
+ "url_ban": "URL {url} está baneada! Eliminada de la lista de reproducción!",
"url_from_playlist": "URL",
"url_from_playlist_item": "{title} de lista de reproducción {playlist} añadido por {user}",
"url_item": "{title} añadido por {user}",
diff --git a/lang/fr_FR.json b/lang/fr_FR.json
index 15286b1..5c58230 100644
--- a/lang/fr_FR.json
+++ b/lang/fr_FR.json
@@ -61,13 +61,13 @@
"shortlist_instruction": "Utilisez !sl {indexes} pour jouer l'élément que vous voulez.",
"start_updating": "Début de la mise à jour...",
"stopped": "Musique arrêté.",
- "too_long": "{song} est trop long, supprimé de la playlist !",
+ "too_long": "{song} est trop long ({duration} > {max_duration}), supprimé de la playlist !",
"unable_download": "Impossible de télécharger {item}. Retiré de la bibliothèque.",
"unable_play": "Impossible de jouer {item}. Retiré de la bibliothèque.",
"unknown_mode": "Mode de lecture \"{mode}\" inconnu. Il devrait s'agir d'un des modes suivants : one-shot, repeat, random.",
"update_successful": "botamusique v{version} Installé !
\nChangelog
{changelog}
Visitez notre repo github pour plus de détails !",
"url": "URL",
- "url_ban": "Cette URL est interdite !",
+ "url_ban": "URL {url} est interdite !",
"url_from_playlist": "URL",
"url_from_playlist_item": "{title} depuis la playlist {playlist} ajouté par {user}",
"url_item": "{title} ajouté par {user}",
diff --git a/lang/it_IT.json b/lang/it_IT.json
index c724b11..f94a26f 100644
--- a/lang/it_IT.json
+++ b/lang/it_IT.json
@@ -61,13 +61,13 @@
"shortlist_instruction": "Usa !sl {indexes} per riprodurre l'elemento desiderato.",
"start_updating": "Inizio aggiornamento...",
"stopped": "Riproduzione interrotta.",
- "too_long": "{song} è troppo lunga, rimossa dalla playlist!",
+ "too_long": "{song} è troppo lunga ({duration} > {max_duration}), rimossa dalla playlist!",
"unable_download": "Impossibile scaricare {item}. Rimosso dalla libreria.",
"unable_play": "Impossibile riprodurre {item}. Rimosso dalla libreria.",
"unknown_mode": "Modalità di riproduzione '{mode}' sconosciuta. Dovrebbe essere one-shot, ripeti, casuale.",
"update_successful": "botamusique v{version} installato!
\nChangelog
{changelog}
Visita la nostra repository GitHub per ulteriori dettagli!",
"url": "URL",
- "url_ban": "Questo URL è vietato!",
+ "url_ban": "URL {url} è vietato!",
"url_from_playlist": "URL",
"url_from_playlist_item": "{title} dalla playlist {playlist} aggiunto da {user}",
"url_item": "{title} aggiunto da {user}",
diff --git a/lang/ja_JP.json b/lang/ja_JP.json
index 940afae..34b35e1 100644
--- a/lang/ja_JP.json
+++ b/lang/ja_JP.json
@@ -53,21 +53,21 @@
"rb_play_empty": "ラジオIDを提供してください。",
"rb_query_result": "検索の結果( !rbplay {ID} を送信して再生する)",
"records_omitted": "…",
- "removed_tags": "もう{song}からタグ「 {tags}」を削除しました。",
- "removed_tags_from_all": "もう再生リストの全ての曲にタグ「{tags} 」を削除しました。",
- "removing_item": "もう再生リストに「{item}」を削除しました。",
+ "removed_tags": "{song}からタグ「 {tags}」を削除しました。",
+ "removed_tags_from_all": "再生リストの全ての曲にタグ「{tags} 」を削除しました。",
+ "removing_item": "再生リストに「{item}」を削除しました。",
"repeat": "「{song}」を{n}回リピートするになります。",
"report_version": "現在のbotamusiqueバージョンは{version}です。",
"shortlist_instruction": "!sl {indexes}を使ってこのリストの曲を再生する。",
"start_updating": "更新しています…",
"stopped": "再生停止。",
- "too_long": "「{song}」が長さ制限を超えました。削除されました。",
+ "too_long": "「{song}」が長さ制限を超えました({duration} > {max_duration})。削除されました。",
"unable_download": "「{item}」がダウンロードできません。削除されました。",
"unable_play": "「{item}」が再生できません。削除されました。",
"unknown_mode": "不正な再生モード「{mode}」。 one-shot, repeat, random, autoplayの中の一つを使ってください。",
"update_successful": "botamusique v{version} インストール完成!
\n更新履歴
{changelog}
このプロジェクトの githubページ をご覧ください!",
"url": "URL",
- "url_ban": "このURLが禁止されています。",
+ "url_ban": "URL {url} が禁止されています。",
"url_from_playlist": "URL",
"url_from_playlist_item": "{title}、({playlist}から)、 {user} に追加されました。",
"url_item": "{title}, {user} に追加されました。",
diff --git a/lang/zh_CN.json b/lang/zh_CN.json
index 6eda882..0242805 100644
--- a/lang/zh_CN.json
+++ b/lang/zh_CN.json
@@ -61,13 +61,13 @@
"shortlist_instruction": "使用!sl {indexes}播放列表中的曲目。",
"start_updating": "开始更新……",
"stopped": "音乐停止。",
- "too_long": "{song}超出长度限制!已被移出播放列表。",
+ "too_long": "{song}超出长度限制({duration} > {max_duration})!已被移出播放列表。",
"unable_download": "无法下载{item}。已移出播放列表。",
"unable_play": "无法播放{item}。已移出播放列表。",
"unknown_mode": "未知播放模式\"{mode}\"。播放模式应为 one-shot, repeat, random中的一个。",
"update_successful": "botamusique v{version} 安装完毕!
\n更新日志
{changelog}
请访问我们的 github页面 获取更多信息!",
"url": "URL",
- "url_ban": "该链接被列入黑名单了!",
+ "url_ban": "链接{url}被列入黑名单了!",
"url_from_playlist": "URL",
"url_from_playlist_item": "{title},来自播放列表 {playlist},由 {user} 添加。",
"url_item": "{title},由 {user} 添加。",
diff --git a/media/url.py b/media/url.py
index 8276b87..3d31375 100644
--- a/media/url.py
+++ b/media/url.py
@@ -15,6 +15,7 @@ import variables as var
from media.item import BaseItem, item_builders, item_loaders, item_id_generators, ValidationFailedError, \
PreparationFailedError
import media.system
+from util import format_time
log = logging.getLogger("bot")
@@ -74,36 +75,45 @@ class URLItem(BaseItem):
return True
def validate(self):
- self.validating_lock.acquire()
- if self.ready in ['yes', 'validated']:
+ try:
+ self.validating_lock.acquire()
+ if self.ready in ['yes', 'validated']:
+ return True
+
+ # if self.ready == 'failed':
+ # self.validating_lock.release()
+ # return False
+ #
+ if os.path.exists(self.path):
+ self.ready = "yes"
+ return True
+
+ # Check if this url is banned
+ if var.db.has_option('url_ban', self.url):
+ raise ValidationFailedError(tr('url_ban', url=self.url))
+
+ # avoid multiple process validating in the meantime
+ info = self._get_info_from_url()
+
+ if not info:
+ return False
+
+ # Check if the song is too long and is not whitelisted
+ max_duration = var.config.getint('bot', 'max_track_duration') * 60
+ if max_duration and \
+ not var.db.has_option('url_whitelist', self.url) and \
+ self.duration > max_duration:
+ log.info(
+ "url: " + self.url + " has a duration of " + str(self.duration / 60) + " min -- too long")
+ raise ValidationFailedError(tr('too_long', song=self.format_title(),
+ duration=format_time(self.duration),
+ max_duration=format_time(max_duration)))
+ else:
+ self.ready = "validated"
+ self.version += 1 # notify wrapper to save me
+ return True
+ finally:
self.validating_lock.release()
- return True
-
- # if self.ready == 'failed':
- # self.validating_lock.release()
- # return False
- #
- if os.path.exists(self.path):
- self.validating_lock.release()
- self.ready = "yes"
- return True
-
- # avoid multiple process validating in the meantime
- info = self._get_info_from_url()
- self.validating_lock.release()
-
- if not info:
- return False
-
- if self.duration > var.config.getint('bot', 'max_track_duration') * 60 != 0:
- # Check the length, useful in case of playlist, it wasn't checked before)
- log.info(
- "url: " + self.url + " has a duration of " + str(self.duration / 60) + " min -- too long")
- raise ValidationFailedError(tr('too_long', song=self.title))
- else:
- self.ready = "validated"
- self.version += 1 # notify wrapper to save me
- return True
# Run in a other thread
def prepare(self):
@@ -126,8 +136,8 @@ class URLItem(BaseItem):
try:
info = ydl.extract_info(self.url, download=False)
self.duration = info['duration']
- self.title = info['title']
- self.keywords = info['title']
+ self.title = info['title'].strip()
+ self.keywords = self.title
succeed = True
return True
except youtube_dl.utils.DownloadError:
@@ -237,7 +247,7 @@ class URLItem(BaseItem):
return display
def format_title(self):
- return self.title if self.title.strip() else self.url
+ return self.title if self.title else self.url
def display_type(self):
return tr("url")
diff --git a/mumbleBot.py b/mumbleBot.py
index 099b580..6072dad 100644
--- a/mumbleBot.py
+++ b/mumbleBot.py
@@ -276,12 +276,10 @@ class MumbleBot:
if not self.is_admin(user) and parameter:
input_url = util.get_url_from_input(parameter)
- if input_url:
- for i in var.db.items("url_ban"):
- if input_url == i[0]:
- self.mumble.users[text.actor].send_text_message(
- tr('url_ban'))
- return
+ if input_url and var.db.has_option('url_ban', input_url):
+ self.mumble.users[text.actor].send_text_message(
+ tr('url_ban'))
+ return
command_exc = ""
try:
@@ -408,13 +406,12 @@ class MumbleBot:
# Function start if the next music isn't ready
# Do nothing in case the next music is already downloaded
self.log.debug("bot: Async download next asked ")
- while var.playlist.next_item() and var.playlist.next_item().type in ['url', 'url_from_playlist']:
+ while var.playlist.next_item():
# usually, all validation will be done when adding to the list.
# however, for performance consideration, youtube playlist won't be validate when added.
# the validation has to be done here.
next = var.playlist.next_item()
try:
- next.validate()
if not next.is_ready():
self.async_download(next)
@@ -432,8 +429,7 @@ class MumbleBot:
th.start()
return th
- def validate_and_start_download(self, item):
- item.validate()
+ def start_download(self, item):
if not item.is_ready():
self.log.info("bot: current music isn't ready, start downloading.")
self.async_download(item)
@@ -442,15 +438,25 @@ class MumbleBot:
def _download(self, item):
ver = item.version
+ try:
+ item.validate()
+ if item.is_ready():
+ return True
+ except ValidationFailedError as e:
+ self.send_channel_msg(e.msg)
+ var.playlist.remove_by_id(item.id)
+ var.cache.free_and_delete(item.id)
+ return False
+
try:
item.prepare()
+ if item.version > ver:
+ var.playlist.version += 1
+ return True
except PreparationFailedError as e:
self.send_channel_msg(e.msg)
return False
- if item.version > ver:
- var.playlist.version += 1
-
# =======================
# Loop
# =======================
@@ -528,7 +534,7 @@ class MumbleBot:
current = var.playlist.current_item()
self.log.debug(f"bot: next into the song: {current.format_debug_string()}")
try:
- self.validate_and_start_download(current)
+ self.start_download(current)
self.wait_for_ready = True
self.song_start_at = -1
@@ -640,7 +646,7 @@ class MumbleBot:
current = var.playlist.current_item()
- self.validate_and_start_download(current)
+ self.start_download(current)
self.is_pause = False
self.wait_for_ready = True
self.song_start_at = -1
diff --git a/util.py b/util.py
index 6415775..e6dffac 100644
--- a/util.py
+++ b/util.py
@@ -159,37 +159,6 @@ def update(current_version):
return msg
-def user_ban(user):
- var.db.set("user_ban", user, None)
- res = "User " + user + " banned"
- return res
-
-
-def user_unban(user):
- var.db.remove_option("user_ban", user)
- res = "Done"
- return res
-
-
-def get_url_ban():
- res = "List of ban:"
- for i in var.db.items("url_ban"):
- res += "
" + i[0]
- return res
-
-
-def url_ban(url):
- var.db.set("url_ban", url, None)
- res = "url " + url + " banned"
- return res
-
-
-def url_unban(url):
- var.db.remove_option("url_ban", url)
- res = "Done"
- return res
-
-
def pipe_no_wait():
""" Generate a non-block pipe used to fetch the STDERR of ffmpeg.
"""
@@ -337,14 +306,14 @@ def get_url_from_input(string):
if res:
string = res.group(1)
else:
- return False
+ return ""
match = re.search("(http|https)://(\S*)?/(\S*)", string, flags=re.IGNORECASE)
if match:
url = match[1].lower() + "://" + match[2].lower() + "/" + match[3]
return url
else:
- return False
+ return ""
def youtube_search(query):
@@ -412,6 +381,14 @@ def parse_time(human):
raise ValueError("Invalid time string given.")
+def format_time(seconds):
+ hours = seconds // 3600
+ seconds = seconds % 3600
+ minutes = seconds // 60
+ seconds = seconds % 60
+ return f"{hours:d}:{minutes:02d}:{seconds:02d}"
+
+
def parse_file_size(human):
units = {"B": 1, "KB": 1024, "MB": 1024 * 1024, "GB": 1024 * 1024 * 1024, "TB": 1024 * 1024 * 1024 * 1024,
"K": 1024, "M": 1024 * 1024, "G": 1024 * 1024 * 1024, "T": 1024 * 1024 * 1024 * 1024}