def __init__(self, audio_bot, name, random_next=False): self.audio_bot = audio_bot self.name = name self.playlist: List[AudioItem] = [] self.current_index = 0 self.handlers = AudioBotHandlers() self.random_next = random_next
class Lyrics(): def __init__(self, audiobot): self.audiobot = audiobot self.lyrics: List[LyricItem] = [] self.previous = None self.handlers = AudioBotHandlers() def load(self, item: AudioItem): self.clear() source = item.source if not isinstance(source, AudioSource): return else: lrc_source = source.lyric if lrc_source is None: return if lrc_source.filecontent != "": self.loadContent(lrc_source.filecontent) else: asyncio.ensure_future(self._async_load(lrc_source), loop=self.audiobot.loop) async def _async_load(self, lrc_source): async with aiohttp.ClientSession() as session: async with session.get(lrc_source.url, headers=lrc_source.headers) as response: self.loadContent(await response.text()) def loadContent(self, raw): for line in raw.split("\n"): li = LyricItem.init_from_text(line) if li is not None: self.lyrics.append(li) self.lyrics.append(LyricItem(2147483647, "")) def findLyricByTime(self, time): for i in range(len(self.lyrics) - 1): if (self.lyrics[i].time <= time < self.lyrics[i + 1].time): return self.lyrics[i] else: continue def clear(self): self.handlers.call(LyricUpdateEvent(self, LyricItem(0, ""))) self.lyrics.clear() def _raiseEvent(self, property, val, *args): if val is None: return lrc = self.findLyricByTime(float(val)) if lrc is None: return if self.previous and lrc.lyric == self.previous.lyric: return self.previous = lrc self.handlers.call(LyricUpdateEvent(self, lrc))
def __init__(self, loop=None): self.running = False self.user_playlist = Playlist(self, "user_playlist") self.system_playlist = Playlist(self, "system_playlist", random_next=True) self.history_playlist = Playlist(self, "history_playlist") self.blacklist = Blacklist(self) self.lyrics = Lyrics(self) self.current: AudioItem = None self.mpv_player: MPVPlayer = None self.live_room: LiveRoom = None self.loop = asyncio.get_event_loop() if loop == None else loop self._command_executors: Dict[str, command.CommandExecutor] = {} self.handlers = AudioBotHandlers()
class AudioBot(): selector = SourceSelector(NeteaseMusicSource, BiliAudioSource, BiliVideoSource, KuwoMusicSource) def __init__(self, loop=None): self.running = False self.user_playlist = Playlist(self, "user_playlist") self.system_playlist = Playlist(self, "system_playlist", random_next=True) self.history_playlist = Playlist(self, "history_playlist") self.blacklist = Blacklist(self) self.lyrics = Lyrics(self) self.current: AudioItem = None self.mpv_player: MPVPlayer = None self.live_room: LiveRoom = None self.loop = asyncio.get_event_loop() if loop == None else loop self._command_executors: Dict[str, command.CommandExecutor] = {} self.handlers = AudioBotHandlers() def start(self): self.running = True self.handlers.call(AudioBotStartEvent(self)) self.__idle_play_next("idle", self.mpv_player.getProperty(MPVProperty.IDLE)) def setPlayer(self, mpv_player: MPVPlayer): self.mpv_player = mpv_player self.mpv_player.registerPropertyHandler("audiobot.idleplaynext", MPVProperty.IDLE, self.__idle_play_next) self.mpv_player.registerPropertyHandler("audiobot.updatelyric", MPVProperty.TIME_POS, self.lyrics._raiseEvent) def setLiveRoom(self, live_room: LiveRoom): if self.live_room != None: self.live_room.clear_msg_handler() self.live_room = live_room self.live_room.register_msg_handler("audiobot.msg", self.__process_command) def registerCommandExecutor(self, id, cmd: Type[command.CommandExecutor.__class__]): self._command_executors[id] = cmd(self) def registerCommandExecutors(self, cmdlist: Dict): for id, val in cmdlist.items(): self.registerCommandExecutor(id, val) def _loadSystemPlaylist(self, config): try: self._thread_call(self.__loadSystemPlaylist, config) except: pass def __loadSystemPlaylist(self, config): playlists = config["playlist"] songs = config["song"] self.system_playlist.random_next = config["random"] for key, vals in playlists.items(): if key == "bilibili": source_class = BiliAudioListSource elif key == "netease": source_class = NeteasePlaylistSource else: continue for val in vals: c_s = source_class.initFromUrl(val) if c_s == None: continue c_s.load() if not c_s.isValid(): return for s in c_s.audios: self.system_playlist.append_raw(s) for key, vals in songs.items(): if key == "bilibili": source_class = BiliAudioSource elif key == "netease": source_class = NeteaseMusicSource else: continue for val in vals: s = source_class.initFromUrl(val) if s == None: continue self.system_playlist.append_raw(s) if self.running and self.current is None: self.playNext() def __getPlayableSource(self, sources: dict) -> BaseSource: for val in sources.values(): val: BaseSource if isinstance(val, WatchableSource) and isinstance(val, BaseSource): return val def playNext(self): if len(self.user_playlist) == 0 and len(self.system_playlist) == 0: return if len(self.user_playlist) != 0: self.__thread_play(self.user_playlist.pop_first()) return if len(self.system_playlist) != 0: next_item = self.system_playlist.get_next() next_item.source.load() self.__thread_play(next_item) def playByIndex(self, index): if index < 0 or index >= len(self.user_playlist): return self.__thread_play(self.user_playlist.remove(index)) def addBlacklistByIndex(self, index): if index < 0 or index >= len(self.user_playlist): return self.blacklist.appendPlaylistItem(self.user_playlist.remove(index)) def play(self, item: AudioItem): self.__thread_play(item) def __thread_play(self, item: AudioItem): self._thread_call(self.__play, item) def __play(self, item: AudioItem): item.source.load() if not item.source.isValid(): return item = MatchEngine.check(item) bs: BaseSource = self.__getPlayableSource(item.source.getBaseSources()) if bs == None: if self.current == None: self.playNext() return self.current = item self.history_playlist.append(item) self.lyrics.load(item) self.mpv_player.playByUrl(bs.url, headers=bs.headers) self.handlers.call(AudioBotPlayEvent(self, item)) def addAudioByUrl(self, url, user: User, index=-1, source_class: CommonSource.__class__ = None): source_class: CommonSource.__class__ = source_class if source_class else self.selector.select(url) source, keyword = MatchEngine.search(url, source_class) if source == None: return source.load() if source.isValid(): if index == -1: event: PlaylistAppendEvent = self.user_playlist.append_raw(source, user=user, keyword=keyword) else: event: PlaylistAppendEvent = self.user_playlist.insert_raw(source, index, user=user, keyword=keyword) if event.isCancelled(): return if self.current == None or self.mpv_player.getProperty(MPVProperty.IDLE): self.playNext() return if Config.system_playlist['autoskip'] and self.current.username == PlaylistUser.username: self.playNext() def playAudioByUrl(self, url, user: User, source_class: CommonSource.__class__ = None): source_class: CommonSource.__class__ = source_class if source_class else self.selector.select(url) source, keyword = MatchEngine.search(url, source_class) if source == None: return source.load() if source.isValid(): event: PlaylistAppendEvent = self.user_playlist.insert_raw(source, 0, user=user, keyword=keyword) if event.isCancelled(): return self.playNext() def _async_call(self, fun, *args, **kwargs): asyncio.ensure_future(vasyncio.asyncwrapper(fun)(*args, **kwargs), loop=self.loop) def _thread_call(self, fun, *args, **kwargs): self.loop.run_in_executor(None, lambda: fun(*args, **kwargs)) def __process_command(self, dmkMsg: DanmakuMessage, *args, **kwargs): command: str = dmkMsg.message.split(" ")[0] for cmd in self._command_executors.values(): if cmd.applicable(command): cmd.process(command, dmkMsg) def __idle_play_next(self, prop, value, *args, **kwargs): if value: self.current = None self.playNext()
class Playlist(): def __init__(self, audio_bot, name, random_next=False): self.audio_bot = audio_bot self.name = name self.playlist: List[AudioItem] = [] self.current_index = 0 self.handlers = AudioBotHandlers() self.random_next = random_next def __len__(self): return len(self.playlist) def size(self): return len(self.playlist) def insert_raw(self, cm, index, user: User = None, keyword="") -> PlaylistAppendEvent: event = self.append_raw(cm, user=user, keyword=keyword) if event.isCancelled(): return event self.move(event.index, index) return event def append_raw(self, cm, user: User = None, keyword="") -> PlaylistAppendEvent: if user is None: return self.append(AudioItem(cm, PlaylistUser, keyword)) else: return self.append(AudioItem(cm, user, keyword)) def insert(self, index: int, item: AudioItem) -> PlaylistAppendEvent: event = self.append(item) if event.isCancelled(): return event self.move(event.index, index) return event def append(self, item: AudioItem) -> PlaylistAppendEvent: event = PlaylistAppendEvent(self, item, self.size()) self.handlers.call(event) if event.isCancelled(): return event self.playlist.append(item) self.handlers.call(PlaylistUpdateEvent(self)) return event def get(self, index) -> AudioItem: if index >= len(self.playlist) or index < 0: return return self.playlist[index] def clear(self): self.playlist.clear() self.handlers.call(PlaylistUpdateEvent(self)) @_trigger_event_handler def pop_first(self): if len(self.playlist) == 0: return None return self.playlist.pop(0), PlaylistUpdateEvent(self) @_trigger_event_handler def remove(self, index): if index >= len(self.playlist) or index < 0: return return self.playlist.pop(index), PlaylistUpdateEvent(self) @_trigger_event_handler def move(self, index, target_index): if index >= len(self.playlist) or index < 0: return if target_index < 0: target_index = 0 if target_index >= len(self.playlist): target_index = len(self.playlist) - 1 if index == target_index: return step = int((target_index - index) / abs(target_index - index)) tmp = self.playlist[index] for i in range(index, target_index, step): self.playlist[i] = self.playlist[i + step] self.playlist[target_index] = tmp return PlaylistUpdateEvent(self) def get_next(self) -> AudioItem: ''' get next AudioItem, if not exists return None :return: AudioItem ''' if len(self.playlist) == 0: return index = 0 if self.random_next: index = random.randint(0, len(self.playlist) - 1) self.current_index = index else: index = self.current_index self.current_index += 1 if self.current_index >= self.size(): self.current_index = 0 return self.playlist[index]
def __init__(self, audio_bot): self.audio_bot = audio_bot self.blacklist_items: List[BlacklistItem] = [] self.handlers = AudioBotHandlers() self.__register_handlers() self.load()
class Blacklist(): def __init__(self, audio_bot): self.audio_bot = audio_bot self.blacklist_items: List[BlacklistItem] = [] self.handlers = AudioBotHandlers() self.__register_handlers() self.load() def remove(self, index): if index >= len(self.blacklist_items) or index < 0: return val = self.blacklist_items.pop(index) self.handlers.call(BlacklistUpdateEvent(self)) return val def append(self, bantype: Union[str, BlacklistItemType], content, whole=None): if isinstance(bantype, str): bantype = BlacklistItemType.getByName(bantype) if bantype is None: return self.appendItem(BlacklistItem(bantype, content, whole=whole)) def appendItem(self, item: BlacklistItem): self.blacklist_items.append(item) self.handlers.call(BlacklistUpdateEvent(self)) def appendPlaylistItem(self, item: AudioItem): self.append(BlacklistItemType.SONG_NAME, item.source.getTitle()) self.append(BlacklistItemType.SONG_ID, item.source.getUniqueId()) def load(self): config = Config.blacklist for identifier, vals in config.items(): bantype = BlacklistItemType.getByName(identifier) if bantype is None: continue for val in vals: self.blacklist_items.append( BlacklistItem(bantype, val["content"], whole=val["whole"])) self.handlers.call(BlacklistLoadedEvent(self)) def dump(self): retval = dict( (member.identifier, []) for member in BlacklistItemType.values()) for item in self.blacklist_items: retval[item.bantype.identifier].append({ "content": item.content, "whole": item.whole }) return retval def __register_handlers(self): self.audio_bot.user_playlist.handlers._register( PlaylistAppendEvent, "blacklist.preventblacklistsong", self.__check_blacklist) self.handlers._register(BlacklistUpdateEvent, "blacklist.writetoconfig", lambda x: self.__write_to_config()) def __write_to_config(self): Config.blacklist = self.dump() def __check_blacklist(self, event: PlaylistAppendEvent): if event.isCancelled(): return for bl in self.blacklist_items: if bl.applicable(event.item): event.setCancelled(True) return
def __init__(self, audiobot): self.audiobot = audiobot self.lyrics: List[LyricItem] = [] self.previous = None self.handlers = AudioBotHandlers()
from typing import List, Type from audiobot.handler import AudioBotHandlers from audiobot.audio import AudioItem from audiobot.event.audiobot import FindSearchResultEvent from sources.audio import BiliAudioSource, NeteaseMusicSource from sources.audio.kuwo import KuwoMusicSource from sources.base import CommonSource from sources.base.interface import SearchableSource SEARCH_ENGINE_LIST: List[SearchableSource.__class__] = [ BiliAudioSource, KuwoMusicSource, NeteaseMusicSource ] DEFAULT_SEARCH_ENGINE = NeteaseMusicSource HANDLERS = AudioBotHandlers() def check(item: AudioItem): source = item.source if source == None: return item if isinstance(source, NeteaseMusicSource): item.source = matchNetease(source, keyword=item.keyword) return item return item def search(url, source_class: CommonSource.__class__): if source_class == None: source_class = DEFAULT_SEARCH_ENGINE