refactor: rewrite confusing part

This commit is contained in:
Terry Geng
2020-04-08 08:55:43 +08:00
parent e1c5fc32e9
commit e86b5ca659
11 changed files with 163 additions and 147 deletions

View File

@ -21,12 +21,12 @@ class MusicCache(dict):
self.log = logging.getLogger("bot")
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, id):
if id in self:
return self[id]
# if not cached, query the database
item = self.fetch(bot, id)
item = self.fetch(id)
if item is not None:
self[id] = item
self.log.debug("library: music found in database: %s" % item.format_debug_string())
@ -36,7 +36,7 @@ class MusicCache(dict):
# print(id)
# 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, **kwargs):
# kwargs should provide type and id, and parameters to build the item if not existed in the library.
# if cached
if 'id' in kwargs:
@ -48,31 +48,31 @@ class MusicCache(dict):
return self[id]
# if not cached, query the database
item = self.fetch(bot, id)
item = self.fetch(id)
if item is not None:
self[id] = item
self.log.debug("library: music found in database: %s" % item.format_debug_string())
return item
# 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']](**kwargs) # newly built item will not be saved immediately
return self[id]
def get_items_by_tags(self, bot, tags):
def get_items_by_tags(self, tags):
music_dicts = self.db.query_music_by_tags(tags)
items = []
if music_dicts:
for music_dict in music_dicts:
id = music_dict['id']
self[id] = dict_to_item(bot, music_dict)
self[id] = dict_to_item(music_dict)
items.append(self[id])
return items
def fetch(self, bot, id):
def fetch(self, id):
music_dict = self.db.query_music_by_id(id)
if music_dict:
self[id] = dict_to_item(bot, music_dict)
self[id] = dict_to_item(music_dict)
return self[id]
else:
return None
@ -83,7 +83,7 @@ class MusicCache(dict):
self.db.manage_special_tags()
def free_and_delete(self, id):
item = self.get_item_by_id(None, id)
item = self.get_item_by_id(id)
if item:
self.log.debug("library: DELETE item from the database: %s" % item.format_debug_string())
@ -104,14 +104,14 @@ class MusicCache(dict):
self.log.debug("library: all cache freed")
self.clear()
def build_dir_cache(self, bot):
def build_dir_cache(self):
self.dir_lock.acquire()
self.log.info("library: rebuild directory cache")
files = util.get_recursive_file_list_sorted(var.music_folder)
for file in files:
results = self.db.query_music(Condition().and_equal('path', file))
if not results:
item = item_builders['file'](bot, path=file)
item = item_builders['file'](path=file)
self.log.debug("library: music save into database: %s" % item.format_debug_string())
self.db.insert_music(item.to_dict())
@ -207,33 +207,33 @@ def get_cached_wrappers(items, user):
return wrappers
def get_cached_wrapper_from_scrap(bot, **kwargs):
item = var.cache.get_item(bot, **kwargs)
def get_cached_wrapper_from_scrap(**kwargs):
item = var.cache.get_item(**kwargs)
if 'user' not in kwargs:
raise KeyError("Which user added this song?")
return CachedItemWrapper(var.cache, item.id, kwargs['type'], kwargs['user'])
def get_cached_wrapper_from_dict(bot, dict_from_db, user):
def get_cached_wrapper_from_dict(dict_from_db, user):
if dict_from_db:
item = dict_to_item(bot, dict_from_db)
item = dict_to_item(dict_from_db)
return get_cached_wrapper(item, user)
return None
def get_cached_wrappers_from_dicts(bot, dicts_from_db, user):
def get_cached_wrappers_from_dicts(dicts_from_db, user):
items = []
for dict_from_db in dicts_from_db:
if dict_from_db:
items.append(get_cached_wrapper_from_dict(bot, dict_from_db, user))
items.append(get_cached_wrapper_from_dict(dict_from_db, user))
return items
def get_cached_wrapper_by_id(bot, id, user):
item = var.cache.get_item_by_id(bot, id)
def get_cached_wrapper_by_id(id, user):
item = var.cache.get_item_by_id(id)
if item:
return CachedItemWrapper(var.cache, item.id, item.type, user)
def get_cached_wrappers_by_tags(bot, tags, user):
items = var.cache.get_items_by_tags(bot, tags)
def get_cached_wrappers_by_tags(tags, user):
items = var.cache.get_items_by_tags(tags)
ret = []
for item in items:
ret.append(CachedItemWrapper(var.cache, item.id, item.type, user))

View File

@ -7,7 +7,7 @@ import mutagen
from PIL import Image
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, ValidationFailedError
import constants
'''
@ -22,12 +22,12 @@ type : file
'''
def file_item_builder(bot, **kwargs):
return FileItem(bot, kwargs['path'])
def file_item_builder(**kwargs):
return FileItem(kwargs['path'])
def file_item_loader(bot, _dict):
return FileItem(bot, "", _dict)
def file_item_loader(_dict):
return FileItem("", _dict)
def file_item_id_generator(**kwargs):
@ -40,9 +40,9 @@ item_id_generators['file'] = file_item_id_generator
class FileItem(BaseItem):
def __init__(self, bot, path, from_dict=None):
def __init__(self, path, from_dict=None):
if not from_dict:
super().__init__(bot)
super().__init__()
self.path = path
self.title = ""
self.artist = ""
@ -53,7 +53,7 @@ class FileItem(BaseItem):
self.ready = "yes"
self.keywords = self.title + " " + self.artist
else:
super().__init__(bot, from_dict)
super().__init__(from_dict)
self.artist = from_dict['artist']
self.thumbnail = from_dict['thumbnail']
if not self.validate():
@ -71,8 +71,7 @@ class FileItem(BaseItem):
if not os.path.exists(self.uri()):
self.log.info(
"file: music file missed for %s" % self.format_debug_string())
self.send_client_message(constants.strings('file_missed', file=self.path))
return False
raise ValidationFailedError(constants.strings('file_missed', file=self.path))
if not self.keywords:
self.keywords = self.title + " " + self.artist # migrate from previous version

View File

@ -5,12 +5,12 @@ item_loaders = {}
item_id_generators = {}
def example_builder(bot, **kwargs):
return BaseItem(bot)
def example_builder(**kwargs):
return BaseItem()
def example_loader(bot, _dict):
return BaseItem(bot, from_dict=_dict)
def example_loader(_dict):
return BaseItem(from_dict=_dict)
def example_id_generator(**kwargs):
@ -22,22 +22,28 @@ item_loaders['base'] = example_loader
item_id_generators['base'] = example_id_generator
def dicts_to_items(bot, music_dicts):
def dicts_to_items(music_dicts):
items = []
for music_dict in music_dicts:
type = music_dict['type']
items.append(item_loaders[type](bot, music_dict))
items.append(item_loaders[type](music_dict))
return items
def dict_to_item(bot, music_dict):
def dict_to_item(music_dict):
type = music_dict['type']
return item_loaders[type](bot, music_dict)
return item_loaders[type](music_dict)
class ValidationFailedError(Exception):
def __init__(self, msg = None):
self.msg = msg
class PreparationFailedError(Exception):
def __init__(self, msg = None):
self.msg = msg
class BaseItem:
def __init__(self, bot, from_dict=None):
self.bot = bot
def __init__(self, from_dict=None):
self.log = logging.getLogger("bot")
self.type = "base"
self.title = ""
@ -64,7 +70,7 @@ class BaseItem:
return True if self.ready == "failed" else False
def validate(self):
return False
raise ValidationFailedError(None)
def uri(self):
raise
@ -104,10 +110,6 @@ class BaseItem:
def display_type(self):
return ""
def send_client_message(self, msg):
if self.bot:
self.bot.send_channel_msg(msg) # TODO: this is way too ugly. It mixed up bot with items. Change it into exceptions in the future.
def to_dict(self):
return {"type": self.type,
"id": self.id,

View File

@ -7,6 +7,7 @@ import time
import variables as var
from media.cache import CachedItemWrapper, get_cached_wrapper_from_dict, get_cached_wrapper_by_id
from database import Condition
from media.item import ValidationFailedError, PreparationFailedError
def get_playlist(mode, _list=None, index=None):
@ -196,7 +197,7 @@ class BasePlaylist(list):
items.sort(key=lambda v: int(v[0]))
for item in items:
item = json.loads(item[1])
music_wrapper = get_cached_wrapper_by_id(var.bot, item['id'], item['user'])
music_wrapper = get_cached_wrapper_by_id(item['id'], item['user'])
if music_wrapper:
music_wrappers.append(music_wrapper)
self.from_list(music_wrappers, current_index)
@ -224,33 +225,23 @@ class BasePlaylist(list):
item = self.pending_items.pop()
self.log.debug("playlist: validating %s" % item.format_debug_string())
ver = item.version
if not item.validate() or item.is_failed():
try:
item.validate()
except ValidationFailedError as e:
self.log.debug("playlist: validating failed.")
if var.bot:
var.bot.send_channel_msg(e.msg)
var.cache.free_and_delete(item.id)
self.remove_by_id(item.id)
continue
if item.version > ver:
self.version += 1
self.log.debug("playlist: validating finished.")
self.validating_thread_lock.release()
def async_prepare(self, index):
th = threading.Thread(
target=self._prepare, name="Prepare-" + self[index].id[:7], args=(index,))
self.log.info(
"%s: start preparing item in thread: " % self[index].item().type + self[index].format_debug_string())
th.daemon = True
th.start()
return th
def _prepare(self, index):
item = self[index]
ver = item.version
item.prepare()
if item.version > ver:
self.version += 1
class OneshotPlaylist(BasePlaylist):
def __init__(self):
@ -363,7 +354,7 @@ class AutoPlaylist(OneshotPlaylist):
Condition().and_like('tags', "%don't autoplay,%")))
if dicts:
_list = [get_cached_wrapper_from_dict(var.bot, _dict, "AutoPlay") for _dict in dicts]
_list = [get_cached_wrapper_from_dict(_dict, "AutoPlay") for _dict in dicts]
self.from_list(_list, -1)
# def from_list(self, _list, current_index):

View File

@ -86,15 +86,15 @@ def get_radio_title(url):
return url
def radio_item_builder(bot, **kwargs):
def radio_item_builder(**kwargs):
if 'name' in kwargs:
return RadioItem(bot, kwargs['url'], kwargs['name'])
return RadioItem(kwargs['url'], kwargs['name'])
else:
return RadioItem(bot, kwargs['url'], '')
return RadioItem(kwargs['url'], '')
def radio_item_loader(bot, _dict):
return RadioItem(bot, "", "", _dict)
def radio_item_loader(_dict):
return RadioItem("", "", _dict)
def radio_item_id_generator(**kwargs):
@ -107,9 +107,9 @@ item_id_generators['radio'] = radio_item_id_generator
class RadioItem(BaseItem):
def __init__(self, bot, url, name="", from_dict=None):
def __init__(self, url, name="", from_dict=None):
if from_dict is None:
super().__init__(bot)
super().__init__()
self.url = url
if not name:
self.title = get_radio_server_description(self.url) # The title of the radio station
@ -117,7 +117,7 @@ class RadioItem(BaseItem):
self.title = name
self.id = hashlib.md5(url.encode()).hexdigest()
else:
super().__init__(bot, from_dict)
super().__init__(from_dict)
self.url = from_dict['url']
self.title = from_dict['title']

View File

@ -12,18 +12,19 @@ import base64
import constants
import media
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, ValidationFailedError, \
PreparationFailedError
import media.system
log = logging.getLogger("bot")
def url_item_builder(bot, **kwargs):
return URLItem(bot, kwargs['url'])
def url_item_builder(**kwargs):
return URLItem(kwargs['url'])
def url_item_loader(bot, _dict):
return URLItem(bot, "", _dict)
def url_item_loader(_dict):
return URLItem("", _dict)
def url_item_id_generator(**kwargs):
@ -36,10 +37,10 @@ item_id_generators['url'] = url_item_id_generator
class URLItem(BaseItem):
def __init__(self, bot, url, from_dict=None):
def __init__(self, url, from_dict=None):
self.validating_lock = threading.Lock()
if from_dict is None:
super().__init__(bot)
super().__init__()
self.url = url if url[-1] != "/" else url[:-1]
self.title = ''
self.duration = 0
@ -47,7 +48,7 @@ class URLItem(BaseItem):
self.path = var.tmp_folder + self.id + ".mp3"
self.thumbnail = ''
else:
super().__init__(bot, from_dict)
super().__init__(from_dict)
self.url = from_dict['url']
self.duration = from_dict['duration']
self.path = from_dict['path']
@ -77,10 +78,10 @@ class URLItem(BaseItem):
self.validating_lock.release()
return True
if self.ready == 'failed':
self.validating_lock.release()
return False
# if self.ready == 'failed':
# self.validating_lock.release()
# return False
#
if os.path.exists(self.path):
self.validating_lock.release()
self.ready = "yes"
@ -97,8 +98,7 @@ class URLItem(BaseItem):
# 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) + " min -- too long")
self.send_client_message(constants.strings('too_long', song=self.title))
return False
raise ValidationFailedError(constants.strings('too_long', song=self.title))
else:
self.ready = "validated"
self.version += 1 # notify wrapper to save me
@ -136,8 +136,7 @@ class URLItem(BaseItem):
if not succeed:
self.ready = 'failed'
self.log.error("url: error while fetching info from the URL")
self.send_client_message(constants.strings('unable_download'))
return False
raise ValidationFailedError(constants.strings('unable_download', item=self.format_title()))
def _download(self):
media.system.clear_tmp_folder(var.tmp_folder, var.config.getint('bot', 'tmp_folder_max_size'))
@ -190,10 +189,9 @@ class URLItem(BaseItem):
else:
for f in glob.glob(base_path + "*"):
os.remove(f)
self.send_client_message(constants.strings('unable_download'))
self.ready = "failed"
self.downloading = False
return False
raise PreparationFailedError(constants.strings('unable_download', item=self.format_title()))
def _read_thumbnail_from_file(self, path_thumbnail):
if os.path.isfile(path_thumbnail):

View File

@ -51,16 +51,15 @@ def get_playlist_info(url, start_index=0, user=""):
return items
def playlist_url_item_builder(bot, **kwargs):
return PlaylistURLItem(bot,
kwargs['url'],
def playlist_url_item_builder(**kwargs):
return PlaylistURLItem(kwargs['url'],
kwargs['title'],
kwargs['playlist_url'],
kwargs['playlist_title'])
def playlist_url_item_loader(bot, _dict):
return PlaylistURLItem(bot, "", "", "", "", _dict)
def playlist_url_item_loader(_dict):
return PlaylistURLItem("", "", "", "", _dict)
item_builders['url_from_playlist'] = playlist_url_item_builder
@ -69,14 +68,14 @@ item_id_generators['url_from_playlist'] = url_item_id_generator
class PlaylistURLItem(URLItem):
def __init__(self, bot, url, title, playlist_url, playlist_title, from_dict=None):
def __init__(self, url, title, playlist_url, playlist_title, from_dict=None):
if from_dict is None:
super().__init__(bot, url)
super().__init__(url)
self.title = title
self.playlist_url = playlist_url
self.playlist_title = playlist_title
else:
super().__init__(bot, "", from_dict)
super().__init__("", from_dict)
self.playlist_title = from_dict['playlist_title']
self.playlist_url = from_dict['playlist_url']