コード例 #1
0
ファイル: main.py プロジェクト: kstep/ajenti-mpd
    def worker(self):
        waiter = MPD()
        connected = True

        while True:
            events = waiter.wait((
                MPD.EV_QUEUE_CHANGED,
                MPD.EV_PLAYLIST_CHANGED,
                MPD.EV_VOLUME_CHANGED,
                MPD.EV_OPTIONS_CHANGED,
                MPD.EV_PLAYER_CHANGED,
                MPD.EV_OUTPUT_CHANGED))

            if events is None:
                if connected:
                    self.context.notify('error', 'Could not connect to MPD!')
                    self.context.launch('configure-plugin', plugin=self)
                    connected = False

                sleep(10)

            else:
                connected = True
                self.refresh(events)
コード例 #2
0
ファイル: main.py プロジェクト: kstep/ajenti-mpd
    def init(self):
        self._mpd = MPD(**self.classconfig)

        # meta-data
        self.title = 'MPD'
        self.icon = 'music'
        self.category = _("Software")

        self.append(self.ui.inflate('mpd:main'))

        # data-bindings
        self.status = Status.EMPTY
        self.song = Song.EMPTY

        self.playlist = []
        self.playlists = []
        self.outputs = []

        self.taxonomy = Taxonomy.EMPTY
        self.library = []

        self.init_ui_bindings()
コード例 #3
0
ファイル: main.py プロジェクト: kstep/ajenti-mpd
class MpdPlugin(SectionPlugin):
    default_classconfig = {'host': '127.0.0.1', 'port': 6600}
    classconfig_editor = MpdPluginConfigurator

    def bindui(self, ui_id, method_name):
        def decorator(method):
            setattr(self.find(ui_id), method_name, method)
            return method
        return decorator

    def init(self):
        self._mpd = MPD(**self.classconfig)

        # meta-data
        self.title = 'MPD'
        self.icon = 'music'
        self.category = _("Software")

        self.append(self.ui.inflate('mpd:main'))

        # data-bindings
        self.status = Status.EMPTY
        self.song = Song.EMPTY

        self.playlist = []
        self.playlists = []
        self.outputs = []

        self.taxonomy = Taxonomy.EMPTY
        self.library = []

        self.init_ui_bindings()


    def init_ui_bindings(self):
        @self.bindui('playlist', 'post_item_bind')
        def post_item_bind(obj, collection, item, ui):
            ui.find('play').on('click', self.play, item)

        @self.bindui('playlist', 'delete_item')
        def delete_item(item, collection):
            self.remove(item)

        @self.bindui('playlists', 'add_item')
        def add_playlist(item, collection):
            for i in count(1):
                try:
                    self._mpd.do('save', 'Untitled %d' % i)
                except CommandError:
                    continue
                else:
                    break

            self.refresh()

        @self.bindui('playlists', 'delete_item')
        def delete_playlist(item, collection):
            self._mpd.do('rm', item.playlist)
            self.refresh()

        @self.bindui('playlists', 'post_item_bind')
        def post_playlist_bind(obj, collection, item, ui):
            ui.find('load').on('click', lambda *args: self._mpd.do(*args) or self.refresh(), 'load', item.playlist)
            #ui.find('clear').on('click', self._mpd.do, 'playlistclear', item.playlist)

        @self.bindui('outputs', 'post_item_bind')
        def post_output_bind(obj, collection, item, ui):
            ui.find('enabled').on('click', self.toggleoutput, item)

        @self.bindui('library', 'post_item_bind')
        def post_library_bind(obj, collection, item, ui):
            ui.find('add').on('click', self.add, item.file)

    @on('tabs', 'switch')
    def tab_switch(self):
        if self.find('tabs').active == 2:  # library tab
            self.taxonomy = Taxonomy(izip(
                        ('artists', 'albums', 'genres'),
                        self._mpd.bulk_do([
                            ('list', 'artist'),
                            ('list', 'album'),
                            ('list', 'genre')
                            ], defaults=([], [], []))))
            self.binder.populate()


    @on('search', 'click')
    def search(self, add=False):
        self.binder.update()

        filter = []
        for f in ('artist', 'album', 'genre'):
            if self.taxonomy[f] is not None:
                filter.append(f)
                filter.append(self.taxonomy[f])

        if add and not filter:
            self.context.notify('error', _('Can not add all library! Select some filters first.'))
            return

        self.library = map(Song, (self._mpd.do('findadd' if add else 'find', *filter) or []) if filter
                else ifilter(lambda s: 'file' in s, self._mpd.do('listallinfo') or []))

        self.binder.populate()

    @on('searchadd', 'click')
    def searchadd(self):
        self.search(add=True)
        self.refresh()

    @on('rename', 'click')
    def rename_playlists(self):
        old_names = map(lambda p: p.playlist, self.playlists)
        self.binder.update()
        new_names = map(lambda p: p.playlist, self.playlists)

        self._mpd.bulk_do(
                imap(lambda p: ('rename',) + p,
                    ifilter(lambda p: p[0] != p[1],
                        izip(old_names, new_names))))

        self.refresh()


    def on_first_page_load(self):
        self.binder = Binder(self, self.find('mpd'))
        self.refresh()
        self.context.session.spawn(self.worker)

    def worker(self):
        waiter = MPD()
        connected = True

        while True:
            events = waiter.wait((
                MPD.EV_QUEUE_CHANGED,
                MPD.EV_PLAYLIST_CHANGED,
                MPD.EV_VOLUME_CHANGED,
                MPD.EV_OPTIONS_CHANGED,
                MPD.EV_PLAYER_CHANGED,
                MPD.EV_OUTPUT_CHANGED))

            if events is None:
                if connected:
                    self.context.notify('error', 'Could not connect to MPD!')
                    self.context.launch('configure-plugin', plugin=self)
                    connected = False

                sleep(10)

            else:
                connected = True
                self.refresh(events)

    def add(self, *urls):
        if not urls:
            return

        def readfile(url):
            with closing(urlopen(url) if url.startswith(('http://', 'https://')) else open(url, 'r')) as f:
                return f.read().splitlines()

        clean_playlist = lambda lines: ifilter(None, imap(str.strip, imap(str, lines)))
        parse_null = lambda url: (url,)

        parsers = {
                '.m3u': lambda url: ifilter(lambda line: not line.startswith('#'), readfile(url)),
                '.pls': lambda url: imap(lambda line: line.split('=')[1], ifilter(lambda line: line.startswith('File'), readfile(url))),
                }

        urls = flatten(imap(lambda url: clean_playlist(parsers.get(os.path.splitext(url)[1], parse_null)(url)), urls))

        cmds = imap(lambda url: ('addid', url), urls)

        try:
            self._mpd.bulk_do(cmds)

        except CommandError:
            self.context.notify('error', _('Songs not found'))

        else:
            self.refresh()

    @on('consume', 'click')
    def toggleconsume(self):
        self._mpd.do('consume', int(not self.status.consume))
        self.refresh()

    @on('single', 'click')
    def togglesingle(self):
        self._mpd.do('single', int(not self.status.single))
        self.refresh()

    @on('random', 'click')
    def togglerandom(self):
        self._mpd.do('random', int(not self.status.random))
        self.refresh()

    @on('repeat', 'click')
    def togglerepeat(self):
        self._mpd.do('repeat', int(not self.status.repeat))
        self.refresh()

    @on('add', 'click')
    def open_add_dialog(self):
        self.find('add_dialog').visible = True

    @on('add_dialog', 'button')
    def submit_add_dialog(self, button):
        dialog = self.find('add_dialog')
        dialog.visible = False
        if button == 'add':
            self.add(*dialog.find('new_song_url').value.strip().splitlines())

    @on('refresh', 'click')
    def refresh(self, areas=()):
        if not areas:
            update = ['playlistinfo', 'status', 'currentsong', 'listplaylists', 'outputs']
            defaults = [[], Status.EMPTY, Song.EMPTY, [], []]

        else:
            update = list(set(ifilter(bool, flatten(
                ('playlistinfo',) if area in (MPD.EV_QUEUE_CHANGED,) else
                ('listplaylists',) if area in (MPD.EV_PLAYLIST_CHANGED,) else
                ('status',) if area in (MPD.EV_VOLUME_CHANGED, MPD.EV_OPTIONS_CHANGED) else
                ('status', 'currentsong') if area in (MPD.EV_PLAYER_CHANGED,) else
                ('outputs',) if area in (MPD.EV_OUTPUT_CHANGED,) else
                None
                for area in areas))))

            defaults = map(lambda n: {} if n in ('status', 'currentsong') else [], update)

        data = UpdateInfo(izip(update,
            self._mpd.bulk_do(*update, defaults=defaults)))

        for key in update:
            setattr(self, {
                'playlistinfo': 'playlist',
                'listplaylists': 'playlists',
                'currentsong': 'song',
                }.get(key, key),
                getattr(data, key))

        if 'status' in update:
            # reset icons if not all playlist is to be updated
            if 'playlistinfo' not in update:
                for song in self.playlist:
                    song.icon = 'music'

            try:
                if self.status.state == 'play':
                    self.find('play').visible = False
                    self.find('pause').visible = True
                    self.playlist[self.status.song].icon = 'play'

                else:
                    self.find('play').visible = True
                    self.find('pause').visible = False

            except AttributeError:
                pass

        self.binder.populate()

    def toggleoutput(self, output):
        self._mpd.do('disableoutput' if output.outputenabled else 'enableoutput', output.outputid)
        self.refresh()

    @on('voldown', 'click')
    def voldown(self, delta=10):
        self._mpd.do('volume', -delta)
        self.refresh()

    @on('volup', 'click')
    def volup(self, delta=10):
        self._mpd.do('volume', delta)
        self.refresh()

    _last_volume = 0
    @on('mute', 'click')
    def mute(self):
        if self.status.volume == 0:
            self.volume(self._last_volume)
        else:
            self._last_volume = self.status.volume
            self.volume(0)

    def volume(self, value):
        self._mpd.do('setvol', value)
        self.refresh()

    def remove(self, song):
        self._mpd.do('deleteid', song.id)
        self.refresh()

    #@on('reorder', 'click')
    #def reorder(self):
        #self.binder.update()

    @on('update', 'click')
    def update(self):
        num = self._mpd.do('update')
        if num:
            self.context.notify('info', _('Update #%s started...') % num)

    @on('play', 'click')
    def play(self, song=None):
        if song is None:
            self._mpd.do('play')
        else:
            self._mpd.do('playid', song.id)

        self.refresh()

    @on('pause', 'click')
    def pause(self):
        self._mpd.do('pause')
        self.refresh()

    @on('stop', 'click')
    def stop(self):
        self._mpd.do('stop')
        self.refresh()

    @on('prev', 'click')
    def prev(self):
        self._mpd.do('previous')
        self.refresh()

    @on('next', 'click')
    def next(self):
        self._mpd.do('next')
        self.refresh()

    @on('clear', 'click')
    def clear(self):
        self._mpd.do('clear')
        self.refresh()