예제 #1
0
 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
예제 #2
0
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))
예제 #3
0
 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()
예제 #4
0
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()
예제 #5
0
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]
예제 #6
0
 def __init__(self, audio_bot):
     self.audio_bot = audio_bot
     self.blacklist_items: List[BlacklistItem] = []
     self.handlers = AudioBotHandlers()
     self.__register_handlers()
     self.load()
예제 #7
0
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
예제 #8
0
 def __init__(self, audiobot):
     self.audiobot = audiobot
     self.lyrics: List[LyricItem] = []
     self.previous = None
     self.handlers = AudioBotHandlers()
예제 #9
0
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