Exemplo n.º 1
0
class Mpd_factory(ReconnectingClientFactory, Client):
    '''
    Twisted Reconnecting client factory for mpd server
    convert all UPnP and OpenHome service function to mpd remote calls
    '''

    _mtlist = ''
    _muted = True
    _rate = "1"
    _state = "pending"
    _track_duration = '0:00:00.000'
    ohduration = 0
    _track_URI = ''
    _volume = 100
    active_source = 0
    attributes = 'Info Time Volume'
    balance = 0
    balancemax = 10
    bitdepth = 0
    bitrate = 0
    cancelplay = False
    codecname = 'MP3'
    counter = 0
    detailscount = 0
    fade = 0
    fademax = 10
    idArray = ''
    lossless = False
    maxid = 0
    max_volume = 100
    metadata = {}
    metadata_str = 'NOT_IMPLEMENTED'
    metatext = ''
    metatextcount = 0
    mimetypes = [
        'audio/aac', 'audio/mp4', 'audio/mpeg', 'audio/mpegurl',
        'audio/vnd.rn-realaudio', 'audio/vorbis', 'audio/x-flac',
        'audio/x-mp3', 'audio/x-mpegurl', 'audio/x-ms-wma', 'audio/x-musepack',
        'audio/x-oggflac', 'audio/x-pn-realaudio', 'audio/x-scpls',
        'audio/x-speex', 'audio/x-vorbis', 'audio/x-wav',
        'application/x-ogm-audio', 'audio/x-vorbis+ogg', 'audio/ogg'
    ]
    mtlist = ''
    name = "MpdPlayer"
    oh_state = 'Buffering'
    old_vol = 0
    parent = None
    reltime = '0:00:00.000'
    repeat = False
    room = 'Home'
    samplerate = 0
    seconds = 0
    shuffle = False
    songid = 0
    sources = ['Playlist']
    sourceindex = 0
    upnp_state = "TRANSITIONNING"
    volumeunity = 3
    volumemillidbperstep = 600
    timer = None
    token = 0
    trackcount = 0
    tracksmax = 0
    transport_actions = ['PLAY']

    #     ohduration = 0

    def __init__(self, cover_dir, net_type='lan'):

        self.connected = False
        self.net_type = net_type
        self.proto = MpdProtocol()
        self.proto._event = self.dispatch_event
        self.status = {'state': 'stop'}
        self.playlist = Playlist(self)
        self.waiting = False
        self.calls = []
        self.cover_dir = cover_dir
        self._sources = [
            OrderedDict(
                sorted({
                    'Name': '_'.join((self.name, n)),
                    'Type': n,
                    'Visible': True
                }.items(),
                       key=lambda t: t[0])) for n in self.sources
        ]
        self.sourcexml = dict2xml(
            {'SourceList': [{
                'Source': n
            } for n in self._sources]})
        self.protocolinfo = self.mtlist = 'http-get:*:' + ''\
            .join([':*,http-get:*:'.join(self.mimetypes)]) + ':*'

    def buildProtocol(self, addr):
        return self.proto

    '''
    Internal functions
    '''

    def dispatch_event(self, events=None):
        def set_connected():
            self.connected = True
            p = self.call('plchanges', self.playlist.plsversion)
            p.addCallback(self.playlist.update)

        d = None
        for evt in events:
            if evt == 'Error':
                log.error('bad event: {name}', name=events[evt])
                return
            elif evt == 'changed':
                chg = events[evt]
            else:
                #                 log.msg(events[evt])
                return
            if chg == 'disconnected':
                self.connected = False
                continue
            if chg == 'playlist':
                log.error('update from: %s' % self.playlist.plsversion)
                d = self.call('plchanges', self.playlist.plsversion)
                d.addCallback(self.playlist.update)
            else:
                d = self.call('status')
                if chg == 'connected':
                    d.addCallback(self.filter_event)
                    reactor.callLater(1, set_connected)  # @UndefinedVariable
                elif chg == 'mixer':
                    d.addCallback(self.filter_event, 'volume')
                elif chg == 'player':
                    d.addCallback(self.filter_event)
            return d

    def filter_event(self, data, filtr=None):
        if data is None:
            return
        if not isinstance(data, list):
            data = [data]
        if filtr is not None:
            for d in data:
                self.status.update(d)
            self.changed_state({filtr: self.status[filtr]})
        else:
            dic = {}
            for d in data:
                for it in d.iteritems():
                    if it not in self.status.items():
                        self.status.update(dict([it]))
                        dic.update(dict([it]))
                        if it[0] == 'playlistlength':
                            self.playlist.length = int(it[1])
            self.changed_state(dic)

    def call(self, command=None, params=None):
        #         log.error('register %s' % command)
        d = defer.Deferred()
        if command is not None:
            self.proto.addCallback(d)
        if not self.connected:
            if command:
                self.calls.append((command, params))
        else:
            if len(self.calls) > 0:
                if command:
                    self.calls.append((command, params))
                else:
                    d = None
                command, params = self.calls.pop(0)
            if params is not None:
                c = ' '.join([command, str(params)]).encode('utf-8')
            else:
                c = command.encode('utf-8')
            if self.proto.idle:
                self.proto.noidle()
            self.proto.sendLine(c)
            #             log.error('send %s' % c)
            if len(self.calls) > 0:
                self.call()
        return d

    def changed_state(self, state):
        changed = state.keys()
        if 'state' in changed:
            self.set_state(state['state'])
        if "volume" in changed:
            log.debug('volume changed')
            vol = int(state['volume'])
            if vol != self._volume:
                if vol != 0:
                    self._volume = vol
                    muted = False
                else:
                    muted = True
                log.debug('send volume')
                self.oh_eventVOLUME(self._volume, 'volume')
                self.upnp_eventRCS(self._volume, 'volume')
                if muted is not self._muted:
                    self._muted = muted
                    self.upnp_eventRCS(self._muted, 'mute')
                self.oh_eventVOLUME(int(self._muted), 'mute')

        if 'songid' in changed:
            self.playlist.current_track(state['songid'])
        if 'repeat' in changed:
            #             log.debug('************repeat***********: %s' %
            #                 bool(int(state['repeat'])))
            if self.repeat != bool(int(state['repeat'])):
                self.repeat = bool(int(state['repeat']))
                if not self.shuffle:
                    self.upnp_eventAV(
                        'REPEAT_ALL' if self.repeat else 'NORMAL',
                        'currentplayMode')
                else:
                    self.upnp_eventAV(
                        'REPEAT_ALL SHUFFLE' if self.repeat else
                        'NORMAL SHUFFLE', 'currentplaymode')
                self.oh_eventPLAYLIST(self.repeat, 'repeat')
        if 'random' in changed:
            log.debug('************shuffle***********: %s' %
                      bool(int(state['random'])))
            if self.shuffle != bool(int(state['random'])):
                self.shuffle = bool(int(state['random']))
                if not self.repeat:
                    self.upnp_eventAV(
                        'NORMAL SHUFFLE' if self.shuffle else 'NORMAL',
                        'currentplaymode')
                else:
                    self.upnp_eventAV(
                        'REPEAT_ALL SHUFFLE' if self.shuffle else
                        'NORMAL SHUFFLE', 'currentplaymode')
                self.oh_eventPLAYLIST(self.shuffle, 'shuffle')
        if 'elapsed' in changed:
            if self.timer is not None:
                self.timer.set(float(state['elapsed']))
        if 'playlist' in changed:
            self.token = int(state['playlist'])
#             self.playlist.plsversion = state['playlist']
        if 'playlistdata' in changed:
            self.playlist.update(state['playlistdata'])
        if 'bitrate' in changed:
            self.detailscount += 1
            self.bitrate = int(state['bitrate'])
            self.oh_eventINFO(self.bitrate, 'bitrate')
        if 'audio' in changed:
            try:
                sr = int(state['audio'].split(':')[0])
                self.samplerate = sr
                self.oh_eventINFO(sr, 'samplerate')
            except:
                log.critical('Bad Samplerate: %s' %
                             state['audio'].split(':')[0])
            try:
                bd = int(state['audio'].split(':')[1])
                self.bitdepth = bd
                self.oh_eventINFO(bd, 'bitdepth')
            except:
                log.critical('Bad Bitdepth: %s' % state['audio'].split(':')[1])

    def update_state(self):
        #  log.err('Update State: %s' % self.mpd.status['state'])
        self.set_state(self.status['state'])

    def update_mimetypes(self):
        self.set_mimetypes(self.mimetypes)

    def upnp_eventAV(self, evt, var):
        pass

    def upnp_eventCM(self, evt, var):
        pass

    def upnp_eventRCS(self, evt, var):
        pass

    def oh_eventPLAYLIST(self, evt, var):
        pass

    def oh_eventINFO(self, evt, var):
        pass

    def oh_eventTIME(self, evt, var):
        pass

    def oh_eventVOLUME(self, evt, var):
        pass

    def getMetadata(self):
        return self.call('playlistid', self.status['songid'])

    def getMimeTypes(self):
        return self.call('decoders')

    def get_state(self):
        return self._state

    def get_rate(self):
        return self._rate

    def get_track_duration(self):
        try:
            duration = self._track_duration
        except:
            duration = '00:00:00'
        return duration

    def get_track_URI(self):
        try:
            uri = self._track_URI
        except:
            uri = ''
        return uri

    def get_track_md(self):
        return self.metadata_str

    def get_sticker(self, url, dic={}):
        def got_sticker(stklist, dic):
            if not isinstance(stklist, list):
                stklist = [stklist]
            for sticker in stklist:
                try:
                    dic.update({
                        sticker['sticker'].split('=')[0],
                        sticker['sticker'].split('=')[1]
                    })
                except:
                    continue
            return dic

        d = self.call('sticker', ' '.join(('list song', url.join('"' * 2))))
        d.addBoth(got_sticker, dic)
        return d

    def get_relcount(self):
        return 2147483647

    def get_abscount(self):
        return 2147483647

    def get_abstime(self):
        return '00:00:00'

    def get_reltime(self, fmt='UPNP'):
        if self.timer is not None:
            if fmt == 'UPNP':
                t = mpdtime_to_upnptime(self.timer.get())
            elif fmt == 'seconds':
                t = int(self.timer.get())
            else:
                # msec
                t = self.timer.get()
        else:
            if fmt == 'UPNP':
                t = self.reltime
            else:
                t = self.seconds
        return t

    def get_volume(self):
        def noVolume(err):
            if self._muted:
                return 0
            else:
                return self._volume

        def convert_volume(vol):
            self._volume = int(float(vol) * 100)
            return self._volume

        d = self.mediaplayer.get("Volume",
                                 interf='org.mpris.MediaPlayer2.Player')
        d.addCallbacks(convert_volume, noVolume)
        return d

    def get_transport_actions(self):
        return {','.join(self.transport_actions)}

    def set_mimetypes(self, mtlist):
        if self._mtlist != mtlist:
            self._mtlist = mtlist
            self.mtlist = 'http-get:*:' + ''\
                .join([':*,http-get:*:'.join(mtlist)]) + ':*'
            self.upnp_eventCM(self.mtlist, 'sinkprotocolinfo')
            self.oh_eventPLAYLIST(self.mtlist, 'protocolinfo')

    def set_metadata(self, metadata):
        if metadata != self.metadata:
            self.metadata.update(metadata)
            if 'duration' in metadata.keys():
                self._track_duration = metadata['duration']
            if 'url' in metadata.keys():
                self._track_URI = metadata['url']
            if 'file' in metadata.keys():
                self._track_URI = metadata['file']

    def set_reltime(self, t):
        self.seconds = int(float(t))
        self.reltime = mpdtime_to_upnptime(t)
        #         log.err('seconds: %s' % t)
        self.timer = None

    def set_state(self, state):
        log.debug("SET NEW STATE : %s " % state)
        if state == self._state:
            return
        self._state = state
        if state == 'pause':
            #             print(self.transport_actions)
            self.transport_actions = ['PLAY', 'STOP']
            #                 self.transport_actions.remove('PAUSE')
            #                 self.transport_actions.append('PLAY')
            if self.timer is not None:
                self.timer.stop()
                d = self.call('status')
                d.addCallback(lambda st: st['elapsed'])
                d.addCallbacks(
                    self.set_reltime,
                    lambda ignored: self.set_reltime(self.timer.get()))
            self.upnp_state = 'PAUSED_PLAYBACK'
            self.oh_state = 'Paused'
        elif state == 'play':
            self.transport_actions = ['STOP', 'PAUSE', 'SEEK']
            self.changed_state({'volume': self.status['volume']})
            self.timer = Timer()
            self.timer.set(self.seconds)
            d = self.call('status')
            d.addCallbacks(lambda st: st['elapsed'])
            d.addCallbacks(lambda t: self.timer.set(float(t)),
                           lambda ignored: self.timer.set(0.000))
            self.upnp_state = 'PLAYING'
            self.oh_state = 'Playing'
        elif state == 'stop':
            self.transport_actions = ['PLAY']
            self.set_reltime(0)
            self.upnp_state = 'STOPPED'
            self.oh_state = 'Stopped'
        elif state == 'pending':
            self.upnp_state = 'TRANSITIONNING'
            self.oh_state = 'Buffering'
        else:
            return
        self.upnp_eventAV(self.upnp_state, 'transportstate')
        self.oh_eventPLAYLIST(self.oh_state, 'transportstate')

    def set_songid(self, songid):
        self.songid = songid['Id']
        return self.songid

    def set_position(self, newpos, fmt='UPNP'):
        def transition(obj):
            current_state = self._state
            self.set_state('pending')
            reactor.callLater(
                0.5,  # @UndefinedVariable
                self.set_state,
                current_state)

        if fmt == 'UPNP':
            pos = upnptime_to_mpdtime(newpos)
        else:
            pos = newpos
        log.debug('seek to %s' % pos)
        d = self.call('seekcur', pos)
        d.addCallback(transition)
        return d

    def set_position_relative(self, delta, fmt='UPNP'):
        newpos = int(self.get_reltime('Seconds')) + int(delta)
        self.set_position(newpos, fmt)

    def set_track_URI(self, uri, md=''):
        log.debug("set track uri : %s " % uri)
        try:
            log.debug("current uri : %s " % self._track_URI)
        except:
            pass
        if uri != self._track_URI:
            self._track_URI = uri
            self.metadata_str = md
            self.set_metadata(didl_decode(md.encode('utf-8')))
            d = self.call('addid', uri)
            d.addCallback(self.set_songid)
            d.addCallback(self.play)

    def playing(self, *ret):
        log.debug('playing...')
        self.set_state('play')

    def playindex(self, index):
        return self.play(songid=self.playlist[int(index)])

    def playpause(self):
        if self._state == 'pause':
            return self.play()
        else:
            return self.pause()

    def insert_metadata(self, md):
        dic = didl_decode(md)
        #             log.err(dic)
        for i, tag in enumerate(dic.keys()):
            if tag.lower() in ('class', 'restricted', 'id', 'duration',
                               'parentid', 'protocolinfo', 'url', 'ownerudn'):
                continue
            if not isinstance(dic[tag], str):
                continue
            reactor.callLater(  # @UndefinedVariable
                float(i) / 2, self.call, ' '.join(
                    ('sticker', 'set song', dic['url'].join('"' * 2), tag,
                     '"' + dic[tag] + '"')))

    def delete(self, songid):
        self.call('deleteid', str(songid))

    def clear(self):
        self.call('clear')

    def volUp(self):
        if self._volume == 0:
            vol = self.r_get_volume()
            try:
                newvol = vol + 5
            except:
                newvol = self._volume + 5
            return self.r_set_volume(newvol, 'Master')
        else:
            if self._volume > 95:
                return
            newvol = self._volume + 5
            return self.r_set_volume(newvol, 'Master')

    def volDown(self):
        if self._volume > 5:
            newvol = self._volume - 5
            return self.r_set_volume(newvol, 'Master')
        else:
            newvol = self._volume
            self._volume = 0
            return self.r_set_volume(newvol, '0')

    '''
    UPNP wrapped functions
    '''

    # onDemand vendor method

    def r_get_room(self):
        return self.room

    '''
    AVTransport and OpenHome Playlist
    '''

    def r_delete_all(self):
        return self.clear()

    def r_delete_id(self, value):
        return self.delete(value)

    def r_get_current_transport_actions(self, instanceID):
        return self.get_transport_actions()

    def r_get_media_info(self, instanceID):
        return (
            str(len(self.playlist)),
            self.get_track_duration(),
            self.get_track_URI(),
            self.get_track_md(),
            '',
            '',
            'UNKNOWN',
            'UNKNOWN',
            'UNKNOWN',
        )

    def r_get_media_info_ext(self, instanceID):
        return (
            str(len(self.playlist)),
            'TRACK_AWARE',
            self.get_track_duration(),
            self.get_track_URI(),
            self.get_track_md(),
            '',
            '',
            'UNKNOWN',
            'UNKNOWN',
            'UNKNOWN',
        )

    def r_get_position_info(self, instanceID):
        return (
            self.player.get_track(),
            self.get_track_duration(),
            self.get_track_md(),
            self.player.get_track_URI(),
            self.player.get_reltime(),
            self.player.get_abstime(),
            self.player.get_relcount(),
            self.player.get_abscount(),
        )

    def r_get_transport_info(self, instanceID):
        return (
            self.get_state(),
            'OK',
            self.get_rate(),
        )

    def r_id(self):
        return self.songid

    def r_id_array(self):
        return (
            self.token,
            self.idArray,
        )

    def r_id_array_changed(self, token):
        if token != self.token:
            return 1
        return 0

    def r_insert(self, afterid, url, metadata, checked=False):
        log.debug('Insert :%s  --  %s  --  %s' % (afterid, url, metadata))

        def inserted(res, md):
            log.debug(res)
            return res['Id']

        if 'youtube' in url and not checked:
            # eclipse workaround !!!
            y = os.environ
            y.update({'PYTHONPATH': '/usr/bin/python'})
            # /eclipse workaround
            d = utils.getProcessOutput('/usr/bin/youtube-dl',
                                       ['-g', '-f', 'bestaudio', url],
                                       env=y,
                                       reactor=reactor)
            d.addCallback(lambda u: self.insert(
                u.split('\n')[0], afterid, metadata, True))
            return d
        if len(self.playlist.tracks) == 0:
            d = self.call('addid', url)
        elif int(afterid) == 0:
            d = self.call('addid', url + ' 0')
        else:
            log.critical('crash ? %s' % self.playlist.tracks)
            log.critical('here ? %s' %
                         str(self.playlist.tracks.index(int(afterid)) + 1))
            d = self.call(
                'addid', ' '.join(
                    (url.encode('utf-8'),
                     str(self.playlist.tracks.index(int(afterid)) + 1))))
        d.addCallback(inserted, metadata)
        return d

    def r_next(self, instanceID=0):
        if self._state not in ('play', 'pause'):
            if self.songid == 0:
                self.r_play(1)
            else:
                self.r_play()
        else:
            self.call('next')

    def r_play(self, instanceID=0, speed=1, songid=None, ignored=None):

        log.debug('entering play...')

        def success(result):
            return None

        if self.cancelplay:
            self.cancelplay = False
        else:
            if songid is not None:
                #                 log.err(songid)
                d = self.call('playid', str(songid))
            else:
                if self._state == 'pause':
                    d = self.call('pause', '0')
                else:
                    d = self.call('playid', self.songid)
            d.addCallback(self.playing)

    def r_pause(self, instanceID=0):
        print('pause')

        def paused(ret):
            if self._state == 'play':
                self.set_state('pause')

        d = self.call('pause', '1')
        d.addCallback(paused)
        return d

    def r_previous(self, instanceID=0):
        self.call('previous')

    def r_protocol_info(self):
        return self.protocolinfo

    def r_read(self, value):
        log.debug('read')
        d = self.playlist.get_track(value)
        return (d, )

    def r_read_list(self, items):
        log.debug('readlist')
        d = self.playlist.get_tracks([int(track) for track in items.split()])
        return d

    def r_repeat(self):
        return self.repeat

    def r_record(self, instanceID):
        raise NotImplementedError()

    def r_seek(self, instanceID, unit, pos):
        log.debug('seek: %s %s' % (unit, pos))
        self.set_position(pos)

    def r_seek_id(self, value):
        log.debug('SeekId')
        return self.r_play(songid=value)

    def r_seek_index(self, value):
        log.debug('Seekindex')
        return self.playindex(value)

    def r_seek_second_absolute(self, value):
        return self.set_position(value, 'SECONDS')

    def r_seek_second_relative(self, value):
        return self.set_position_relative(value, 'SECONDS')

    def r_set_repeat(self, repeat):
        self.call('repeat', str(int(repeat)))
        self.changed_state({'repeat': str(int(repeat))})

    def r_set_shuffle(self, shuffle):
        self.call('random', str(int(shuffle)))
        self.changed_state({'random': str(int(shuffle))})

    def r_set_avtransport_uri(self, instanceID, uri, uri_metadata):
        self.set_track_URI(uri, uri_metadata)

    def r_shuffle(self):
        return self.shuffle

    def r_stop(self, instanceID=0):
        def stopped(ret):
            self.set_state('stop')

        if self._state != 'STOPPED':
            d = self.call('stop')
            self.reltime = '00:00:00'
            d.addCallback(stopped)

    def r_tracks_max(self):
        return self.tracksmax

    def r_transport_state(self, instanceID=0):
        if self.parent.type == 'Source':
            return self.oh_state
        return self.upnp_state

    '''
    OpenHome Info
    '''

    def r_counters(self):
        return (
            self.trackcount,
            self.detailscount,
            self.metatextcount,
        )

    def r_track(self):
        return (
            self._track_URI,
            self.metadata_str,
        )

    def r_details(self):
        return (
            self.ohduration,
            self.bitrate,
            self.bitdepth,
            self.samplerate,
            self.lossless,
            self.codecname,
        )

    def r_metatext(self):
        return self.metatext

    '''
    Rendering Control and Open Home Volume
    '''

    def r_volume(self):
        return self._volume

    def r_set_volume(self, volume, channel=0):
        volume = str(volume)
        d = self.call('setvol', volume)
        d.addErrback(log.critical,
                     'Set Volume Error : %s - %d' % (channel, int(volume)))
        reactor.callLater(
            0.1,  # @UndefinedVariable
            self.changed_state,
            {'volume': str(volume)})

    def r_volume_inc(self):
        return self.volUp()

    def r_volume_dec(self):
        return self.volDown()

    def r_volume_limit(self):
        return self.max_volume

    def r_balance(self):
        return self.balance

    def r_balance_inc(self):
        # TODO
        self.balance += 1

    def r_balance_dec(self):
        # TODO
        self.balance -= 1

    def r_set_fade(self, value):
        # TODO
        self.fade = int(value)

    def r_fade_inc(self):
        # TODO
        self.fade += 1

    def r_fade_dec(self):
        # TODO
        self.fade -= 1

    def r_mute(self):
        return self._muted

    def r_set_mute(self, mute):
        if mute is not self._muted:
            self._muted = mute
            if mute:
                self.old_vol = self._volume
                self.r_set_volume('0')
            else:
                self.r_set_volume(self.old_vol)

    def r_characteristics(self):
        return self.max_volume, self.volumeunity, self.max_volume,\
            self.volumemillidbperstep, self.balancemax, self.fademax

    '''
    OpenHome Time
    '''

    def r_time(self):
        return (self.trackcount, self.ohduration, self.get_reltime('seconds'))

    '''
    OpenHome Product
    '''

    def r_manufacturer(self=None):
        log.debug('Manufacturer from Product')
        return (
            self.parent.manufacturer,
            self.parent.manufacturerInfo,
            self.parent.manufacturerURL,
            ''.join((self.parent.getLocation(get_default_v4_address()),
                     '/pictures/icon.png')),
        )

    def r_model(self=None):
        log.debug('Model from Product')
        return (self.parent.modelName, self.parent.modelDescription,
                self.parent.manufacturerURL, ''.join((
                    self.parent.getLocation(get_default_v4_address()),
                    '/pictures/',
                    self.parent.modelName,
                    '.png',
                )))

    def r_product(self):
        log.debug('Product from Product')
        return self.room, self.parent.modelName, self.parent.modelDescription,\
            self.parent.manufacturerURL,\
            ''.join((self.parent.getLocation(get_default_v4_address()),
                     '/pictures/', self.parent.modelName, '.png',))

    def r_standby(self):
        log.debug('Standby from Product')
        return self.standby

    def r_set_standby(self, val=None):
        log.debug('SetStandby from Product')
        if val is None:
            return self.standby
        raise NotImplementedError()

    def r_source_count(self):
        log.debug('SourceCount from Product')
        return len(self.sources)

    def r_source_xml(self, *args, **kwargs):
        log.debug('SourceXml from Product')
        return self.sourcexml


# return dict2xml({'SourceList': [{'Source': n} for n in self.sources]})

    def r_source_index(self):
        log.debug('SourceIndex from Product')
        return self.sourceindex

    def r_set_source_index(self, idx=None):
        log.debug('SetSourceIndex from Product')
        if idx is None:
            return self.sourceindex
        else:
            try:
                self.sourceindex = int(idx)
                self.oh_product_event('sourceindex', self.sourceindex)
            except:
                for i, source in enumerate(self.sources.keys()):
                    if source['Name'] == idx:
                        self.sourceindex = i
                        self.oh_product_event('sourceindex', self.sourceindex)
                        return
                    log.critical('Unknown Source: %s' % idx)

    def r_set_source_index_by_name(self, value):
        log.debug('SetSourceIndexByName from Product')
        return self.set_source_index(value)

    def r_source(self, idx):
        idx = int(idx)
        return (
            self.parent.sources[idx].friendlyName,
            self.parent.sources[idx].type,
            True,
            self.parent.sources[idx].name,
        )

    def r_attributes(self):
        return self.attributes

    def r_source_xml_change_count(self):
        raise NotImplementedError()
Exemplo n.º 2
0
class Mpdclient(internet.TCPClient):  # @UndefinedVariable
    '''
    classdocs
   '''
    _state = "pending"
    upnp_state = "TRANSITIONNING"
    oh_state = 'Buffering'
    _track_URI = ''
    _managed = False
    _errors = 0
    _metadata = {}
    _playlist = []
    _volume = 100
    _rate = "1"
    _mtlist = ''
    mtlist = ''
    _muted = True
    _track_duration = '0:00:00.000'
    name = "MpdPlayer"
    cancelplay = False
    reltime = '0:00:00.000'
    seconds = 0
    repeat = False
    shuffle = False
    counter = 0
    tracksmax = 0
    metadata = {}
    playlist = []
    idArray = ''
    maxid = 0
    metadata_str = 'NOT_IMPLEMENTED'
    songid = 0
    transport_actions = ['PLAY']
    timer = None
    token = 0
    max_volume = 100

    def __init__(self, addr='127.0.0.1', port='6600', **kwargs):
        '''
        Constructor
        '''
        self.addr = addr
        print addr
        print port
        self.port = int(port)
        self.mpd = MpdFactory(self.changed_state)
        internet.TCPClient.__init__(self,  # @UndefinedVariable
                                    addr,
                                    self.port,
                                    self.mpd)

    def startService(self):
        internet.TCPClient.startService(self)  # @UndefinedVariable
        self.update_state()
        self.update_metadata()
        self.update_mimetypes()

    def changed_state(self, state):
        changed = state.keys()
        if 'state' in changed:
            self.set_state(state['state'])
        if "volume" in changed:
            log.msg('volume changed', loglevel=logging.DEBUG)
            vol = int(state['volume'])
            if vol != self._volume:
                if vol != 0:
                    self._volume = vol
                    muted = False
                else:
                    muted = True
                log.msg('send volume', loglevel=logging.DEBUG)
                self.oh_eventVOLUME(self._volume, 'volume')
                self.upnp_eventRCS(self._volume, 'Volume')
                if muted is not self._muted:
                    self._muted = muted
                    self.upnp_eventRCS(self._muted, 'Mute')
                self.oh_eventVOLUME(int(self._muted), 'mute')

        if 'songid' in changed:
            self.update_metadata(state['songid'])
        if 'repeat' in changed:
            if self.repeat != bool(state['repeat']):
                self.repeat = bool(state['repeat'])
                if not self.shuffle:
                    self.upnp_eventAV(
                        'REPEAT_ALL' if self.repeat else 'NORMAL',
                        'CurrentPlayMode')
                else:
                    self.upnp_eventAV('REPEAT_ALL SHUFFLE' if self.repeat
                                      else 'NORMAL SHUFFLE', 'CurrentPlayMode')
                self.oh_eventPLAYLIST(self.repeat, 'repeat')
        if 'random' in changed:
            if self.shuffle != bool(state['repeat']):
                self.shuffle = bool(state['repeat'])
                if not self.repeat:
                    self.upnp_eventAV(
                        'NORMAL SHUFFLE' if self.shuffle else 'NORMAL',
                        'CurrentPlayMode')
                else:
                    self.upnp_eventAV(
                        'REPEAT_ALL SHUFFLE' if self.shuffle
                        else 'NORMAL SHUFFLE', 'CurrentPlayMode')
                self.oh_eventPLAYLIST(self.shuffle, 'shuffle')
        if 'elapsed' in changed:
            if self.timer is not None:
                self.timer.set(float(state['elapsed']))
        if 'playlist' in changed:
            self.token = int(state['playlist'])
        if 'playlistdata' in changed:
            self.update_playlist(state['playlistdata'])
        if 'bitrate' in changed:
            self.oh_eventINFO(int(state['bitrate']), 'bitrate')
        if 'audio' in changed:
            try:
                sr = int(state['audio'].split(':')[0])
                self.oh_eventINFO(sr, 'samplerate')
            except:
                log.err('Bad Samplerate: %s' % state['audio'].split(':')[0])
            try:
                bd = int(state['audio'].split(':')[1])
                self.oh_eventINFO(bd, 'bitdepth')
            except:
                log.err('Bad Bitdepth: %s' % state['audio'].split(':')[1])

    def update_state(self):
#         log.err('Update State: %s' % self.mpd.status['state'])
        self.set_state(self.mpd.status['state'])

    def update_playlist(self, newpl):
        self.playlist = newpl
        self.idArray = id_array(newpl)
        self.oh_eventPLAYLIST(self.idArray, 'idarray')
#         d.addCallback(self.oh_eventPLAYLIST, 'idarray')

    def update_metadata(self, songid=None):
        log.msg('update metadata')

        def getmd(md):
            if isinstance(md, list):
                nd = {}
                for d in md:
                    nd.update(d)
                md = nd
            if md != self._metadata:
                self._metadata = md
#                 log.err(md)
                self.metadata.update(mpd_decode(self._metadata))
                if self._track_duration != self.metadata['duration']:
                    self._track_duration = self.metadata['duration']
                    self.upnp_eventAV(self._track_duration,
                                      'CurrentTrackDuration')
                    sec = upnptime_to_mpdtime(self._track_duration)
                    self.oh_eventINFO(sec, 'duration')
                    self.oh_eventTIME(sec, 'duration')
                if self.songid != self.metadata['id']:
                    self.songid = self.metadata['id']
                    self.upnp_eventAV(int(self.songid), 'CurrentTrack')
                    self.oh_eventPLAYLIST(int(self.songid), 'id')
                    self.oh_eventTIME(1, 'trackcount')
                if 'url' in self.metadata.keys():
                    self._track_URI = self.metadata['url']
                    self.upnp_eventAV(self._track_URI, 'AVTransportURI')
                    self.oh_eventINFO(self._track_URI, 'uri')
                    self.upnp_eventAV(self._track_URI, 'CurrentTrackURI')
                    try:
                        self.oh_eventINFO(
                            self.metadata['codec'].upper(), 'codecname')
                        if self.metadata['codec'].lower() in ['flac', 'm4a']:
                            self.oh_eventINFO(1, 'lossless')
                        else:
                            self.oh_eventINFO(0, 'lossless')
                    except KeyError:
                        pass
                self.metadata_str = didl_encode(self.metadata)
                self.oh_eventINFO(self.metadata_str, 'metadata')
                self.upnp_eventAV(self.metadata_str, 'AVTransportURIMetaData')
                if self.tracksmax == 0:
                    self.tracksmax = 10000
                    self.oh_eventPLAYLIST(self.tracksmax, 'tracksmax')

        if songid is not None:
            d = self.mpd.call('playlistid', songid)
        else:
            d = self.mpd.call('currentsong')
        d.addCallback(getmd)

    def update_mimetypes(self):
        self.set_mimetypes(self.mpd.mimetypes)

    def upnp_eventAV(self, evt, var):
        pass

    def upnp_eventCM(self, evt, var):
        pass

    def upnp_eventRCS(self, evt, var):
        pass

    def oh_eventPLAYLIST(self, evt, var):
        pass

    def oh_eventINFO(self, evt, var):
        pass

    def oh_eventTIME(self, evt, var):
        pass

    def oh_eventVOLUME(self, evt, var):
        pass

    def getMetadata(self):
        return self.mpd.call('playlistid', self.mpd.status['songid'])

    def getMimeTypes(self):
        return self.mpd.call('decoders')

    def get_state(self):
        return self._state

    def get_rate(self):
        return self._rate

    def get_track_duration(self):
        try:
            duration = self._track_duration
        except:
            duration = '00:00:00'
        return duration

    def get_track_URI(self):
        try:
            uri = self._track_URI
        except:
            uri = ''
        return uri

    def get_track_md(self):
        return self.metadata_str

    def get_sticker(self, url, dic={}):

        def got_sticker(stklist, dic):
            if not isinstance(stklist, list):
                stklist = [stklist]
            for sticker in stklist:
                try:
                    dic.update(
                        {sticker['sticker'].split('=')[0],
                         sticker['sticker'].split('=')[1]})
                except:
                    continue
            return dic

        d = self.mpd.call('sticker', ' '.join(('list song', url.join('"'*2))))
        d.addBoth(got_sticker, dic)
        return d

    def get_tracks(self, tracks):

        def got_tracks(tracks):
            if not isinstance(tracks, list):
                tracks = [tracks]
            sl = []
            for track in tracks:
                t = mpd_decode(track)
                sl.append(self.get_sticker(t['url'], t))
            return defer.gatherResults(sl)

        def generate_tracklist(tracks, tracklist=None):
#             log.err(tracks)
            if not isinstance(tracks, list):
                tracks = [tracks]
            tl = et.Element('TrackList')
            for idx, track in enumerate(tracks):
#                 log.err(track)
                if isinstance(track, dict):
                    track = mpd_decode(track)
                else:
#                     log.err(track)
                    nd = {}
                    for d in track:
#                         log.err(d)
                        nd.update(d)
                    track = mpd_decode(nd)
#                     log.msg(nd)
                en = et.Element('Entry')
                i = et.Element('Id')
                if not 'id' in track:
                    if tracklist:
                        track.update({'id': str(tracklist[idx])})
                    else:
                        log.err(track)
                i.text = track['id'].decode('utf-8')
                en.append(i)
                uri = et.Element('Uri')
                uri.text = track['url'].decode('utf-8')
                en.append(uri)
                md = et.Element('Metadata')
                md.text = didl_encode(track)
                en.append(md)
                tl.append(en)
            return et.tostring(tl)

#         if tracks == self.playlist:
#             d = self.mpd.call('playlistid')
#             d.addCallback(generate_tracklist, tracks)
#         else:
        tl = []
        for track in tracks:
            tl.append(self.mpd.call('playlistid', str(track)))
        d = defer.gatherResults(tl)
#         d.addCallback(got_tracks)
        d.addCallback(generate_tracklist)
        return d

    def get_track(self, track=None):

        def got_result(res):
            uri = res['url']
            return (uri, didl_encode(res))
        if track is None:
            d = defer.succeed((self._track_URI, self.metadata_str))
        else:
            d = self.mpd.call('playlistid', str(track))
            d.addCallback(mpd_decode)
            d.addCallback(got_result)
        return d

    def get_relcount(self):
        return 2147483647

    def get_abscount(self):
        return 2147483647

    def get_abstime(self):
            return '00:00:00'

    def get_reltime(self, fmt='UPNP'):
        if self.timer is not None:
            if fmt == 'UPNP':
                t = mpdtime_to_upnptime(self.timer.get())
            elif fmt == 'seconds':
                t = int(self.timer.get())
            else:
                # msec
                t = self.timer.get()
        else:
            if fmt == 'UPNP':
                t = self.reltime
            else:
                t = self.seconds
#         log.err('reltime: %s' % t)
        return t

    def get_volume(self):

        def noVolume(err):
            if self._muted:
                return 0
            else:
                return self._volume

        def convert_volume(vol):
            self._volume = int(float(vol) * 100)
            log.msg("volume= %d" % self._volume, loglevel=logging.DEBUG)
            return self._volume

        d = self.mediaplayer.get("Volume",
                                 interf='org.mpris.MediaPlayer2.Player')
        d.addCallbacks(convert_volume, noVolume)
        return d

    def get_transport_actions(self):
            return {','.join(self.transport_actions)}

    def set_volume(self, channel, volume):
        volume = str(volume)
        d = self.mpd.call('setvol', volume)
        d.addErrback(
            log.msg,
            'Set Volume Error : %s - %d' % (channel, int(volume)))
        reactor.callLater(0.1,  # @UndefinedVariable
                          self.changed_state,
                          {'volume': str(volume)})

    def set_mimetypes(self, mtlist):
        if self._mtlist != mtlist:
            self._mtlist = mtlist
            self.mtlist = 'http-get:*:' + ''\
                .join([':*,http-get:*:'.join(mtlist)]) + ':*'
            self.upnp_eventCM(self.mtlist, 'SinkProtocolInfo')
            self.oh_eventPLAYLIST(self.mtlist, 'protocolinfo')

    def set_metadata(self, metadata):
        if metadata != self.metadata:
            self.metadata.update(metadata)
            if 'duration' in metadata.keys():
#                 log.err('duration set to %s by metadata'
#                         % metadata['duration'])
                self._track_duration = metadata['duration']
            if 'url' in metadata.keys():
                self._track_URI = metadata['url']
            if 'file' in metadata.keys():
                self._track_URI = metadata['file']

    def set_reltime(self, t):
        self.seconds = int(float(t))
        self.reltime = mpdtime_to_upnptime(t)
#         log.err('seconds: %s' % t)
        self.timer = None

    def set_state(self, state):
        log.msg("SET NEW STATE : %s " % state, loglevel=logging.DEBUG)
        if state == self._state:
            return
        self._state = state
        if state == 'pause':
            #             print(self.transport_actions)
            self.transport_actions = ['PLAY', 'STOP']
#                 self.transport_actions.remove('PAUSE')
#                 self.transport_actions.append('PLAY')
            if self.timer is not None:
                self.timer.stop()
                d = self.mpd.call('status')
                d.addCallback(lambda st: st['elapsed'])
                d.addCallbacks(
                    self.set_reltime,
                    lambda ignored: self.set_reltime(self.timer.get()))
            self.upnp_state = 'PAUSED_PLAYBACK'
            self.oh_state = 'Paused'
        elif state == 'play':
            self.transport_actions = ['STOP', 'PAUSE', 'SEEK']
            self.changed_state({'volume': self.mpd.status['volume']})
            self.timer = Timer()
            self.timer.set(self.seconds)
            d = self.mpd.call('status')
            d.addCallbacks(lambda st: st['elapsed'])
            d.addCallbacks(lambda t: self.timer.set(float(t)),
                           lambda ignored: self.timer.set(0.000))
            self.upnp_state = 'PLAYING'
            self.oh_state = 'Playing'
        elif state == 'stop':
            self.transport_actions = ['PLAY']
            self.set_reltime(0)
            self.upnp_state = 'STOPPED'
            self.oh_state = 'Stopped'
        elif state == 'pending':
            self.upnp_state = 'TRANSITIONNING'
            self.oh_state = 'Buffering'
        else:
#             log.err('Unknow State from player : %s' % state)
            return
        log.msg('send new state: %s' % self._state, loglevel=logging.DEBUG)
        self.upnp_eventAV(self.upnp_state, 'TransportState')
        self.oh_eventPLAYLIST(self.oh_state, 'transportstate')

    def set_songid(self, songid):
        self.songid = songid['Id']
#         log.err('songid = %s' % songid)
        return self.songid

    def set_position(self, newpos, fmt='UPNP'):
        def transition(obj):
            current_state = self._state
            self.set_state('pending')
            reactor.callLater(0.5,  # @UndefinedVariable
                              self.set_state,
                              current_state)
        if fmt == 'UPNP':
            pos = upnptime_to_mpdtime(newpos)
        else:
            pos = newpos
        d = self.mpd.call('seekcur', pos)
        d.addCallback(transition)
        return d

    def set_position_relative(self, delta, fmt='UPNP'):
        newpos = int(self.get_reltime('Seconds')) + int(delta)
        self.set_position(newpos, fmt)

    def set_track_URI(self, uri, md=''):
        log.msg("set track uri : %s " % uri, loglevel=logging.DEBUG)
        try:
            log.msg("current uri : %s " % self._track_URI, loglevel=logging.DEBUG)
        except:
            pass
        if uri != self._track_URI:
            self._track_URI = uri
            self.metadata_str = md
            self.set_metadata(didl_decode(md.encode('utf-8')))
            d = self.mpd.call('addid', uri)
            d.addCallback(self.set_songid)
            d.addCallback(self.play)

    def set_repeat(self, repeat):
        self.mpd.call('repeat', str(int(repeat)))

    def set_shuffle(self, repeat):
        self.mpd.call('shuffle', str(int(repeat)))

    def stop(self):
        def stopped(ret):
                self.set_state('stop')
        if self._state != 'STOPPED':
            d = self.mpd.call('stop')
            self.reltime = '00:00:00'
            d.addCallback(stopped)

    def play(self, songid=None, ignored=None):
        def success(result):
            return None
        if self.cancelplay:
            self.cancelplay = False
        else:
            if songid is not None:
                d = self.mpd.call('playid', songid)
            else:
                if self._state == 'pause':
                    d = self.mpd.call('pause', '0')
                else:
                    d = self.mpd.call('playid', self.songid)
            d.addCallback(self.playing)

    def playing(self, *ret):
            log.msg('playing...', loglevel=logging.DEBUG)
            self.set_state('play')

    def playindex(self, index):
        return self.play(self.playlist[int(index)])

    def playpause(self):
        if self._state == 'pause':
            return self.play()
        else:
            return self.pause()

    def pause(self):
        def paused(ret):
            if self._state == 'play':
                self.set_state('pause')
    #         d =  self.player_func('Pause','org.mpris.MediaPlayer2.Player' )
        d = self.mpd.call('pause', '1')
        d.addCallback(paused)
        return d

    def next(self):
        self.mpd.call('next')

    def previous(self):
        self.mpd.call('previous')

    def volUp(self):
        if self._volume == 0:
            vol = self.get_volume()
            try:
                newvol = vol + 5
            except:
                newvol = self._volume + 5
            return self.set_volume('Master', newvol)
        else:
            if self._volume > 95:
                return
            newvol = self._volume + 5
            return self.set_volume('Master', newvol)

    def volDown(self):
        if self._volume > 5:
            newvol = self._volume - 5
            return self.set_volume('Master', newvol)
        else:
            self._volume = 0
            return self.set_volume('Master', '0')

    def insert_metadata(self, md):
            dic = didl_decode(md)
#             log.err(dic)
            for i, tag in enumerate(dic.keys()):
                if tag.lower() in ('class', 'restricted', 'id', 'duration',
                                   'parentid', 'protocolinfo', 'url',
                                   'ownerudn'):
                    continue
                if not isinstance(dic[tag], str):
                    continue
                reactor.callLater(  # @UndefinedVariable
                    float(i)/2,
                    self.mpd.call,
                    ' '.join(('sticker',
                              'set song',
                              dic['url'].join('"'*2),
                              tag,
                              '"' + dic[tag] + '"')))

    def insert(self, url, afterid, metadata, checked=False):

        def inserted(res, md):
            #             log.err('%s %s' % (res, md))
            #             reactor.callLater(2,  # @UndefinedVariable
            #                               self.insert_metadata,
            #                               md)
            return res['Id']

        if 'youtube' in url and not checked:
            # eclipse workaround !!!
            y = os.environ
            y.update({'PYTHONPATH': '/usr/bin/python'})
            # / eclipse workaround
            d = utils.getProcessOutput(
                '/usr/bin/youtube-dl',
                ['-g', '-f', 'bestaudio', url],
                env=y,
                reactor=reactor)
            d.addCallback(
                lambda u: self.insert(
                    u.split('\n')[0], afterid, metadata, True))
            return d
        if len(self.playlist) == 0:
            d = self.mpd.call('addid', url)
        elif int(afterid) == 0:
            d = self.mpd.call('addid', url + ' 0')
        else:
            d = self.mpd.call(
                'addid',
                ' '.join((url, str(self.playlist.index(int(afterid))+1))))
        d.addCallback(inserted, metadata)

        return d

    def delete(self, songid):
        self.mpd.call('deleteid', str(songid))

    def clear(self):
        self.mpd.call('clear')
Exemplo n.º 3
0
class Omxclient(Service):
    bus = None
    addrs = None
    token = 0
    con = None
    _state = 'Pending'
    _volume = 1
    max_volume = 100
    seconds = 0
    uri = ''
    properties = None
    player = None
    is_connected = False
    pending = True
    event_msg = {
        'Paused': ['PAUSED_PLAYBACK', 'Paused'],
        'Playing': ['PLAYING', 'Playing'],
        'Pending': ['TRANSITIONNING', 'Buffering'],
        'Stopped': ['STOPPED', 'Stopped']}
    _metadata = {}
    metadata_str = ''
    metadata = {}
    songid = 0
    maxsongid = 0
    _duration = 0
    upnp_duration = '0:00:00'
    reltime = '0:00:00.000'
    _playlist = []
    playlist = []
    _track_URI = ''
    idArray = ''
    repeat = False
    shuffle = False
    tracksmax = 1000
    mtlist = ''
    timer = None
    _next = False

    def __init__(self, program='', args=None, **kwargs):
        if args:
            self.process_args = [program] + [arg for arg in args.split()]
        else:
            self.process_args = [program]
        self.process_path = program
        self.process = PlayerProcess(self)
        self.playername = program.split("/")[-1]

    def startService(self):
        self.con = DbusConnection()
        d = self.con.connect()
        d.addCallbacks(self.connected, self.not_connected)

    def connected(self, bus):
        log.msg('connected: %s' % bus)
        self.bus = bus

        def got_properties(proxy):
            d = self.player.get_proxy()
            d.addCallback(got_player)
            return d

        def got_player(proxy):
            self.con.watch_process(
                'org.mpris.MediaPlayer2.omxplayer', self.connection_lost)
            self.pending = False
            reactor.callLater(6,  # @UndefinedVariable
                              lambda: setattr(self, 'polling', True))
            reactor.callLater(  # @UndefinedVariable
                7, self.poll_status)

        if not self.properties:
            self.properties = ODbusProxy(
                self.bus,
                bus_name='org.mpris.MediaPlayer2.omxplayer',
                object_path='/org/mpris/MediaPlayer2',
                interface='org.freedesktop.DBus.Properties',
                timeout=5)
            self.player = ODbusProxy(
                self.bus,
                bus_name='org.mpris.MediaPlayer2.omxplayer',
                object_path='/org/mpris/MediaPlayer2',
                interface='org.mpris.MediaPlayer2.Player',
                timeout=5)
            d = self.properties.get_proxy()
        else:
            return defer.succeed(None)
        d.addCallback(got_properties)
        return d

    def connection_lost(self, *args, **kwargs):
        log.err('connection lost')
        #for arg in args:
            #log.err(arg)
        if 'really_lost' in kwargs:
            really_lost = kwargs['really_lost']
        else:
            really_lost = False
        if really_lost:
            self.is_connected = False
        else:
            self.is_connected = not self.is_connected
        if not self.is_connected:
            if self.process.launched:
                log.err('launched')
                self.connect()
            else:
                log.err('not launched')
                self.properties = None
                self.player = None
                self.polling = False
                self.changed_state({'PlaybackStatus': 'Stopped'})
                self.timer = None
                self.seconds = 0
                self.reltime = '0:00:00.000'
            #self.changed_state({'PlaybackStatus': 'Stopped'})

    def not_connected(self, err):
        self.bus = None
        log.err('Dbus Connection failed: %s' % err)

    def connect(self, err=None):
        log.msg('connect')
        if not self.bus:
            if not self.addrs:
                self.addrs = get_user_sessions()
                log.msg(self.addrs)
                if len(self.addrs) > 0:
                    if self.con:
                        d = self.con.connect_addr(self.addrs.pop(0))
                        d.addCallbacks(self.connected, self.connect)
                    else:
                        d = task.deferLater(reactor, 2, self.connect)
                else:
                    self.addrs = None
                    d = task.deferLater(reactor, 2, self.connect)
            return d
        else:
            return self.connected(self.bus)

    def poll_status(self):
        if self.polling:
            if not self.pending:
                if self.process.launched:
                    self.pending = True
                    d = self.properties.Position()
                    d.addCallbacks(self.update_position, self.call_failed)
                    d.addCallback(
                        lambda ignored: setattr(self, 'pending', False))
            reactor.callLater(5, self.poll_status)  # @UndefinedVariable

    def call_failed(self, err):
        self.pending = False
        log.msg(err)

    def got_event(self, *args, **kwargs):
        if args[0] == 'properties':
            if args[1][0] == 'org.mpris.MediaPlayer2.Player':
                self.changed_state(args[1][1])
            elif args[1][0] == 'org.mpris.MediaPlayer2.TrackList':
                self.changed_tracks()
        elif args[0] == 'tracklist':
            self.changed_tracks()

    def update_position(self, pos):
        if self._state in ('Paused', 'Stopped'):
            log.msg('del timer')
            self.seconds = int(pos)/1000000
            self.timer = None
        elif self.timer:
            log.msg('timer alive')
            self.timer.set(int(pos)/1000000)
        else:
            self.seconds = int(pos)/1000000

    def changed_state(self, *args, **kwargs):
        for arg in args:
            if isinstance(arg, dict):
                if 'PlaybackStatus' in arg:
                    log.err(arg)
                    if self._state != arg['PlaybackStatus']:
                        self._state = arg['PlaybackStatus']
                        self.upnp_eventAV(
                            self.event_msg[self._state][0], 'TransportState')
                        self.oh_eventPLAYLIST(
                            self.event_msg[self._state][1], 'transportstate')
                if 'Volume' in arg:
                    if self._volume != arg['Volume']:
                        self._volume = arg['Volume']
                        self.upnp_eventRCS(int(self._volume*100), 'Volume')
                        self.oh_eventVOLUME(int(self._volume*100), 'volume')
                if 'Metadata' in arg:
                    self.update_metadata(arg['Metadata'])

    def update_metadata(self, metadata):
        songid = None
        if isinstance(metadata, dict):
            if self._metadata == metadata:
                return
            else:
                self._metadata = metadata
                self.metadata = mpris_decode(metadata)
        elif isinstance(metadata, str):
            if self.metadata_str == metadata:
                return
            else:
                self.metadata_str = metadata
                self.metadata = didl_decode(metadata)
                log.msg(self.metadata)
        else:
            log.err('Bad metadata format : %s' % metadata)
            return
        if 'songid' in self.metadata:
                if self.songid != int(self.metadata['songid']):
                    songid = int(self.metadata['songid'])
        if songid:
            self.songid = songid
            self.upnp_eventAV(int(self.songid), 'CurrentTrack')
            self.oh_eventPLAYLIST(int(self.songid), 'id')
            self.oh_eventTIME(1, 'trackcount')
        if 'duration' in self.metadata:
            if self._duration != self.metadata['duration']:
                duration = int(self.metadata['duration'])
                log.msg('duration: %d' % duration)
                if duration < 1:
                    self.upnp_duration = "0:00:00"
                    self._duration = 0
                else:
                    self._duration = duration
                    self.upnp_duration = mpristime_to_upnptime(duration)
                log.msg('track length: %s'
                        % self.upnp_duration, loglevel=logging.DEBUG)
                self.upnp_eventAV(self.upnp_duration,
                                  'CurrentTrackDuration')
                self.oh_eventINFO(int(self._duration//1000000), 'duration')
                self.oh_eventTIME(int(self._duration//1000000), 'duration')
        if 'url' in self.metadata:
            if self._track_URI != self.metadata['url']:
                self._track_URI = self.metadata['url']
                self.upnp_eventAV(self._track_URI, 'AVTransportURI')
                self.oh_eventINFO(self._track_URI, 'uri')
                self.upnp_eventAV(self._track_URI, 'CurrentTrackURI')
        if 'mpris:artUrl' in self.metadata:
            url = self.parent.register_art_url(self.metadata['mpris:artUrl'])
            self.metadata['albumArtURI'] = url
        self.oh_eventINFO(self.metadata_str, 'metadata')
        self.upnp_eventAV(self.metadata_str, 'AVTransportURIMetaData')

    def changed_tracks(self):
        self.oh_eventPLAYLIST(id_array(self.playlist), 'idarray')

    def upnp_eventAV(self, evt, var):
        pass

    def upnp_eventCM(self, evt, var):
        pass

    def upnp_eventRCS(self, evt, var):
        pass

    def oh_eventPLAYLIST(self, evt, var):
        pass

    def oh_eventINFO(self, evt, var):
        pass

    def oh_eventTIME(self, evt, var):
        pass

    def oh_eventVOLUME(self, evt, var):
        pass

    def get_track_id(self):
        return self.songid

    def get_track(self, track):
        ind = self.playlist.index(int(track))
        return defer.succeed((self._playlist[ind][1], self._playlist[ind][2],))

    def get_tracks(self, tracks):
        tr = []
        for track in tracks:
            ind = self.playlist.index(int(track))
            tr.append(
                (self._playlist[ind][0],
                 self._playlist[ind][1],
                 self._playlist[ind][2],))
        tracks = tr
        if not isinstance(tracks, list):
                tracks = [tracks]
        tl = et.Element('TrackList')
        for track in tracks:
            #             log.err('track: %s' % track[0])
            en = et.Element('Entry')
            i = et.Element('Id')
            i.text = str(track[0])
            en.append(i)
            uri = et.Element('Uri')
            uri.text = track[1].decode('utf-8')
            en.append(uri)
            md = et.Element('Metadata')
            md.text = track[2]
            en.append(md)
            tl.append(en)
        return defer.succeed(et.tostring(tl))

    def get_state(self):
        return self._state

    def get_rate(self):

        return self.rate

    def get_track_duration(self):
        try:
            duration = self._track_duration
        except:
            duration = '00:00:00'
        return duration

    def get_track_URI(self):
        try:
            uri = self._track_URI
        except:
            uri = ''
        return uri

    def get_track_md(self):
        return self.metadata_str

    def get_relcount(self):
        return 2147483647

    def get_abscount(self):
        return 2147483647

    def get_abstime(self):
            return '00:00:00'

    def get_reltime(self, fmt='UPNP'):
        if self.timer is not None:
            s = self.timer.get()
            if (self._duration//1000000 - s) < 2:
                if not self._next:
                    log.msg('next!!')
                    self._next = True
                    reactor.callLater(3, self.next)  # @UndefinedVariable
            if fmt == 'UPNP':
                t = mpristime_to_upnptime(s)
            elif fmt == 'seconds':
                t = int(s)
            else:
                # msec
                t = s
        else:
            if fmt == 'UPNP':
                t = self.reltime
            else:
                t = self.seconds
        return t

    def get_volume(self):
        if self.process.launched:
            def noVolume(err):
                if self._muted:
                    return 0
                else:
                    return self._volume

            def convert_volume(vol):
                self._volume = int(float(vol))
                log.msg("volume= %d" % self._volume, loglevel=logging.DEBUG)
                return self._volume

            d = self.properties.Volume()
            d.addCallbacks(convert_volume, noVolume)
            return d
        else:
            return 1

    def set_volume(self, channel, volume):
        if self.process.launched:
            if int(volume) != 0:
                d = self.properties.Volume(float(int(volume)/100.00))
            else:
                if self._muted:
                    d = self.properties.Volume(float(self._volume))
                else:
                    d = self.properties.Volume(0.00)
            d.addErrback(self.call_failed)
            reactor.callLater(0.1, self.changed_state,  # @UndefinedVariable
                              {'Volume': float(int(volume)/100.00)})

    def set_track_URI(self, uri, md=''):
        log.msg("set track uri : %s " % uri, loglevel=logging.DEBUG)
        try:
            log.msg("current uri : %s " % self._track_URI, loglevel=logging.DEBUG)
        except:
            pass
        if uri != self._track_URI:
            self.changed_state({'Metadata': md.encode('utf-8')})

    def set_position(self, newpos, fmt='UPNP'):
        if self.process.launched:
            def transition(obj):
                current_state = self._state
                self.changed_state({'PlaybackStatus': 'Pending'})
                reactor.callLater(  # @UndefinedVariable
                    0.5,
                    self.changed_state,
                    {'PlaybackStatus': current_state})
            if fmt == 'UPNP':
                newtime = upnptime_to_mpristime(newpos)
                offset = newtime - self.seconds
            else:
                offset = float(newpos) - self.seconds
            d = self.player.Seek(offset)
            d.addCallbacks(transition, self.call_failed)
            return d

    def set_repeat(self, repeat):
        log.err('repeat=%s' % repeat)
        self.repeat = repeat
        self.oh_eventPLAYLIST(self.repeat, 'repeat')

    def set_shuffle(self, shuffle):
        self.shuffle = shuffle

    def play(self, songid=None):
        log.err('play :%s' % songid)

        def playing(ignored, trackid=None, md=None):
            log.err('playing')
            self.pending = False
            if not self.timer:
                log.msg('create timer')
                self.timer = Timer()
                self.timer.set(self.seconds)
            else:
                log.msg('resume timer')
                self.timer.resume()
            if trackid:
                self.changed_state(
                    {'PlaybackStatus': 'Playing',
                     'Metadata': {'songid': trackid}}, {'Metadata': md})
            else:
                self.changed_state({'PlaybackStatus': 'Playing'})
        log.msg('from %s to %s' % (self._state, 'Play'))
        if songid:
            songid = int(songid)
            if self.process.launched:
                log.err('killing player')
                self.player.Stop()
                task.deferLater(reactor, 1, self.play, songid)
            else:
                volmb = 2000.0 * math.log10(self._volume)
                self.pending = True
                args = self.process_args\
                    + ['--vol']\
                    + [str(volmb)]\
                    + [self._playlist[self.playlist.index(songid)][1]]
#                 args = self.process_args +\
#                     [self._playlist[self.playlist.index(songid)][1]]
                reactor.spawnProcess(  # @UndefinedVariable
                    self.process,
                    self.process_path,
                    tuple(args),
                    env=os.environ)
                log.msg('play in one second')
                reactor.callLater(  # @UndefinedVariable
                    1,
                    playing,
                    *(0,
                      songid,
                      self._playlist[self.playlist.index(songid)][2],))
        else:
            if self.process.launched:
                if self._state == 'Paused':
                    if not self.pending:
                        self.pending = True
                        d = self.player.Pause()
                        d.addCallbacks(playing, self.call_failed)
                    else:
                        reactor.callLater(  # @UndefinedVariable
                            0.2, self.play)
            else:
                volmb = 2000.0 * math.log10(self._volume)
                self.pending = True
                args = self.process_args\
                    + ['--vol']\
                    + [str(volmb)]\
                    + [self.track_URI]
                reactor.spawnProcess(  # @UndefinedVariable
                    self.process,
                    self.process_path,
                    tuple(args),
                    env=os.environ)
                reactor.callLater(  # @UndefinedVariable
                    1,
                    playing,
                    *(0, self.songid, self.metadata_str))

    def pause(self):
        def paused(ignored):
            self.pending = False
            self.timer.stop()
#             self.seconds = self.timer.get()
#             self.timer = None
            self.changed_state({'PlaybackStatus': 'Paused'})
        log.msg('from %s to %s' % (self._state, 'Pause'))
        if self._state != 'Paused':
            if not self.pending:
                self.pending = True
                d = self.player.Pause()
                d.addCallbacks(paused, self.call_failed)
            else:
                reactor.callLater(0.3, self.pause)  # @UndefinedVariable

    def stop(self):
        if self._state != 'Stopped':
            self.timer = None
            self.seconds = 0
            self.player.Stop()
            self.reltime = '00:00:00'
            self.changed_state({'PlaybackStatus': 'Stopped'})

    def next(self):
        if self._next:
            self._next = False
        if len(self.playlist) > 0:
            if self.songid == self.playlist[-1]:
                if self.repeat:
                    self.play(songid=self.playlist[0])
            else:
                self.play(
                    songid=self.playlist[
                        self.playlist.index(self.songid) + 1])

    def previous(self):
        if len(self.playlist) > 0:
            if self.songid == self.playlist[0]:
                if not self.repeat:
                    return
            self.play(
                songid=self.playlist[
                    self.playlist.index(self.songid) - 1])

    def volUp(self):
        if self._volume == 0:
            vol = self.get_volume()
            try:
                newvol = vol + 5
            except:
                newvol = self._volume*100 + 5
            return self.set_volume('Master', newvol)
        else:
            if self._volume > 95:
                return
            newvol = self._volume*100 + 5
            return self.set_volume('Master', newvol)

    def volDown(self):
        if self._volume > 0.05:
            newvol = self._volume*100 - 5
            return self.set_volume('Master', newvol)
        else:
            self._volume = 0
            return self.set_volume('Master', 0)

    def insert(self, url, afterid, metadata, checked=False):
        if 'youtube' in url and not checked:
            # eclipse workaround !!!
            y = os.environ
            y.update({'PYTHONPATH': '/usr/bin/python'})  # / eclipse workaround
            d = utils.getProcessOutput(
                '/usr/bin/youtube-dl',
                ['-g', '-f', 'bestaudio', url],
                env=y,
                reactor=reactor)
            d.addCallback(
                lambda u: self.insert(
                    u.split('\n')[0], afterid, metadata, True))
            return d
#         log.err('playlist length:%s' % len(self._playlist))
        self.maxsongid += 1
        if len(self._playlist) == 0:
            self._playlist.append([self.maxsongid, url, metadata])
        else:
            if afterid == '0':
                self._playlist.insert(0, [self.maxsongid, url, metadata])
            else:
                self._playlist.insert(
                    self.playlist[self.playlist.index(int(afterid))],
                    [self.maxsongid, url, metadata])
#         log.err('real playlist: %s' % self._playlist)
        self.playlist = [i[0] for i in self._playlist]
#         log.err('new playlist: %s' % self.playlist)
#         log.err('metadata dic: %s' % metadata)
        self.oh_playlist = [str(i) for i in self.playlist]
        self.idArray = id_array(self.playlist)
        self.changed_tracks()
        if self.songid == 0:
            self.update_metadata({'songid': 1})
        return defer.succeed(self.maxsongid)

    def delete(self, songid):
        #  log.err(self.playlist)
        try:
            suppressed = self.playlist.index(int(songid))
        except IndexError:
            pass
        else:
            self._playlist.pop(suppressed)
            self.playlist.pop(suppressed)
            self.idArray = id_array(self.playlist)
            self.changed_tracks()

    def clear(self):
        self.playlist = []
        self._playlist = []
        self.songid = 0
        self.idArray = ''
        self.changed_state('TrackList', {}, '')
Exemplo n.º 4
0
class Mpd_factory(ReconnectingClientFactory, Client):
    '''
    Twisted Reconnecting client factory for mpd server
    convert all UPnP and OpenHome service function to mpd remote calls
    '''

    _mtlist = ''
    _muted = True
    _rate = "1"
    _state = "pending"
    _track_duration = '0:00:00.000'
    ohduration = 0
    _track_URI = ''
    _volume = 100
    active_source = 0
    attributes = 'Info Time Volume'
    balance = 0
    balancemax = 10
    bitdepth = 0
    bitrate = 0
    cancelplay = False
    codecname = 'MP3'
    counter = 0
    detailscount = 0
    fade = 0
    fademax = 10
    idArray = ''
    lossless = False
    maxid = 0
    max_volume = 100
    metadata = {}
    metadata_str = 'NOT_IMPLEMENTED'
    metatext = ''
    metatextcount = 0
    mimetypes = ['audio/aac', 'audio/mp4', 'audio/mpeg', 'audio/mpegurl',
                 'audio/vnd.rn-realaudio', 'audio/vorbis', 'audio/x-flac',
                 'audio/x-mp3', 'audio/x-mpegurl', 'audio/x-ms-wma',
                 'audio/x-musepack', 'audio/x-oggflac', 'audio/x-pn-realaudio',
                 'audio/x-scpls', 'audio/x-speex', 'audio/x-vorbis',
                 'audio/x-wav', 'application/x-ogm-audio',
                 'audio/x-vorbis+ogg', 'audio/ogg']
    mtlist = ''
    name = "MpdPlayer"
    oh_state = 'Buffering'
    old_vol = 0
    parent = None
    reltime = '0:00:00.000'
    repeat = False
    room = 'Home'
    samplerate = 0
    seconds = 0
    shuffle = False
    songid = 0
    sources = ['Playlist']
    sourceindex = 0
    upnp_state = "TRANSITIONNING"
    volumeunity = 3
    volumemillidbperstep = 600
    timer = None
    token = 0
    trackcount = 0
    tracksmax = 0
    transport_actions = ['PLAY']
#     ohduration = 0

    def __init__(self, cover_dir, net_type='lan'):

        self.connected = False
        self.net_type = net_type
        self.proto = MpdProtocol()
        self.proto._event = self.dispatch_event
        self.status = {'state': 'stop'}
        self.playlist = Playlist(self)
        self.waiting = False
        self.calls = []
        self.cover_dir = cover_dir
        self._sources = [OrderedDict(sorted(
            {'Name': '_'.join((self.name, n)),
             'Type': n,
             'Visible': True}.
            items(), key=lambda t: t[0])) for n in self.sources]
        self.sourcexml = dict2xml(
            {'SourceList': [{'Source': n} for n in self._sources]})
        self.protocolinfo = self.mtlist = 'http-get:*:' + ''\
            .join([':*,http-get:*:'.join(self.mimetypes)]) + ':*'

    def buildProtocol(self, addr):
        return self.proto
    '''
    Internal functions
    '''

    def dispatch_event(self, events=None):

        def set_connected():
            self.connected = True
            p = self.call('plchanges', self.playlist.plsversion)
            p.addCallback(self.playlist.update)

        d = None
        for evt in events:
            if evt == 'Error':
                log.error('bad event: {name}', name=events[evt])
                return
            elif evt == 'changed':
                chg = events[evt]
            else:
                #                 log.msg(events[evt])
                return
            if chg == 'disconnected':
                self.connected = False
                continue
            if chg == 'playlist':
                log.error('update from: %s' % self.playlist.plsversion)
                d = self.call('plchanges', self.playlist.plsversion)
                d.addCallback(self.playlist.update)
            else:
                d = self.call('status')
                if chg == 'connected':
                    d.addCallback(self.filter_event)
                    reactor.callLater(1, set_connected)  # @UndefinedVariable
                elif chg == 'mixer':
                    d.addCallback(self.filter_event,
                                  'volume')
                elif chg == 'player':
                    d.addCallback(self.filter_event)
            return d

    def filter_event(self, data, filtr=None):
        if data is None:
            return
        if not isinstance(data, list):
            data = [data]
        if filtr is not None:
            for d in data:
                self.status.update(d)
            self.changed_state({filtr: self.status[filtr]})
        else:
            dic = {}
            for d in data:
                for it in d.iteritems():
                    if it not in self.status.items():
                        self.status.update(dict([it]))
                        dic.update(dict([it]))
                        if it[0] == 'playlistlength':
                            self.playlist.length = int(it[1])
            self.changed_state(dic)

    def call(self, command=None, params=None):
        #         log.error('register %s' % command)
        d = defer.Deferred()
        if command is not None:
            self.proto.addCallback(d)
        if not self.connected:
            if command:
                self.calls.append((command, params))
        else:
            if len(self.calls) > 0:
                if command:
                    self.calls.append((command, params))
                else:
                    d = None
                command, params = self.calls.pop(0)
            if params is not None:
                c = ' '.join([command, str(params)]).encode('utf-8')
            else:
                c = command.encode('utf-8')
            if self.proto.idle:
                self.proto.noidle()
            self.proto.sendLine(c)
#             log.error('send %s' % c)
            if len(self.calls) > 0:
                self.call()
        return d

    def changed_state(self, state):
        changed = state.keys()
        if 'state' in changed:
            self.set_state(state['state'])
        if "volume" in changed:
            log.debug('volume changed')
            vol = int(state['volume'])
            if vol != self._volume:
                if vol != 0:
                    self._volume = vol
                    muted = False
                else:
                    muted = True
                log.debug('send volume')
                self.oh_eventVOLUME(self._volume, 'volume')
                self.upnp_eventRCS(self._volume, 'volume')
                if muted is not self._muted:
                    self._muted = muted
                    self.upnp_eventRCS(self._muted, 'mute')
                self.oh_eventVOLUME(int(self._muted), 'mute')

        if 'songid' in changed:
            self.playlist.current_track(state['songid'])
        if 'repeat' in changed:
            #             log.debug('************repeat***********: %s' %
            #                 bool(int(state['repeat'])))
            if self.repeat != bool(int(state['repeat'])):
                self.repeat = bool(int(state['repeat']))
                if not self.shuffle:
                    self.upnp_eventAV(
                        'REPEAT_ALL' if self.repeat else 'NORMAL',
                        'currentplayMode')
                else:
                    self.upnp_eventAV('REPEAT_ALL SHUFFLE' if self.repeat
                                      else 'NORMAL SHUFFLE', 'currentplaymode')
                self.oh_eventPLAYLIST(self.repeat, 'repeat')
        if 'random' in changed:
            log.debug('************shuffle***********: %s' %
                      bool(int(state['random'])))
            if self.shuffle != bool(int(state['random'])):
                self.shuffle = bool(int(state['random']))
                if not self.repeat:
                    self.upnp_eventAV(
                        'NORMAL SHUFFLE' if self.shuffle else 'NORMAL',
                        'currentplaymode')
                else:
                    self.upnp_eventAV(
                        'REPEAT_ALL SHUFFLE' if self.shuffle
                        else 'NORMAL SHUFFLE', 'currentplaymode')
                self.oh_eventPLAYLIST(self.shuffle, 'shuffle')
        if 'elapsed' in changed:
            if self.timer is not None:
                self.timer.set(float(state['elapsed']))
        if 'playlist' in changed:
            self.token = int(state['playlist'])
#             self.playlist.plsversion = state['playlist']
        if 'playlistdata' in changed:
            self.playlist.update(state['playlistdata'])
        if 'bitrate' in changed:
            self.detailscount += 1
            self.bitrate = int(state['bitrate'])
            self.oh_eventINFO(self.bitrate, 'bitrate')
        if 'audio' in changed:
            try:
                sr = int(state['audio'].split(':')[0])
                self.samplerate = sr
                self.oh_eventINFO(sr, 'samplerate')
            except:
                log.critical('Bad Samplerate: %s' %
                             state['audio'].split(':')[0])
            try:
                bd = int(state['audio'].split(':')[1])
                self.bitdepth = bd
                self.oh_eventINFO(bd, 'bitdepth')
            except:
                log.critical('Bad Bitdepth: %s' % state['audio'].split(':')[1])

    def update_state(self):
        #  log.err('Update State: %s' % self.mpd.status['state'])
        self.set_state(self.status['state'])

    def update_mimetypes(self):
        self.set_mimetypes(self.mimetypes)

    def upnp_eventAV(self, evt, var):
        pass

    def upnp_eventCM(self, evt, var):
        pass

    def upnp_eventRCS(self, evt, var):
        pass

    def oh_eventPLAYLIST(self, evt, var):
        pass

    def oh_eventINFO(self, evt, var):
        pass

    def oh_eventTIME(self, evt, var):
        pass

    def oh_eventVOLUME(self, evt, var):
        pass

    def getMetadata(self):
        return self.call('playlistid', self.status['songid'])

    def getMimeTypes(self):
        return self.call('decoders')

    def get_state(self):
        return self._state

    def get_rate(self):
        return self._rate

    def get_track_duration(self):
        try:
            duration = self._track_duration
        except:
            duration = '00:00:00'
        return duration

    def get_track_URI(self):
        try:
            uri = self._track_URI
        except:
            uri = ''
        return uri

    def get_track_md(self):
        return self.metadata_str

    def get_sticker(self, url, dic={}):

        def got_sticker(stklist, dic):
            if not isinstance(stklist, list):
                stklist = [stklist]
            for sticker in stklist:
                try:
                    dic.update(
                        {sticker['sticker'].split('=')[0],
                         sticker['sticker'].split('=')[1]})
                except:
                    continue
            return dic

        d = self.call('sticker', ' '.join(('list song', url.join('"' * 2))))
        d.addBoth(got_sticker, dic)
        return d

    def get_relcount(self):
        return 2147483647

    def get_abscount(self):
        return 2147483647

    def get_abstime(self):
        return '00:00:00'

    def get_reltime(self, fmt='UPNP'):
        if self.timer is not None:
            if fmt == 'UPNP':
                t = mpdtime_to_upnptime(self.timer.get())
            elif fmt == 'seconds':
                t = int(self.timer.get())
            else:
                # msec
                t = self.timer.get()
        else:
            if fmt == 'UPNP':
                t = self.reltime
            else:
                t = self.seconds
        return t

    def get_volume(self):

        def noVolume(err):
            if self._muted:
                return 0
            else:
                return self._volume

        def convert_volume(vol):
            self._volume = int(float(vol) * 100)
            return self._volume

        d = self.mediaplayer.get("Volume",
                                 interf='org.mpris.MediaPlayer2.Player')
        d.addCallbacks(convert_volume, noVolume)
        return d

    def get_transport_actions(self):
        return {','.join(self.transport_actions)}

    def set_mimetypes(self, mtlist):
        if self._mtlist != mtlist:
            self._mtlist = mtlist
            self.mtlist = 'http-get:*:' + ''\
                .join([':*,http-get:*:'.join(mtlist)]) + ':*'
            self.upnp_eventCM(self.mtlist, 'sinkprotocolinfo')
            self.oh_eventPLAYLIST(self.mtlist, 'protocolinfo')

    def set_metadata(self, metadata):
        if metadata != self.metadata:
            self.metadata.update(metadata)
            if 'duration' in metadata.keys():
                self._track_duration = metadata['duration']
            if 'url' in metadata.keys():
                self._track_URI = metadata['url']
            if 'file' in metadata.keys():
                self._track_URI = metadata['file']

    def set_reltime(self, t):
        self.seconds = int(float(t))
        self.reltime = mpdtime_to_upnptime(t)
#         log.err('seconds: %s' % t)
        self.timer = None

    def set_state(self, state):
        log.debug("SET NEW STATE : %s " % state)
        if state == self._state:
            return
        self._state = state
        if state == 'pause':
            #             print(self.transport_actions)
            self.transport_actions = ['PLAY', 'STOP']
#                 self.transport_actions.remove('PAUSE')
#                 self.transport_actions.append('PLAY')
            if self.timer is not None:
                self.timer.stop()
                d = self.call('status')
                d.addCallback(lambda st: st['elapsed'])
                d.addCallbacks(
                    self.set_reltime,
                    lambda ignored: self.set_reltime(self.timer.get()))
            self.upnp_state = 'PAUSED_PLAYBACK'
            self.oh_state = 'Paused'
        elif state == 'play':
            self.transport_actions = ['STOP', 'PAUSE', 'SEEK']
            self.changed_state({'volume': self.status['volume']})
            self.timer = Timer()
            self.timer.set(self.seconds)
            d = self.call('status')
            d.addCallbacks(lambda st: st['elapsed'])
            d.addCallbacks(lambda t: self.timer.set(float(t)),
                           lambda ignored: self.timer.set(0.000))
            self.upnp_state = 'PLAYING'
            self.oh_state = 'Playing'
        elif state == 'stop':
            self.transport_actions = ['PLAY']
            self.set_reltime(0)
            self.upnp_state = 'STOPPED'
            self.oh_state = 'Stopped'
        elif state == 'pending':
            self.upnp_state = 'TRANSITIONNING'
            self.oh_state = 'Buffering'
        else:
            return
        self.upnp_eventAV(self.upnp_state, 'transportstate')
        self.oh_eventPLAYLIST(self.oh_state, 'transportstate')

    def set_songid(self, songid):
        self.songid = songid['Id']
        return self.songid

    def set_position(self, newpos, fmt='UPNP'):
        def transition(obj):
            current_state = self._state
            self.set_state('pending')
            reactor.callLater(0.5,  # @UndefinedVariable
                              self.set_state,
                              current_state)
        if fmt == 'UPNP':
            pos = upnptime_to_mpdtime(newpos)
        else:
            pos = newpos
        log.debug('seek to %s' % pos)
        d = self.call('seekcur', pos)
        d.addCallback(transition)
        return d

    def set_position_relative(self, delta, fmt='UPNP'):
        newpos = int(self.get_reltime('Seconds')) + int(delta)
        self.set_position(newpos, fmt)

    def set_track_URI(self, uri, md=''):
        log.debug("set track uri : %s " % uri)
        try:
            log.debug("current uri : %s " % self._track_URI)
        except:
            pass
        if uri != self._track_URI:
            self._track_URI = uri
            self.metadata_str = md
            self.set_metadata(didl_decode(md.encode('utf-8')))
            d = self.call('addid', uri)
            d.addCallback(self.set_songid)
            d.addCallback(self.play)

    def playing(self, *ret):
        log.debug('playing...')
        self.set_state('play')

    def playindex(self, index):
        return self.play(songid=self.playlist[int(index)])

    def playpause(self):
        if self._state == 'pause':
            return self.play()
        else:
            return self.pause()

    def insert_metadata(self, md):
        dic = didl_decode(md)
#             log.err(dic)
        for i, tag in enumerate(dic.keys()):
            if tag.lower() in ('class', 'restricted', 'id', 'duration',
                               'parentid', 'protocolinfo', 'url',
                               'ownerudn'):
                continue
            if not isinstance(dic[tag], str):
                continue
            reactor.callLater(  # @UndefinedVariable
                float(i) / 2,
                self.call,
                ' '.join(('sticker',
                          'set song',
                          dic['url'].join('"' * 2),
                          tag,
                          '"' + dic[tag] + '"')))

    def delete(self, songid):
        self.call('deleteid', str(songid))

    def clear(self):
        self.call('clear')

    def volUp(self):
        if self._volume == 0:
            vol = self.r_get_volume()
            try:
                newvol = vol + 5
            except:
                newvol = self._volume + 5
            return self.r_set_volume(newvol, 'Master')
        else:
            if self._volume > 95:
                return
            newvol = self._volume + 5
            return self.r_set_volume(newvol, 'Master')

    def volDown(self):
        if self._volume > 5:
            newvol = self._volume - 5
            return self.r_set_volume(newvol, 'Master')
        else:
            newvol = self._volume
            self._volume = 0
            return self.r_set_volume(newvol, '0')

    '''
    UPNP wrapped functions
    '''
    # onDemand vendor method

    def r_get_room(self):
        return self.room

    '''
    AVTransport and OpenHome Playlist
    '''

    def r_delete_all(self):
        return self.clear()

    def r_delete_id(self, value):
        return self.delete(value)

    def r_get_current_transport_actions(self, instanceID):
        return self.get_transport_actions()

    def r_get_media_info(self, instanceID):
        return (str(len(self.playlist)), self.get_track_duration(),
                self.get_track_URI(), self.get_track_md(), '', '', 'UNKNOWN',
                'UNKNOWN', 'UNKNOWN',)

    def r_get_media_info_ext(self, instanceID):
        return (str(len(self.playlist)), 'TRACK_AWARE',
                self.get_track_duration(), self.get_track_URI(),
                self.get_track_md(), '', '', 'UNKNOWN', 'UNKNOWN',
                'UNKNOWN',)

    def r_get_position_info(self, instanceID):
        return (self.player.get_track(), self.get_track_duration(),
                self.get_track_md(), self.player.get_track_URI(),
                self.player.get_reltime(), self.player.get_abstime(),
                self.player.get_relcount(), self.player.get_abscount(),)

    def r_get_transport_info(self, instanceID):
        return (self.get_state(), 'OK', self.get_rate(),)

    def r_id(self):
        return self.songid

    def r_id_array(self):
        return (self.token, self.idArray,)

    def r_id_array_changed(self, token):
        if token != self.token:
            return 1
        return 0

    def r_insert(self, afterid, url, metadata, checked=False):
        log.debug('Insert :%s  --  %s  --  %s' % (afterid, url, metadata))

        def inserted(res, md):
            log.debug(res)
            return res['Id']

        if 'youtube' in url and not checked:
            # eclipse workaround !!!
            y = os.environ
            y.update({'PYTHONPATH': '/usr/bin/python'})
            # /eclipse workaround
            d = utils.getProcessOutput(
                '/usr/bin/youtube-dl',
                ['-g', '-f', 'bestaudio', url],
                env=y,
                reactor=reactor)
            d.addCallback(
                lambda u: self.insert(
                    u.split('\n')[0], afterid, metadata, True))
            return d
        if len(self.playlist.tracks) == 0:
            d = self.call('addid', url)
        elif int(afterid) == 0:
            d = self.call('addid', url + ' 0')
        else:
            log.critical('crash ? %s' % self.playlist.tracks)
            log.critical('here ? %s' %
                         str(self.playlist.tracks.index(int(afterid)) + 1))
            d = self.call(
                'addid',
                ' '.join(
                    (url.encode('utf-8'),
                     str(self.playlist.tracks.index(int(afterid)) + 1))))
        d.addCallback(inserted, metadata)
        return d

    def r_next(self, instanceID=0):
        if self._state not in ('play', 'pause'):
            if self.songid == 0:
                self.r_play(1)
            else:
                self.r_play()
        else:
            self.call('next')

    def r_play(self, instanceID=0, speed=1, songid=None,
               ignored=None):

        log.debug('entering play...')

        def success(result):
            return None
        if self.cancelplay:
            self.cancelplay = False
        else:
            if songid is not None:
                #                 log.err(songid)
                d = self.call('playid', str(songid))
            else:
                if self._state == 'pause':
                    d = self.call('pause', '0')
                else:
                    d = self.call('playid', self.songid)
            d.addCallback(self.playing)

    def r_pause(self, instanceID=0):
        print('pause')

        def paused(ret):
            if self._state == 'play':
                self.set_state('pause')
        d = self.call('pause', '1')
        d.addCallback(paused)
        return d

    def r_previous(self, instanceID=0):
        self.call('previous')

    def r_protocol_info(self):
        return self.protocolinfo

    def r_read(self, value):
        log.debug('read')
        d = self.playlist.get_track(value)
        return (d,)

    def r_read_list(self, items):
        log.debug('readlist')
        d = self.playlist.get_tracks(
            [int(track) for track in items.split()])
        return d

    def r_repeat(self):
        return self.repeat

    def r_record(self, instanceID):
        raise NotImplementedError()

    def r_seek(self, instanceID, unit, pos):
        log.debug('seek: %s %s' % (unit, pos))
        self.set_position(pos)

    def r_seek_id(self, value):
        log.debug('SeekId')
        return self.r_play(songid=value)

    def r_seek_index(self, value):
        log.debug('Seekindex')
        return self.playindex(value)

    def r_seek_second_absolute(self, value):
        return self.set_position(value, 'SECONDS')

    def r_seek_second_relative(self, value):
        return self.set_position_relative(value, 'SECONDS')

    def r_set_repeat(self, repeat):
        self.call('repeat', str(int(repeat)))
        self.changed_state({'repeat': str(int(repeat))})

    def r_set_shuffle(self, shuffle):
        self.call('random', str(int(shuffle)))
        self.changed_state({'random': str(int(shuffle))})

    def r_set_avtransport_uri(self, instanceID, uri, uri_metadata):
        self.set_track_URI(uri, uri_metadata)

    def r_shuffle(self):
        return self.shuffle

    def r_stop(self, instanceID=0):
        def stopped(ret):
            self.set_state('stop')
        if self._state != 'STOPPED':
            d = self.call('stop')
            self.reltime = '00:00:00'
            d.addCallback(stopped)

    def r_tracks_max(self):
        return self.tracksmax

    def r_transport_state(self, instanceID=0):
        if self.parent.type == 'Source':
            return self.oh_state
        return self.upnp_state

    '''
    OpenHome Info
    '''

    def r_counters(self):
        return (self.trackcount, self.detailscount, self.metatextcount,)

    def r_track(self):
        return (self._track_URI, self.metadata_str,)

    def r_details(self):
        return (
            self.ohduration, self.bitrate, self.bitdepth,
            self.samplerate, self.lossless, self.codecname,)

    def r_metatext(self):
        return self.metatext

    '''
    Rendering Control and Open Home Volume
    '''

    def r_volume(self):
        return self._volume

    def r_set_volume(self, volume, channel=0):
        volume = str(volume)
        d = self.call('setvol', volume)
        d.addErrback(
            log.critical,
            'Set Volume Error : %s - %d' % (channel, int(volume)))
        reactor.callLater(0.1,  # @UndefinedVariable
                          self.changed_state,
                          {'volume': str(volume)})

    def r_volume_inc(self):
        return self.volUp()

    def r_volume_dec(self):
        return self.volDown()

    def r_volume_limit(self):
        return self.max_volume

    def r_balance(self):
        return self.balance

    def r_balance_inc(self):
        # TODO
        self.balance += 1

    def r_balance_dec(self):
        # TODO
        self.balance -= 1

    def r_set_fade(self, value):
        # TODO
        self.fade = int(value)

    def r_fade_inc(self):
        # TODO
        self.fade += 1

    def r_fade_dec(self):
        # TODO
        self.fade -= 1

    def r_mute(self):
        return self._muted

    def r_set_mute(self, mute):
        if mute is not self._muted:
            self._muted = mute
            if mute:
                self.old_vol = self._volume
                self.r_set_volume('0')
            else:
                self.r_set_volume(self.old_vol)

    def r_characteristics(self):
        return self.max_volume, self.volumeunity, self.max_volume,\
            self.volumemillidbperstep, self.balancemax, self.fademax

    '''
    OpenHome Time
    '''

    def r_time(self):
        return (self.trackcount, self.ohduration, self.get_reltime('seconds'))

    '''
    OpenHome Product
    '''

    def r_manufacturer(self=None):
        log.debug('Manufacturer from Product')
        return (self.parent.manufacturer,
                self.parent.manufacturerInfo, self.parent.manufacturerURL,
                ''.join((self.parent.getLocation(get_default_v4_address()),
                         '/pictures/icon.png')),)

    def r_model(self=None):
        log.debug('Model from Product')
        return (self.parent.modelName, self.parent.modelDescription,
                self.parent.manufacturerURL,
                ''.join((self.parent.getLocation(get_default_v4_address()),
                         '/pictures/', self.parent.modelName, '.png',)))

    def r_product(self):
        log.debug('Product from Product')
        return self.room, self.parent.modelName, self.parent.modelDescription,\
            self.parent.manufacturerURL,\
            ''.join((self.parent.getLocation(get_default_v4_address()),
                     '/pictures/', self.parent.modelName, '.png',))

    def r_standby(self):
        log.debug('Standby from Product')
        return self.standby

    def r_set_standby(self, val=None):
        log.debug('SetStandby from Product')
        if val is None:
            return self.standby
        raise NotImplementedError()

    def r_source_count(self):
        log.debug('SourceCount from Product')
        return len(self.sources)

    def r_source_xml(self, *args, **kwargs):
        log.debug('SourceXml from Product')
        return self.sourcexml
# return dict2xml({'SourceList': [{'Source': n} for n in self.sources]})

    def r_source_index(self):
        log.debug('SourceIndex from Product')
        return self.sourceindex

    def r_set_source_index(self, idx=None):
        log.debug('SetSourceIndex from Product')
        if idx is None:
            return self.sourceindex
        else:
            try:
                self.sourceindex = int(idx)
                self.oh_product_event('sourceindex', self.sourceindex)
            except:
                for i, source in enumerate(self.sources.keys()):
                    if source['Name'] == idx:
                        self.sourceindex = i
                        self.oh_product_event('sourceindex', self.sourceindex)
                        return
                    log.critical('Unknown Source: %s' % idx)

    def r_set_source_index_by_name(self, value):
        log.debug('SetSourceIndexByName from Product')
        return self.set_source_index(value)

    def r_source(self, idx):
        idx = int(idx)
        return (self.parent.sources[idx].friendlyName,
                self.parent.sources[idx].type, True,
                self.parent.sources[idx].name,)

    def r_attributes(self):
        return self.attributes

    def r_source_xml_change_count(self):
        raise NotImplementedError()
Exemplo n.º 5
0
class Omxclient(Service):
    bus = None
    addrs = None
    token = 0
    con = None
    _state = 'Pending'
    _volume = 1
    max_volume = 100
    seconds = 0
    uri = ''
    properties = None
    player = None
    is_connected = False
    pending = True
    event_msg = {
        'Paused': ['PAUSED_PLAYBACK', 'Paused'],
        'Playing': ['PLAYING', 'Playing'],
        'Pending': ['TRANSITIONNING', 'Buffering'],
        'Stopped': ['STOPPED', 'Stopped']
    }
    _metadata = {}
    metadata_str = ''
    metadata = {}
    songid = 0
    maxsongid = 0
    _duration = 0
    upnp_duration = '0:00:00'
    reltime = '0:00:00.000'
    _playlist = []
    playlist = []
    _track_URI = ''
    idArray = ''
    repeat = False
    shuffle = False
    tracksmax = 1000
    mtlist = ''
    timer = None
    _next = False

    def __init__(self, program='', args=None, **kwargs):
        if args:
            self.process_args = [program] + [arg for arg in args.split()]
        else:
            self.process_args = [program]
        self.process_path = program
        self.process = PlayerProcess(self)
        self.playername = program.split("/")[-1]

    def startService(self):
        self.con = DbusConnection()
        d = self.con.connect()
        d.addCallbacks(self.connected, self.not_connected)

    def connected(self, bus):
        log.msg('connected: %s' % bus)
        self.bus = bus

        def got_properties(proxy):
            d = self.player.get_proxy()
            d.addCallback(got_player)
            return d

        def got_player(proxy):
            self.con.watch_process('org.mpris.MediaPlayer2.omxplayer',
                                   self.connection_lost)
            self.pending = False
            reactor.callLater(
                6,  # @UndefinedVariable
                lambda: setattr(self, 'polling', True))
            reactor.callLater(  # @UndefinedVariable
                7, self.poll_status)

        if not self.properties:
            self.properties = ODbusProxy(
                self.bus,
                bus_name='org.mpris.MediaPlayer2.omxplayer',
                object_path='/org/mpris/MediaPlayer2',
                interface='org.freedesktop.DBus.Properties',
                timeout=5)
            self.player = ODbusProxy(
                self.bus,
                bus_name='org.mpris.MediaPlayer2.omxplayer',
                object_path='/org/mpris/MediaPlayer2',
                interface='org.mpris.MediaPlayer2.Player',
                timeout=5)
            d = self.properties.get_proxy()
        else:
            return defer.succeed(None)
        d.addCallback(got_properties)
        return d

    def connection_lost(self, *args, **kwargs):
        log.err('connection lost')
        #for arg in args:
        #log.err(arg)
        if 'really_lost' in kwargs:
            really_lost = kwargs['really_lost']
        else:
            really_lost = False
        if really_lost:
            self.is_connected = False
        else:
            self.is_connected = not self.is_connected
        if not self.is_connected:
            if self.process.launched:
                log.err('launched')
                self.connect()
            else:
                log.err('not launched')
                self.properties = None
                self.player = None
                self.polling = False
                self.changed_state({'PlaybackStatus': 'Stopped'})
                self.timer = None
                self.seconds = 0
                self.reltime = '0:00:00.000'
            #self.changed_state({'PlaybackStatus': 'Stopped'})

    def not_connected(self, err):
        self.bus = None
        log.err('Dbus Connection failed: %s' % err)

    def connect(self, err=None):
        log.msg('connect')
        if not self.bus:
            if not self.addrs:
                self.addrs = get_user_sessions()
                log.msg(self.addrs)
                if len(self.addrs) > 0:
                    if self.con:
                        d = self.con.connect_addr(self.addrs.pop(0))
                        d.addCallbacks(self.connected, self.connect)
                    else:
                        d = task.deferLater(reactor, 2, self.connect)
                else:
                    self.addrs = None
                    d = task.deferLater(reactor, 2, self.connect)
            return d
        else:
            return self.connected(self.bus)

    def poll_status(self):
        if self.polling:
            if not self.pending:
                if self.process.launched:
                    self.pending = True
                    d = self.properties.Position()
                    d.addCallbacks(self.update_position, self.call_failed)
                    d.addCallback(
                        lambda ignored: setattr(self, 'pending', False))
            reactor.callLater(5, self.poll_status)  # @UndefinedVariable

    def call_failed(self, err):
        self.pending = False
        log.msg(err)

    def got_event(self, *args, **kwargs):
        if args[0] == 'properties':
            if args[1][0] == 'org.mpris.MediaPlayer2.Player':
                self.changed_state(args[1][1])
            elif args[1][0] == 'org.mpris.MediaPlayer2.TrackList':
                self.changed_tracks()
        elif args[0] == 'tracklist':
            self.changed_tracks()

    def update_position(self, pos):
        if self._state in ('Paused', 'Stopped'):
            log.msg('del timer')
            self.seconds = int(pos) / 1000000
            self.timer = None
        elif self.timer:
            log.msg('timer alive')
            self.timer.set(int(pos) / 1000000)
        else:
            self.seconds = int(pos) / 1000000

    def changed_state(self, *args, **kwargs):
        for arg in args:
            if isinstance(arg, dict):
                if 'PlaybackStatus' in arg:
                    log.err(arg)
                    if self._state != arg['PlaybackStatus']:
                        self._state = arg['PlaybackStatus']
                        self.upnp_eventAV(self.event_msg[self._state][0],
                                          'TransportState')
                        self.oh_eventPLAYLIST(self.event_msg[self._state][1],
                                              'transportstate')
                if 'Volume' in arg:
                    if self._volume != arg['Volume']:
                        self._volume = arg['Volume']
                        self.upnp_eventRCS(int(self._volume * 100), 'Volume')
                        self.oh_eventVOLUME(int(self._volume * 100), 'volume')
                if 'Metadata' in arg:
                    self.update_metadata(arg['Metadata'])

    def update_metadata(self, metadata):
        songid = None
        if isinstance(metadata, dict):
            if self._metadata == metadata:
                return
            else:
                self._metadata = metadata
                self.metadata = mpris_decode(metadata)
        elif isinstance(metadata, str):
            if self.metadata_str == metadata:
                return
            else:
                self.metadata_str = metadata
                self.metadata = didl_decode(metadata)
                log.msg(self.metadata)
        else:
            log.err('Bad metadata format : %s' % metadata)
            return
        if 'songid' in self.metadata:
            if self.songid != int(self.metadata['songid']):
                songid = int(self.metadata['songid'])
        if songid:
            self.songid = songid
            self.upnp_eventAV(int(self.songid), 'CurrentTrack')
            self.oh_eventPLAYLIST(int(self.songid), 'id')
            self.oh_eventTIME(1, 'trackcount')
        if 'duration' in self.metadata:
            if self._duration != self.metadata['duration']:
                duration = int(self.metadata['duration'])
                log.msg('duration: %d' % duration)
                if duration < 1:
                    self.upnp_duration = "0:00:00"
                    self._duration = 0
                else:
                    self._duration = duration
                    self.upnp_duration = mpristime_to_upnptime(duration)
                log.msg('track length: %s' % self.upnp_duration,
                        loglevel=logging.DEBUG)
                self.upnp_eventAV(self.upnp_duration, 'CurrentTrackDuration')
                self.oh_eventINFO(int(self._duration // 1000000), 'duration')
                self.oh_eventTIME(int(self._duration // 1000000), 'duration')
        if 'url' in self.metadata:
            if self._track_URI != self.metadata['url']:
                self._track_URI = self.metadata['url']
                self.upnp_eventAV(self._track_URI, 'AVTransportURI')
                self.oh_eventINFO(self._track_URI, 'uri')
                self.upnp_eventAV(self._track_URI, 'CurrentTrackURI')
        if 'mpris:artUrl' in self.metadata:
            url = self.parent.register_art_url(self.metadata['mpris:artUrl'])
            self.metadata['albumArtURI'] = url
        self.oh_eventINFO(self.metadata_str, 'metadata')
        self.upnp_eventAV(self.metadata_str, 'AVTransportURIMetaData')

    def changed_tracks(self):
        self.oh_eventPLAYLIST(id_array(self.playlist), 'idarray')

    def upnp_eventAV(self, evt, var):
        pass

    def upnp_eventCM(self, evt, var):
        pass

    def upnp_eventRCS(self, evt, var):
        pass

    def oh_eventPLAYLIST(self, evt, var):
        pass

    def oh_eventINFO(self, evt, var):
        pass

    def oh_eventTIME(self, evt, var):
        pass

    def oh_eventVOLUME(self, evt, var):
        pass

    def get_track_id(self):
        return self.songid

    def get_track(self, track):
        ind = self.playlist.index(int(track))
        return defer.succeed((
            self._playlist[ind][1],
            self._playlist[ind][2],
        ))

    def get_tracks(self, tracks):
        tr = []
        for track in tracks:
            ind = self.playlist.index(int(track))
            tr.append((
                self._playlist[ind][0],
                self._playlist[ind][1],
                self._playlist[ind][2],
            ))
        tracks = tr
        if not isinstance(tracks, list):
            tracks = [tracks]
        tl = et.Element('TrackList')
        for track in tracks:
            #             log.err('track: %s' % track[0])
            en = et.Element('Entry')
            i = et.Element('Id')
            i.text = str(track[0])
            en.append(i)
            uri = et.Element('Uri')
            uri.text = track[1].decode('utf-8')
            en.append(uri)
            md = et.Element('Metadata')
            md.text = track[2]
            en.append(md)
            tl.append(en)
        return defer.succeed(et.tostring(tl))

    def get_state(self):
        return self._state

    def get_rate(self):

        return self.rate

    def get_track_duration(self):
        try:
            duration = self._track_duration
        except:
            duration = '00:00:00'
        return duration

    def get_track_URI(self):
        try:
            uri = self._track_URI
        except:
            uri = ''
        return uri

    def get_track_md(self):
        return self.metadata_str

    def get_relcount(self):
        return 2147483647

    def get_abscount(self):
        return 2147483647

    def get_abstime(self):
        return '00:00:00'

    def get_reltime(self, fmt='UPNP'):
        if self.timer is not None:
            s = self.timer.get()
            if (self._duration // 1000000 - s) < 2:
                if not self._next:
                    log.msg('next!!')
                    self._next = True
                    reactor.callLater(3, self.next)  # @UndefinedVariable
            if fmt == 'UPNP':
                t = mpristime_to_upnptime(s)
            elif fmt == 'seconds':
                t = int(s)
            else:
                # msec
                t = s
        else:
            if fmt == 'UPNP':
                t = self.reltime
            else:
                t = self.seconds
        return t

    def get_volume(self):
        if self.process.launched:

            def noVolume(err):
                if self._muted:
                    return 0
                else:
                    return self._volume

            def convert_volume(vol):
                self._volume = int(float(vol))
                log.msg("volume= %d" % self._volume, loglevel=logging.DEBUG)
                return self._volume

            d = self.properties.Volume()
            d.addCallbacks(convert_volume, noVolume)
            return d
        else:
            return 1

    def set_volume(self, channel, volume):
        if self.process.launched:
            if int(volume) != 0:
                d = self.properties.Volume(float(int(volume) / 100.00))
            else:
                if self._muted:
                    d = self.properties.Volume(float(self._volume))
                else:
                    d = self.properties.Volume(0.00)
            d.addErrback(self.call_failed)
            reactor.callLater(
                0.1,
                self.changed_state,  # @UndefinedVariable
                {'Volume': float(int(volume) / 100.00)})

    def set_track_URI(self, uri, md=''):
        log.msg("set track uri : %s " % uri, loglevel=logging.DEBUG)
        try:
            log.msg("current uri : %s " % self._track_URI,
                    loglevel=logging.DEBUG)
        except:
            pass
        if uri != self._track_URI:
            self.changed_state({'Metadata': md.encode('utf-8')})

    def set_position(self, newpos, fmt='UPNP'):
        if self.process.launched:

            def transition(obj):
                current_state = self._state
                self.changed_state({'PlaybackStatus': 'Pending'})
                reactor.callLater(  # @UndefinedVariable
                    0.5, self.changed_state, {'PlaybackStatus': current_state})

            if fmt == 'UPNP':
                newtime = upnptime_to_mpristime(newpos)
                offset = newtime - self.seconds
            else:
                offset = float(newpos) - self.seconds
            d = self.player.Seek(offset)
            d.addCallbacks(transition, self.call_failed)
            return d

    def set_repeat(self, repeat):
        log.err('repeat=%s' % repeat)
        self.repeat = repeat
        self.oh_eventPLAYLIST(self.repeat, 'repeat')

    def set_shuffle(self, shuffle):
        self.shuffle = shuffle

    def play(self, songid=None):
        log.err('play :%s' % songid)

        def playing(ignored, trackid=None, md=None):
            log.err('playing')
            self.pending = False
            if not self.timer:
                log.msg('create timer')
                self.timer = Timer()
                self.timer.set(self.seconds)
            else:
                log.msg('resume timer')
                self.timer.resume()
            if trackid:
                self.changed_state(
                    {
                        'PlaybackStatus': 'Playing',
                        'Metadata': {
                            'songid': trackid
                        }
                    }, {'Metadata': md})
            else:
                self.changed_state({'PlaybackStatus': 'Playing'})

        log.msg('from %s to %s' % (self._state, 'Play'))
        if songid:
            songid = int(songid)
            if self.process.launched:
                log.err('killing player')
                self.player.Stop()
                task.deferLater(reactor, 1, self.play, songid)
            else:
                volmb = 2000.0 * math.log10(self._volume)
                self.pending = True
                args = self.process_args\
                    + ['--vol']\
                    + [str(volmb)]\
                    + [self._playlist[self.playlist.index(songid)][1]]
                #                 args = self.process_args +\
                #                     [self._playlist[self.playlist.index(songid)][1]]
                reactor.spawnProcess(  # @UndefinedVariable
                    self.process,
                    self.process_path,
                    tuple(args),
                    env=os.environ)
                log.msg('play in one second')
                reactor.callLater(  # @UndefinedVariable
                    1, playing,
                    *(
                        0,
                        songid,
                        self._playlist[self.playlist.index(songid)][2],
                    ))
        else:
            if self.process.launched:
                if self._state == 'Paused':
                    if not self.pending:
                        self.pending = True
                        d = self.player.Pause()
                        d.addCallbacks(playing, self.call_failed)
                    else:
                        reactor.callLater(  # @UndefinedVariable
                            0.2, self.play)
            else:
                volmb = 2000.0 * math.log10(self._volume)
                self.pending = True
                args = self.process_args\
                    + ['--vol']\
                    + [str(volmb)]\
                    + [self.track_URI]
                reactor.spawnProcess(  # @UndefinedVariable
                    self.process,
                    self.process_path,
                    tuple(args),
                    env=os.environ)
                reactor.callLater(  # @UndefinedVariable
                    1, playing, *(0, self.songid, self.metadata_str))

    def pause(self):
        def paused(ignored):
            self.pending = False
            self.timer.stop()
            #             self.seconds = self.timer.get()
            #             self.timer = None
            self.changed_state({'PlaybackStatus': 'Paused'})

        log.msg('from %s to %s' % (self._state, 'Pause'))
        if self._state != 'Paused':
            if not self.pending:
                self.pending = True
                d = self.player.Pause()
                d.addCallbacks(paused, self.call_failed)
            else:
                reactor.callLater(0.3, self.pause)  # @UndefinedVariable

    def stop(self):
        if self._state != 'Stopped':
            self.timer = None
            self.seconds = 0
            self.player.Stop()
            self.reltime = '00:00:00'
            self.changed_state({'PlaybackStatus': 'Stopped'})

    def next(self):
        if self._next:
            self._next = False
        if len(self.playlist) > 0:
            if self.songid == self.playlist[-1]:
                if self.repeat:
                    self.play(songid=self.playlist[0])
            else:
                self.play(
                    songid=self.playlist[self.playlist.index(self.songid) + 1])

    def previous(self):
        if len(self.playlist) > 0:
            if self.songid == self.playlist[0]:
                if not self.repeat:
                    return
            self.play(songid=self.playlist[self.playlist.index(self.songid) -
                                           1])

    def volUp(self):
        if self._volume == 0:
            vol = self.get_volume()
            try:
                newvol = vol + 5
            except:
                newvol = self._volume * 100 + 5
            return self.set_volume('Master', newvol)
        else:
            if self._volume > 95:
                return
            newvol = self._volume * 100 + 5
            return self.set_volume('Master', newvol)

    def volDown(self):
        if self._volume > 0.05:
            newvol = self._volume * 100 - 5
            return self.set_volume('Master', newvol)
        else:
            self._volume = 0
            return self.set_volume('Master', 0)

    def insert(self, url, afterid, metadata, checked=False):
        if 'youtube' in url and not checked:
            # eclipse workaround !!!
            y = os.environ
            y.update({'PYTHONPATH': '/usr/bin/python'})  # / eclipse workaround
            d = utils.getProcessOutput('/usr/bin/youtube-dl',
                                       ['-g', '-f', 'bestaudio', url],
                                       env=y,
                                       reactor=reactor)
            d.addCallback(lambda u: self.insert(
                u.split('\n')[0], afterid, metadata, True))
            return d
#         log.err('playlist length:%s' % len(self._playlist))
        self.maxsongid += 1
        if len(self._playlist) == 0:
            self._playlist.append([self.maxsongid, url, metadata])
        else:
            if afterid == '0':
                self._playlist.insert(0, [self.maxsongid, url, metadata])
            else:
                self._playlist.insert(
                    self.playlist[self.playlist.index(int(afterid))],
                    [self.maxsongid, url, metadata])


#         log.err('real playlist: %s' % self._playlist)
        self.playlist = [i[0] for i in self._playlist]
        #         log.err('new playlist: %s' % self.playlist)
        #         log.err('metadata dic: %s' % metadata)
        self.oh_playlist = [str(i) for i in self.playlist]
        self.idArray = id_array(self.playlist)
        self.changed_tracks()
        if self.songid == 0:
            self.update_metadata({'songid': 1})
        return defer.succeed(self.maxsongid)

    def delete(self, songid):
        #  log.err(self.playlist)
        try:
            suppressed = self.playlist.index(int(songid))
        except IndexError:
            pass
        else:
            self._playlist.pop(suppressed)
            self.playlist.pop(suppressed)
            self.idArray = id_array(self.playlist)
            self.changed_tracks()

    def clear(self):
        self.playlist = []
        self._playlist = []
        self.songid = 0
        self.idArray = ''
        self.changed_state('TrackList', {}, '')