4 from database import MusicDatabase
8 from media.item import item_builders, item_loaders, item_id_generators, dict_to_item, dicts_to_items
9 from database import MusicDatabase
10 import variables as var
14 class MusicCache(dict):
15 def __init__(self, db: MusicDatabase):
18 self.log = logging.getLogger("bot")
21 self.dir_lock = threading.Lock()
23 def get_item_by_id(self, bot, id): # Why all these functions need a bot? Because it need the bot to send message!
27 # if not cached, query the database
28 item = self.fetch(bot, id)
31 self.log.debug("library: music found in database: %s" % item.format_debug_string())
36 #raise KeyError("Unable to fetch item from the database! Please try to refresh the cache by !recache.")
39 def get_item(self, bot, **kwargs):
40 # kwargs should provide type and id, and parameters to build the item if not existed in the library.
45 id = item_id_generators[kwargs['type']](**kwargs)
50 # if not cached, query the database
51 item = self.fetch(bot, id)
54 self.log.debug("library: music found in database: %s" % item.format_debug_string())
57 # if not in the database, build one
58 self[id] = item_builders[kwargs['type']](bot, **kwargs) # newly built item will not be saved immediately
61 def get_items_by_tags(self, bot, tags):
62 music_dicts = self.db.query_music_by_tags(tags)
65 for music_dict in music_dicts:
67 self[id] = dict_to_item(bot, music_dict)
68 items.append(self[id])
72 def fetch(self, bot, id):
73 music_dicts = self.db.query_music(id=id)
75 music_dict = music_dicts[0]
76 self[id] = dict_to_item(bot, music_dict)
82 self.log.debug("library: music save into database: %s" % self[id].format_debug_string())
83 self.db.insert_music(self[id].to_dict())
85 def free_and_delete(self, id):
86 item = self.get_item_by_id(None, id)
88 self.log.debug("library: DELETE item from the database: %s" % item.format_debug_string())
90 if item.type == 'file' and item.path in self.file_id_lookup:
91 if item.path in self.file_id_lookup:
92 del self.file_id_lookup[item.path]
93 self.files.remove(item.path)
95 elif item.type == 'url':
100 self.db.delete_music(id=item.id)
104 self.log.debug("library: cache freed for item: %s" % self[id].format_debug_string())
108 self.log.debug("library: all cache freed")
111 def build_dir_cache(self, bot):
112 self.dir_lock.acquire()
113 self.log.info("library: rebuild directory cache")
115 self.file_id_lookup = {}
116 files = util.get_recursive_file_list_sorted(var.music_folder)
117 self.dir = util.Dir(var.music_folder)
119 item = self.fetch(bot, item_id_generators['file'](path=file))
121 item = item_builders['file'](bot, path=file)
122 self.log.debug("library: music save into database: %s" % item.format_debug_string())
123 self.db.insert_music(item.to_dict())
125 self.dir.add_file(file)
126 self.files.append(file)
127 self.file_id_lookup[file] = item.id
129 self.save_dir_cache()
130 self.dir_lock.release()
132 def save_dir_cache(self):
133 var.db.set("dir_cache", "files", json.dumps(self.file_id_lookup))
135 def load_dir_cache(self, bot):
136 self.dir_lock.acquire()
137 self.log.info("library: load directory cache from database")
138 loaded = json.loads(var.db.get("dir_cache", "files"))
139 self.files = loaded.keys()
140 self.file_id_lookup = loaded
141 self.dir = util.Dir(var.music_folder)
142 for file, id in loaded.items():
143 self.dir.add_file(file)
144 self.dir_lock.release()
147 class CachedItemWrapper:
148 def __init__(self, lib, id, type, user):
153 self.log = logging.getLogger("bot")
157 return self.lib[self.id]
160 dict = self.item().to_dict()
161 dict['user'] = self.user
165 ret = self.item().validate()
166 if ret and self.item().version > self.version:
167 self.version = self.item().version
168 self.lib.save(self.id)
172 ret = self.item().prepare()
173 if ret and self.item().version > self.version:
174 self.version = self.item().version
175 self.lib.save(self.id)
178 def async_prepare(self):
179 th = threading.Thread(
180 target=self.prepare, name="Prepare-" + self.id[:7])
182 "%s: start preparing item in thread: " % self.item().type + self.format_debug_string())
188 return self.item().uri()
190 def add_tags(self, tags):
191 self.item().add_tags(tags)
192 if self.item().version > self.version:
193 self.version = self.item().version
194 self.lib.save(self.id)
196 def remove_tags(self, tags):
197 self.item().remove_tags(tags)
198 if self.item().version > self.version:
199 self.version = self.item().version
200 self.lib.save(self.id)
202 def clear_tags(self):
203 self.item().clear_tags()
204 if self.item().version > self.version:
205 self.version = self.item().version
206 self.lib.save(self.id)
209 return self.item().is_ready()
212 return self.item().is_failed()
214 def format_current_playing(self):
215 return self.item().format_current_playing(self.user)
217 def format_song_string(self):
218 return self.item().format_song_string(self.user)
220 def format_short_string(self):
221 return self.item().format_short_string()
223 def format_debug_string(self):
224 return self.item().format_debug_string()
226 def display_type(self):
227 return self.item().display_type()
230 # Remember!!! Get wrapper functions will automatically add items into the cache!
231 def get_cached_wrapper_from_scrap(bot, **kwargs):
232 item = var.cache.get_item(bot, **kwargs)
233 if 'user' not in kwargs:
234 raise KeyError("Which user added this song?")
235 return CachedItemWrapper(var.cache, item.id, kwargs['type'], kwargs['user'])
238 def get_cached_wrapper_from_dict(bot, dict_from_db, user):
239 item = dict_to_item(bot, dict_from_db)
240 var.cache[dict_from_db['id']] = item
241 return CachedItemWrapper(var.cache, item.id, item.type, user)
244 def get_cached_wrapper_by_id(bot, id, user):
245 item = var.cache.get_item_by_id(bot, id)
247 return CachedItemWrapper(var.cache, item.id, item.type, user)
252 def get_cached_wrappers_by_tags(bot, tags, user):
253 items = var.cache.get_items_by_tags(bot, tags)
256 ret.append(CachedItemWrapper(var.cache, item.id, item.type, user))