feat: 'search' command to search the db, and 'shortlist' to add songs from search result

This commit is contained in:
Terry Geng 2020-03-08 16:56:31 +08:00
parent 45b83af4ba
commit f7042db657
10 changed files with 226 additions and 73 deletions

View File

@ -10,6 +10,7 @@ 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.playlist import get_item_wrapper, get_item_wrapper_by_id, get_item_wrappers_by_tags from media.playlist import get_item_wrapper, get_item_wrapper_by_id, get_item_wrappers_by_tags
from media.file import FileItem from media.file import FileItem
from media.url_from_playlist import PlaylistURLItem, get_playlist_info from media.url_from_playlist import PlaylistURLItem, get_playlist_info
@ -58,6 +59,8 @@ def register_all_commands(bot):
bot.register_command(constants.commands('add_tag'), cmd_add_tag) bot.register_command(constants.commands('add_tag'), cmd_add_tag)
bot.register_command(constants.commands('remove_tag'), cmd_remove_tag) bot.register_command(constants.commands('remove_tag'), cmd_remove_tag)
bot.register_command(constants.commands('find_tagged'), cmd_find_tagged) bot.register_command(constants.commands('find_tagged'), cmd_find_tagged)
bot.register_command(constants.commands('search'), cmd_search_library)
bot.register_command(constants.commands('add_from_shortlist'), cmd_shortlist)
bot.register_command(constants.commands('drop_database'), cmd_drop_database, True) bot.register_command(constants.commands('drop_database'), cmd_drop_database, True)
bot.register_command(constants.commands('rescan'), cmd_refresh_cache, True) bot.register_command(constants.commands('rescan'), cmd_refresh_cache, True)
@ -81,6 +84,10 @@ def send_multi_lines(bot, lines, text, linebreak="<br />"):
bot.send_msg(msg, text) bot.send_msg(msg, text)
# ---------------- Variables -----------------
song_shortlist = []
# ---------------- Commands ------------------ # ---------------- Commands ------------------
@ -169,13 +176,13 @@ def cmd_pause(bot, user, text, command, parameter):
def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=False): def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=False):
global log global log, song_shortlist
# if parameter is {index} # if parameter is {index}
if parameter.isdigit(): if parameter.isdigit():
files = var.library.files files = var.cache.files
if int(parameter) < len(files): if int(parameter) < len(files):
music_wrapper = get_item_wrapper_by_id(bot, var.library.file_id_lookup[files[int(parameter)]], user) music_wrapper = get_item_wrapper_by_id(bot, var.cache.file_id_lookup[files[int(parameter)]], user)
var.playlist.append(music_wrapper) var.playlist.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())
bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text) bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
@ -189,7 +196,7 @@ def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=Fals
# bot.send_msg(constants.strings('no_file'), text) # bot.send_msg(constants.strings('no_file'), text)
# return # return
if parameter in var.library.files: if parameter in var.cache.files:
music_wrapper = get_item_wrapper(bot, type='file', path=parameter, user=user) music_wrapper = get_item_wrapper(bot, type='file', path=parameter, user=user)
var.playlist.append(music_wrapper) var.playlist.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())
@ -197,14 +204,14 @@ def cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=Fals
return return
# if parameter is {folder} # if parameter is {folder}
files = var.library.dir.get_files(parameter) files = var.cache.dir.get_files(parameter)
if files: if files:
msgs = [constants.strings('multiple_file_added')] msgs = [constants.strings('multiple_file_added')]
count = 0 count = 0
for file in files: for file in files:
count += 1 count += 1
music_wrapper = get_item_wrapper_by_id(bot, var.library.file_id_lookup[file],user) music_wrapper = get_item_wrapper_by_id(bot, var.cache.file_id_lookup[file], user)
var.playlist.append(music_wrapper) var.playlist.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("{} ({})".format(music_wrapper.item().title, music_wrapper.item().path)) msgs.append("{} ({})".format(music_wrapper.item().title, music_wrapper.item().path))
@ -215,28 +222,34 @@ 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.library.files files = var.cache.files
matches = [(index, file) for index, file in enumerate(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][1] file = matches[0]
music_wrapper = get_item_wrapper_by_id(bot, var.library.file_id_lookup[file],user) music_wrapper = get_item_wrapper_by_id(bot, var.cache.file_id_lookup[file], user)
var.playlist.append(music_wrapper) var.playlist.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())
bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text) bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
return return
elif len(matches) > 1: elif len(matches) > 1:
msgs = [ constants.strings('multiple_matches') ] msgs = [ constants.strings('multiple_matches') ]
for match in matches: song_shortlist = []
music_wrapper = get_item_wrapper_by_id(bot, var.library.file_id_lookup[match[1]], user) for index, match in enumerate(matches):
msgs.append("<b>{:0>3d}</b> - <b>{:s}</b> ({:s})".format( id = var.cache.file_id_lookup[match]
match[0], music_wrapper.item().title, match[1])) music_dict = var.music_db.query_music_by_id(id)
item = dict_to_item(bot, music_dict)
song_shortlist.append(music_dict)
msgs.append("<b>{:d}</b> - <b>{:s}</b> ({:s})".format(
index + 1, item.title, match))
send_multi_lines(bot, msgs, text) send_multi_lines(bot, msgs, text)
return return
if do_not_refresh_cache: if do_not_refresh_cache:
bot.send_msg(constants.strings("no_file"), text) bot.send_msg(constants.strings("no_file"), text)
else: else:
var.library.build_dir_cache(bot) var.cache.build_dir_cache(bot)
cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=True) cmd_play_file(bot, user, text, command, parameter, do_not_refresh_cache=True)
@ -245,7 +258,7 @@ def cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cach
music_folder = var.music_folder music_folder = var.music_folder
if parameter: if parameter:
files = var.library.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:
@ -254,7 +267,7 @@ def cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cach
match = re.search(parameter, file) match = re.search(parameter, file)
if match and match[0]: if match and match[0]:
count += 1 count += 1
music_wrapper = get_item_wrapper_by_id(bot, var.library.file_id_lookup[file], user) music_wrapper = get_item_wrapper_by_id(bot, var.cache.file_id_lookup[file], user)
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,
@ -273,7 +286,7 @@ def cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cach
if do_not_refresh_cache: if do_not_refresh_cache:
bot.send_msg(constants.strings("no_file"), text) bot.send_msg(constants.strings("no_file"), text)
else: else:
var.library.build_dir_cache(bot) var.cache.build_dir_cache(bot)
cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cache=True) cmd_play_file_match(bot, user, text, command, parameter, do_not_refresh_cache=True)
except re.error as e: except re.error as e:
@ -435,7 +448,7 @@ def cmd_rb_play(bot, user, text, command, parameter):
url = radiobrowser.geturl_byid(parameter) url = radiobrowser.geturl_byid(parameter)
if url != "-1": if url != "-1":
log.info('cmd: Found url: ' + url) log.info('cmd: Found url: ' + url)
music_wrapper = get_item_wrapper(bot, type='radio', url=url, name=stationname) music_wrapper = get_item_wrapper(bot, type='radio', url=url, name=stationname, user=user)
var.playlist.append(music_wrapper) var.playlist.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())
bot.async_download_next() bot.async_download_next()
@ -448,7 +461,7 @@ 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 global log, yt_last_result, yt_last_page, song_shortlist
item_per_page = 5 item_per_page = 5
if parameter: if parameter:
@ -456,6 +469,10 @@ def cmd_yt_search(bot, user, text, command, parameter):
if parameter.startswith("-n"): if parameter.startswith("-n"):
yt_last_page += 1 yt_last_page += 1
if len(yt_last_result) > yt_last_page * item_per_page: if len(yt_last_result) > yt_last_page * item_per_page:
song_shortlist = [{'type': 'url',
'url': "https://www.youtube.com/watch?v=" + result[0],
'title': result[1]
} for result in yt_last_result[yt_last_page * item_per_page: item_per_page]]
msg = _yt_format_result(yt_last_result, yt_last_page * item_per_page, item_per_page) msg = _yt_format_result(yt_last_result, yt_last_page * item_per_page, item_per_page)
bot.send_msg(constants.strings('yt_result', result_table=msg), text) bot.send_msg(constants.strings('yt_result', result_table=msg), text)
else: else:
@ -467,6 +484,8 @@ def cmd_yt_search(bot, user, text, command, parameter):
if results: if results:
yt_last_result = results yt_last_result = results
yt_last_page = 0 yt_last_page = 0
song_shortlist = [{'type': 'url', 'url': "https://www.youtube.com/watch?v=" + result[0]}
for result in results[0: item_per_page]]
msg = _yt_format_result(results, 0, item_per_page) msg = _yt_format_result(results, 0, item_per_page)
bot.send_msg(constants.strings('yt_result', result_table=msg), text) bot.send_msg(constants.strings('yt_result', result_table=msg), text)
else: else:
@ -478,7 +497,7 @@ 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]):
msg += '<tr><td>{index:d}</td><td>{title}</td><td>{uploader}</td></tr>'.format( msg += '<tr><td>{index:d}</td><td>{title}</td><td>{uploader}</td></tr>'.format(
index=index + 1 + start, title=item[1], uploader=item[2]) index=index + 1, title=item[1], uploader=item[2])
msg += '</table>' msg += '</table>'
return msg return msg
@ -488,18 +507,14 @@ def cmd_yt_play(bot, user, text, command, parameter):
global log, yt_last_result, yt_last_page global log, yt_last_result, yt_last_page
if parameter: if parameter:
if parameter.isdigit() and 0 <= int(parameter) - 1 < len(yt_last_result): results = util.youtube_search(parameter)
url = "https://www.youtube.com/watch?v=" + yt_last_result[int(parameter) - 1][0] if results:
yt_last_result = results
yt_last_page = 0
url = "https://www.youtube.com/watch?v=" + yt_last_result[0][0]
cmd_play_url(bot, user, text, command, url) cmd_play_url(bot, user, text, command, url)
else: else:
results = util.youtube_search(parameter) bot.send_msg(constants.strings('yt_query_error'))
if results:
yt_last_result = results
yt_last_page = 0
url = "https://www.youtube.com/watch?v=" + yt_last_result[0][0]
cmd_play_url(bot, user, text, command, url)
else:
bot.send_msg(constants.strings('yt_query_error'))
else: else:
bot.send_msg(constants.strings('bad_parameter', command=command), text) bot.send_msg(constants.strings('bad_parameter', command=command), text)
@ -690,7 +705,7 @@ def cmd_remove(bot, user, text, command, parameter):
def cmd_list_file(bot, user, text, command, parameter): def cmd_list_file(bot, user, text, command, parameter):
global log global log
files = var.library.files files = var.cache.files
msgs = [ "<br> <b>Files available:</b>" if not parameter else "<br> <b>Matched files:</b>" ] msgs = [ "<br> <b>Files available:</b>" if not parameter else "<br> <b>Matched files:</b>" ]
try: try:
count = 0 count = 0
@ -871,6 +886,8 @@ 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
if not parameter: if not parameter:
bot.send_msg(constants.strings('bad_parameter', command=command)) bot.send_msg(constants.strings('bad_parameter', command=command))
return return
@ -880,17 +897,99 @@ def cmd_find_tagged(bot, user, text, command, parameter):
tags = parameter.split(",") tags = parameter.split(",")
tags = list(map(lambda t: t.strip(), tags)) tags = list(map(lambda t: t.strip(), tags))
music_wrappers = get_item_wrappers_by_tags(bot, tags, user)
for music_wrapper in music_wrappers: music_dicts = var.music_db.query_music_by_tags(tags)
song_shortlist = music_dicts
items = dicts_to_items(bot, music_dicts)
for i, item in enumerate(items):
count += 1 count += 1
msgs.append("<li><b>{}</b> (<i>{}</i>)</li>".format(music_wrapper.item().title, ", ".join(music_wrapper.item().tags))) msgs.append("<li><b>{:d}</b> - <b>{}</b> (<i>{}</i>)</li>".format(i+1, item.title, ", ".join(item.tags)))
if count != 0: if count != 0:
msgs.append("</ul>") msgs.append("</ul>")
msgs.append(constants.strings("shortlist_instruction"))
send_multi_lines(bot, msgs, text, "") send_multi_lines(bot, msgs, text, "")
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):
global song_shortlist
if not parameter:
bot.send_msg(constants.strings('bad_parameter', command=command))
return
msgs = [constants.strings('multiple_file_found') + "<ul>"]
count = 0
_keywords = parameter.split(" ")
keywords = []
for kw in _keywords:
if kw:
keywords.append(kw)
music_dicts = var.music_db.query_music_by_keywords(keywords)
items = dicts_to_items(bot, music_dicts)
song_shortlist = music_dicts
for item in items:
count += 1
if len(item.tags) > 0:
msgs.append("<li><b>{:d}</b> - [{}] <b>{}</b> (<i>{}</i>)</li>".format(count, item.display_type(), item.title, ", ".join(item.tags)))
else:
msgs.append("<li><b>{:d}</b> - [{}] <b>{}</b> </li>".format(count, item.display_type(), item.title, ", ".join(item.tags)))
if count != 0:
msgs.append("</ul>")
msgs.append(constants.strings("shortlist_instruction"))
send_multi_lines(bot, msgs, text, "")
else:
bot.send_msg(constants.strings("no_file"), text)
def cmd_shortlist(bot, user, text, command, parameter):
global song_shortlist
indexes = []
try:
indexes = [ int(i) for i in parameter.split(" ") ]
except ValueError:
bot.send_msg(constants.strings('bad_parameter', command=command), text)
return
if len(indexes) > 1:
msgs = [constants.strings('multiple_file_added') + "<ul>"]
for index in indexes:
if 1 <= index <= len(song_shortlist):
kwargs = song_shortlist[index - 1]
kwargs['user'] = user
music_wrapper = get_item_wrapper(bot, **kwargs)
var.playlist.append(music_wrapper)
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
msgs.append("<li><b>{}</b></li>".format(music_wrapper.item().title))
song_shortlist = []
else:
bot.send_msg(constants.strings('bad_parameter', command=command), text)
return
msgs.append("</ul>")
send_multi_lines(bot, msgs, text, "")
song_shortlist = []
return
elif len(indexes) == 1:
index = indexes[0]
if 1 <= index <= len(song_shortlist):
kwargs = song_shortlist[index - 1]
kwargs['user'] = user
music_wrapper = get_item_wrapper(bot, **kwargs)
var.playlist.append(music_wrapper)
log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
song_shortlist = []
return
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
@ -907,7 +1006,7 @@ def cmd_drop_database(bot, user, text, command, parameter):
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):
var.library.build_dir_cache(bot) var.cache.build_dir_cache(bot)
log.info("command: Local file cache refreshed.") log.info("command: Local file cache refreshed.")
bot.send_msg(constants.strings('cache_refreshed'), text) bot.send_msg(constants.strings('cache_refreshed'), text)
else: else:

View File

@ -160,6 +160,8 @@ play_tag = tag
add_tag = addtag add_tag = addtag
remove_tag = untag remove_tag = untag
find_tagged = findtagged, ft find_tagged = findtagged, ft
search = search
add_from_shortlist = shortlist, sl
user_ban = userban user_ban = userban
user_unban = userunban user_unban = userunban
@ -225,7 +227,7 @@ unknown_mode = Unknown playback mode '{mode}'. It should be one of <i>one-shot</
current_mode = Current playback mode is <i>{mode}</i>. current_mode = Current playback mode is <i>{mode}</i>.
change_mode = Playback mode set to <i>{mode}</i> by {user}. change_mode = Playback mode set to <i>{mode}</i> by {user}.
repeat = Repeat {song} for {n} times. repeat = Repeat {song} for {n} times.
yt_result = Youtube query result: {result_table} Use <i>!ytplay</i> {{index}} to play the item you want. <br /> yt_result = Youtube query result: {result_table} Use <i>!sl {{indexes}}</i> to play the item you want. <br />
<i>!ytquery -n</i> for the next page. <i>!ytquery -n</i> for the next page.
yt_no_more = No more results! yt_no_more = No more results!
yt_query_error = Unable to query youtube! yt_query_error = Unable to query youtube!
@ -237,6 +239,7 @@ removed_tags = Removed tags <i>{tags}</i> from <b>{song}</b>.
removed_tags_from_all = Removed tags <i>{tags}</i> from songs on the playlist. removed_tags_from_all = Removed tags <i>{tags}</i> from songs on the playlist.
cleared_tags = Removed all tags from <b>{song}</b>. cleared_tags = Removed all tags from <b>{song}</b>.
cleared_tags_from_all = Removed all tags from songs on the playlist. cleared_tags_from_all = Removed all tags from songs on the playlist.
shortlist_instruction = Use <i>!sl {indexes}</i> to play the item you want.
help = <h3>Commands</h3> help = <h3>Commands</h3>
<b>Control</b> <b>Control</b>
@ -264,6 +267,7 @@ help = <h3>Commands</h3>
<li> <b>!<u>ur</u>l </b> {url} - add Youtube or SoundCloud music </li> <li> <b>!<u>ur</u>l </b> {url} - add Youtube or SoundCloud music </li>
<li> <b>!<u>playl</u>ist </b> {url} [{offset}] - add all items in a Youtube or SoundCloud playlist, and start with the {offset}-th item </li> <li> <b>!<u>playl</u>ist </b> {url} [{offset}] - add all items in a Youtube or SoundCloud playlist, and start with the {offset}-th item </li>
<li> <b>!<u>t</u>ag </b> {tags} - add all items with tags {tags}, tags separated by ",". </li> <li> <b>!<u>t</u>ag </b> {tags} - add all items with tags {tags}, tags separated by ",". </li>
<li> <b>!<u>sh</u>ortlist </b> (or <b>!sl</b>) {indexes} - add {indexes}-th item on the shortlist. </li>
<li> <b>!rm </b> {num} - remove the num-th song on the playlist </li> <li> <b>!rm </b> {num} - remove the num-th song on the playlist </li>
<li> <b>!<u>rep</u>eat </b> [{num}] - repeat current song {num} (1 by default) times.</li> <li> <b>!<u>rep</u>eat </b> [{num}] - repeat current song {num} (1 by default) times.</li>
<li> <b>!<u>ran</u>dom </b> - randomize the playlist.</li> <li> <b>!<u>ran</u>dom </b> - randomize the playlist.</li>
@ -275,8 +279,9 @@ help = <h3>Commands</h3>
<li> <b>!<u>yp</u>lay </b> {index/keywords} - play an item from the list returned by <i>!ytquery</i>, or add the <li> <b>!<u>yp</u>lay </b> {index/keywords} - play an item from the list returned by <i>!ytquery</i>, or add the
first search result of {keywords} into the playlist.</li> first search result of {keywords} into the playlist.</li>
</ul> </ul>
<b>Tag</b> <b>Music Library</b>
<ul> <ul>
<li> <b>!<u>se</u>arch </b> {keywords} - find item with {keywords} in the music library, keywords separated by space.</li>
<li> <b>!<u>addt</u>ag </b> {index} {tags} - add {tags} to {index}-th item on the playlist, tags separated by ",". </li> <li> <b>!<u>addt</u>ag </b> {index} {tags} - add {tags} to {index}-th item on the playlist, tags separated by ",". </li>
<li> <b>!<u>addt</u>ag </b> * {tags} - add {tags} to all items on the playlist. </li> <li> <b>!<u>addt</u>ag </b> * {tags} - add {tags} to all items on the playlist. </li>
<li> <b>!<u>un</u>tag </b> {index/*} {tags} - remove {tags} from {index}-th item on the playlist. </li> <li> <b>!<u>un</u>tag </b> {index/*} {tags} - remove {tags} from {index}-th item on the playlist. </li>

View File

@ -231,6 +231,40 @@ class MusicDatabase:
else: else:
return None return None
def query_music_by_keywords(self, keywords):
condition = []
filler = []
for keyword in keywords:
condition.append('title LIKE ?')
filler.append("%{:s}%".format(keyword))
condition_str = " AND ".join(condition)
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
results = cursor.execute("SELECT id, type, title, metadata, tags FROM music "
"WHERE %s" % condition_str, filler).fetchall()
conn.close()
if len(results) > 0:
music_dicts = []
for result in results:
music_dict = json.loads(result[3])
music_dict['type'] = result[1]
music_dict['title'] = result[2]
music_dict['id'] = result[0]
music_dict['tags'] = result[4].strip(",").split(",")
if not music_dict['tags'][0]:
music_dict['tags'] = []
music_dicts.append(music_dict)
return music_dicts
else:
return None
def query_music_by_tags(self, tags): def query_music_by_tags(self, tags):
condition = [] condition = []
filler = [] filler = []

View File

@ -119,7 +119,7 @@ def build_tags_color_lookup():
def build_path_tags_lookup(): def build_path_tags_lookup():
path_lookup = {} path_lookup = {}
items = var.library.file_id_lookup.items() items = var.cache.file_id_lookup.items()
for path, id in items: for path, id in items:
path_lookup[path] = var.music_db.query_tags_by_id(id) path_lookup[path] = var.music_db.query_tags_by_id(id)
@ -133,17 +133,17 @@ def recur_dir(dirobj):
@web.route("/", methods=['GET']) @web.route("/", methods=['GET'])
@requires_auth @requires_auth
def index(): def index():
while var.library.dir_lock.locked(): while var.cache.dir_lock.locked():
time.sleep(0.1) time.sleep(0.1)
tags_color_lookup = build_tags_color_lookup() tags_color_lookup = build_tags_color_lookup()
path_tags_lookup = build_path_tags_lookup() path_tags_lookup = build_path_tags_lookup()
return render_template('index.html', return render_template('index.html',
all_files=var.library.files, all_files=var.cache.files,
tags_lookup=path_tags_lookup, tags_lookup=path_tags_lookup,
tags_color_lookup=tags_color_lookup, tags_color_lookup=tags_color_lookup,
music_library=var.library.dir, music_library=var.cache.dir,
os=os, os=os,
playlist=var.playlist, playlist=var.playlist,
user=var.user, user=var.user,
@ -198,7 +198,7 @@ def post():
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):
music_wrapper = get_item_wrapper_by_id(var.bot, var.library.file_id_lookup[request.form['add_file_bottom']], user) music_wrapper = get_item_wrapper_by_id(var.bot, var.cache.file_id_lookup[request.form['add_file_bottom']], user)
var.playlist.append(music_wrapper) var.playlist.append(music_wrapper)
log.info('web: add to playlist(bottom): ' + music_wrapper.format_debug_string()) log.info('web: add to playlist(bottom): ' + music_wrapper.format_debug_string())
@ -206,7 +206,7 @@ def post():
elif 'add_file_next' in request.form and ".." not in request.form['add_file_next']: elif 'add_file_next' in request.form and ".." not in request.form['add_file_next']:
path = var.music_folder + request.form['add_file_next'] path = var.music_folder + request.form['add_file_next']
if os.path.isfile(path): if os.path.isfile(path):
music_wrapper = get_item_wrapper_by_id(var.bot, var.library.file_id_lookup[request.form['add_file_next']], user) music_wrapper = get_item_wrapper_by_id(var.bot, var.cache.file_id_lookup[request.form['add_file_next']], user)
var.playlist.insert(var.playlist.current_index + 1, music_wrapper) var.playlist.insert(var.playlist.current_index + 1, music_wrapper)
log.info('web: add to playlist(next): ' + music_wrapper.format_debug_string()) log.info('web: add to playlist(next): ' + music_wrapper.format_debug_string())
@ -220,7 +220,7 @@ def post():
folder += '/' folder += '/'
if os.path.isdir(var.music_folder + folder): if os.path.isdir(var.music_folder + folder):
dir = var.library.dir dir = var.cache.dir
if 'add_folder_recursively' in request.form: if 'add_folder_recursively' in request.form:
files = dir.get_files_recursively(folder) files = dir.get_files_recursively(folder)
else: else:
@ -228,7 +228,7 @@ def post():
music_wrappers = list(map( music_wrappers = list(map(
lambda file: lambda file:
get_item_wrapper_by_id(var.bot, var.library.file_id_lookup[folder + file], user), get_item_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)
@ -328,7 +328,7 @@ def post():
var.db.set('playlist', 'playback_mode', "autoplay") var.db.set('playlist', 'playback_mode', "autoplay")
log.info("web: playback mode changed to autoplay.") log.info("web: playback mode changed to autoplay.")
if action == "rescan": if action == "rescan":
var.library.build_dir_cache(var.bot) var.cache.build_dir_cache(var.bot)
log.info("web: Local file cache refreshed.") log.info("web: Local file cache refreshed.")
elif action == "stop": elif action == "stop":
var.bot.stop() var.bot.stop()
@ -413,7 +413,7 @@ def download():
log.info('web: Download of file %s requested from %s:' % (requested_file, request.remote_addr)) log.info('web: Download of file %s requested from %s:' % (requested_file, request.remote_addr))
if '../' not in requested_file: if '../' not in requested_file:
folder_path = var.music_folder folder_path = var.music_folder
files = var.library.files files = var.cache.files
if requested_file in files: if requested_file in files:
filepath = os.path.join(folder_path, requested_file) filepath = os.path.join(folder_path, requested_file)

View File

@ -3,13 +3,13 @@ from database import MusicDatabase
import json import json
import threading import threading
from media.item import item_builders, item_loaders, item_id_generators from media.item import item_builders, item_loaders, item_id_generators, dict_to_item, dicts_to_items
from database import MusicDatabase from database import MusicDatabase
import variables as var import variables as var
import util import util
class MusicLibrary(dict): class MusicCache(dict):
def __init__(self, db: MusicDatabase): def __init__(self, db: MusicDatabase):
super().__init__() super().__init__()
self.db = db self.db = db
@ -36,7 +36,11 @@ class MusicLibrary(dict):
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.
# if cached # if cached
id = item_id_generators[kwargs['type']](**kwargs) if 'id' in kwargs:
id = kwargs['id']
else:
id = item_id_generators[kwargs['type']](**kwargs)
if id in self: if id in self:
return self[id] return self[id]
@ -57,8 +61,7 @@ class MusicLibrary(dict):
if music_dicts: if music_dicts:
for music_dict in music_dicts: for music_dict in music_dicts:
id = music_dict['id'] id = music_dict['id']
type = music_dict['type'] self[id] = dict_to_item(bot, music_dict)
self[id] = item_loaders[type](bot, music_dict)
items.append(self[id]) items.append(self[id])
return items return items
@ -67,8 +70,7 @@ class MusicLibrary(dict):
music_dicts = self.db.query_music(id=id) music_dicts = self.db.query_music(id=id)
if music_dicts: if music_dicts:
music_dict = music_dicts[0] music_dict = music_dicts[0]
type = music_dict['type'] self[id] = dict_to_item(bot, music_dict)
self[id] = item_loaders[type](bot, music_dict)
return self[id] return self[id]
else: else:
return None return None

View File

@ -154,7 +154,7 @@ 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, artist=self.artist if self.artist else '??',
user=user user=user
) )

View File

@ -28,6 +28,18 @@ 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):
items = []
for music_dict in music_dicts:
type = music_dict['type']
items.append(item_loaders[type](bot, music_dict))
return items
def dict_to_item(bot, music_dict):
type = music_dict['type']
return item_loaders[type](bot, music_dict)
class BaseItem: class BaseItem:
def __init__(self, bot, from_dict=None): def __init__(self, bot, from_dict=None):
self.bot = bot self.bot = bot

View File

@ -10,7 +10,7 @@ from media.url import URLItem
from media.url_from_playlist import PlaylistURLItem from media.url_from_playlist import PlaylistURLItem
from media.radio import RadioItem from media.radio import RadioItem
from database import MusicDatabase from database import MusicDatabase
from media.library import MusicLibrary from media.cache import MusicCache
class PlaylistItemWrapper: class PlaylistItemWrapper:
def __init__(self, lib, id, type, user): def __init__(self, lib, id, type, user):
@ -95,24 +95,25 @@ class PlaylistItemWrapper:
return self.item().display_type() return self.item().display_type()
# Remember!!! Using these three get wrapper functions will automatically add items into the cache!
def get_item_wrapper(bot, **kwargs): def get_item_wrapper(bot, **kwargs):
item = var.library.get_item(bot, **kwargs) item = var.cache.get_item(bot, **kwargs)
if 'user' not in kwargs: if 'user' not in kwargs:
raise KeyError("Which user added this song?") raise KeyError("Which user added this song?")
return PlaylistItemWrapper(var.library, item.id, kwargs['type'], kwargs['user']) return PlaylistItemWrapper(var.cache, item.id, kwargs['type'], kwargs['user'])
def get_item_wrapper_by_id(bot, id, user): def get_item_wrapper_by_id(bot, id, user):
item = var.library.get_item_by_id(bot, id) item = var.cache.get_item_by_id(bot, id)
if item: if item:
return PlaylistItemWrapper(var.library, item.id, item.type, user) return PlaylistItemWrapper(var.cache, item.id, item.type, user)
else: else:
return None return None
def get_item_wrappers_by_tags(bot, tags, user): def get_item_wrappers_by_tags(bot, tags, user):
items = var.library.get_items_by_tags(bot, tags) items = var.cache.get_items_by_tags(bot, tags)
ret = [] ret = []
for item in items: for item in items:
ret.append(PlaylistItemWrapper(var.library, item.id, item.type, user)) ret.append(PlaylistItemWrapper(var.cache, item.id, item.type, user))
return ret return ret
def get_playlist(mode, _list=None, index=None): def get_playlist(mode, _list=None, index=None):
@ -235,7 +236,7 @@ class BasePlaylist(list):
counter += 1 counter += 1
if counter == 0: if counter == 0:
var.library.free(removed.id) var.cache.free(removed.id)
return removed return removed
def remove_by_id(self, id): def remove_by_id(self, id):
@ -280,7 +281,7 @@ class BasePlaylist(list):
def clear(self): def clear(self):
self.version += 1 self.version += 1
self.current_index = -1 self.current_index = -1
var.library.free_all() var.cache.free_all()
super().clear() super().clear()
def save(self): def save(self):
@ -330,7 +331,7 @@ class BasePlaylist(list):
self.log.debug("playlist: validating %s" % item.format_debug_string()) self.log.debug("playlist: validating %s" % item.format_debug_string())
if not item.validate() or item.is_failed(): if not item.validate() or item.is_failed():
self.log.debug("playlist: validating failed.") self.log.debug("playlist: validating failed.")
var.library.delete(item.id) var.cache.delete(item.id)
self.remove_by_id(item.id) self.remove_by_id(item.id)
self.log.debug("playlist: validating finished.") self.log.debug("playlist: validating finished.")

View File

@ -30,7 +30,7 @@ import media.file
import media.radio import media.radio
import media.system import media.system
from media.playlist import BasePlaylist from media.playlist import BasePlaylist
from media.library import MusicLibrary from media.cache import MusicCache
class MumbleBot: class MumbleBot:
@ -347,7 +347,7 @@ class MumbleBot:
break break
else: else:
var.playlist.remove_by_id(next.id) var.playlist.remove_by_id(next.id)
var.library.delete(next.id) var.cache.delete(next.id)
# ======================= # =======================
@ -407,7 +407,7 @@ class MumbleBot:
self.send_msg(constants.strings('download_in_progress', item=current.format_short_string())) self.send_msg(constants.strings('download_in_progress', item=current.format_short_string()))
else: else:
var.playlist.remove_by_id(current.id) var.playlist.remove_by_id(current.id)
var.library.delete(current.id) var.cache.delete(current.id)
else: else:
self._loop_status = 'Empty queue' self._loop_status = 'Empty queue'
else: else:
@ -639,7 +639,7 @@ if __name__ == '__main__':
else: else:
var.music_db = MusicDatabase(":memory:") var.music_db = MusicDatabase(":memory:")
var.library = MusicLibrary(var.music_db) var.cache = MusicCache(var.music_db)
# load playback mode # load playback mode
playback_mode = None playback_mode = None
@ -658,9 +658,9 @@ if __name__ == '__main__':
if var.config.get("bot", "refresh_cache_on_startup", fallback=True)\ if var.config.get("bot", "refresh_cache_on_startup", fallback=True)\
or not var.db.has_option("dir_cache", "files"): or not var.db.has_option("dir_cache", "files"):
var.library.build_dir_cache(var.bot) var.cache.build_dir_cache(var.bot)
else: else:
var.library.load_dir_cache(var.bot) var.cache.load_dir_cache(var.bot)
# load playlist # load playlist
if var.config.getboolean('bot', 'save_playlist', fallback=True): if var.config.getboolean('bot', 'save_playlist', fallback=True):

View File

@ -1,6 +1,6 @@
bot = None bot = None
playlist = None playlist = None
library = None cache = None
user = "" user = ""
is_proxified = False is_proxified = False