Example #1
0
    def start_client(self):
        """ Start client thread """

        self.conn = MpdConnection(self.host, self.port)
        self.conn.connect()
        thread = threading.Thread(target=self.mpd_event_listener)
        thread.start()
Example #2
0
 def mpd_event_listener(self):
     """ Starts the loop for listening MPD events """
                     
     while self.playing:
         c = MpdConnection(self.host, self.port, reader_flags='r', encoding=None)
         c.connect()
         if not c.writer:
             continue
         c.writer.write(IDLE + "\n")
         c.writer.flush()
         line = None
         try:        
             line = c.reader.readline() # blocking line
         except:
             break
         if "player" in line:
             volume = self.get_volume()
             self.notify_volume_listeners(volume)
             current = self.current()
             current_title = None
             try:
                 current_title = current["Title"]
             except:
                 pass
             if current_title:
                 self.notify_player_listeners(current_title)                
Example #3
0
    def mpd_event_listener(self):
        """ Starts the loop for listening MPD events """

        while self.playing:
            c = MpdConnection(self.host,
                              self.port,
                              reader_flags='r',
                              encoding=None)
            c.connect()

            if not c.writer:
                continue

            c.writer.write(IDLE + "\n")
            c.writer.flush()

            line = None
            try:
                line = c.reader.readline()  # blocking line
                logging.debug("line from idle: " + line)
            except Exception as e:
                logging.debug(e)

            if line and "mixer" in line:
                volume = self.get_volume()
                self.notify_volume_listeners(volume)
                continue

            self.dispatch_callback(line)
            c.disconnect()
Example #4
0
class Mpdsocket(BasePlayer):
    """ This class extends base player and provides communication with MPD process using TCP/IP socket """
    def __init__(self):
        """ Initializer. Starts separate thread for listening MPD events """

        BasePlayer.__init__(self)
        self.host = "localhost"
        self.port = 6600  # this is default MPD port. If it was changed in mpd.conf it should be changed here as well
        self.muted = False
        self.playing = True
        self.conn = None
        self.dont_parse_track_name = False
        self.current_volume_level = "-1"

    def set_proxy(self, proxy_process, proxy=None):
        """ mpd socket client doesn't use proxy """

        pass

    def start_client(self):
        """ Start client thread """

        self.conn = MpdConnection(self.host, self.port)
        self.conn.connect()
        thread = threading.Thread(target=self.mpd_event_listener)
        thread.start()

    def mpd_event_listener(self):
        """ Starts the loop for listening MPD events """

        while self.playing:
            c = MpdConnection(self.host,
                              self.port,
                              reader_flags='r',
                              encoding=None)
            c.connect()

            if not c.writer:
                continue

            c.writer.write(IDLE + "\n")
            c.writer.flush()

            line = None
            try:
                line = c.reader.readline()  # blocking line
                logging.debug("line from idle: " + line)
            except Exception as e:
                logging.debug(e)

            if line and "mixer" in line:
                volume = self.get_volume()
                self.notify_volume_listeners(volume)
                continue

            self.dispatch_callback(line)
            c.disconnect()

    def dispatch_callback(self, line):
        """ Callback dispatcher
        
        :line: line from idle command
        """
        if self.player_mode == RADIO:
            self.handle_radio_callback()
        elif self.player_mode == CD_PLAYER:
            self.handle_cdplayer_callback(line)
        elif self.player_mode == AUDIO_FILES:
            self.handle_audiofiles_callback()
        elif self.player_mode == AUDIOBOOKS:
            self.handle_audiobooks_callback(line)
        elif self.player_mode == STREAM:
            self.handle_radio_callback()

    def handle_radio_callback(self):
        """ Radio callback handler """

        current = self.current()
        current_title = self.util.get_dictionary_value(current, "Title")
        if current_title == None:
            return

        current_title = current_title.strip()
        current["current_title"] = current_title
        current["state"] = self.util.get_dictionary_value(current, "state")
        current["source"] = "player"
        self.notify_player_listeners(current)

    def handle_audiofiles_callback(self):
        """ Audiofiles callback handler """

        current = self.current()
        status = self.status()
        current_file = self.util.get_dictionary_value(current, "file")
        current_title = self.util.get_dictionary_value(current, "Title")
        current["current_track_id"] = self.util.get_dictionary_value(
            current, "Track")

        if not current_title:
            try:
                if not self.dont_parse_track_name:
                    current_title = current["file"]
                    tokens = current_title.split("/")
                    current_title = tokens[len(tokens) - 1]
            except:
                pass

        if current_title == None and current_file == None:
            self.notify_end_of_track_listeners()
            return
        elif current_title:
            current_title = current_title.strip()
            current["current_title"] = current_title
            current["Time"] = self.util.get_dictionary_value(
                status, Player.DURATION)
            current["state"] = self.util.get_dictionary_value(status, "state")
            current["source"] = "player"
            track_time = self.util.get_dictionary_value(status, "time")
            if track_time:
                current["seek_time"] = track_time.replace(":", ".")
            self.notify_player_listeners(current)

    def handle_audiobooks_callback(self, line):
        """ Audiobooks callback handler
        
        :line: line from idle command
        """
        current = self.current()
        status = self.status()
        current_title = self.util.get_dictionary_value(current, "Title")
        current_file = self.util.get_dictionary_value(current, "file")

        if current_title == None and current_file == None and "player" in line:
            self.notify_end_of_track_listeners()
            return

        current_track_id = self.util.get_dictionary_value(current, "Track")
        current["current_track_id"] = current_track_id
        current["Time"] = self.util.get_dictionary_value(
            status, Player.DURATION)
        current["current_title"] = current_file
        t = self.util.get_dictionary_value(status, "time")
        if t:
            track_time = t.replace(":", ".")
            current["seek_time"] = track_time
        current["state"] = self.util.get_dictionary_value(current, "state")
        current["source"] = "player"
        if current_file:
            current_title = current["file"]
            tokens = current_file.split("/")
            current_title = tokens[len(tokens) - 1]
            ct = urllib.parse.unquote(current_title)
            current["file_name"] = ct

        self.notify_player_listeners(current)

    def handle_cdplayer_callback(self, line):
        """ CD player callback handler
        
        :line: line from idle command
        """
        current = self.current()
        status = self.status()
        current_file = current_title = current_track_id = None

        current_title = self.util.get_dictionary_value(current, "Title")
        current_file = self.util.get_dictionary_value(current, "file")
        current_track_id = self.util.get_dictionary_value(current, "Track")
        current["current_track_id"] = current_track_id
        current["Time"] = self.util.get_dictionary_value(
            status, Player.DURATION)
        current["state"] = status["state"]
        current["source"] = "player"

        if "playlist" in line and current_title == None:
            return

        if current_title == None and current_file == None:
            self.notify_end_of_track_listeners()
            return

        current["cd_track_id"] = self.cd_track_id
        current["file_name"] = "cdrom"
        if self.cd_tracks:
            current["current_title"] = self.cd_tracks[int(self.cd_track_id) -
                                                      1].name
        else:
            current[
                "current_title"] = self.cd_drive_name + self.cd_track_title + " " + self.cd_track_id

        try:
            track_time = status["time"].replace(":", ".")
            current["seek_time"] = track_time
        except:
            pass

        self.notify_player_listeners(current)

    def play(self, state):
        """ Start playing specified track/station. First it cleans the playlist 
        then adds new track/station to the list and then starts playback
        
        :param state: button state which contains the track/station info
        """
        self.state = state
        s = getattr(state, "playback_mode", None)
        track_time = self.get_track_time(state)

        if s and s == FILE_PLAYLIST:
            track_number = str(state.playlist_track_number)
            self.conn.command(PLAY + track_number)
            self.seek(track_time)
            self.mode = FILE_PLAYLIST
            return

        self.mode = FILE_AUDIO
        url = getattr(state, "url", None)
        if url == None: return

        file_name = getattr(state, "file_name", None)

        if file_name and getattr(state, "folder",
                                 None) and not url.startswith("http"):
            self.dont_parse_track_name = False
            url = self.get_url(state)
        else:
            self.dont_parse_track_name = True

        v = getattr(state, "volume", None)
        if v and v != self.current_volume_level:
            self.current_volume_level = v
            self.set_volume(v)

        if url.startswith("http") or url.startswith("https"):
            url = self.encode_url(url)
        elif url.startswith("cdda://"):
            if file_name:
                parts = file_name.split()
                self.cd_track_id = parts[1].split("=")[1]
                self.cd_drive_name = parts[0][len("cdda:///"):]
                url = parts[0].replace("////",
                                       "///") + os.sep + self.cd_track_id

        self.current_url = url

        batch = COMMAND_LIST_BEGIN + EOL
        batch += CLEAR + EOL
        batch += ADD + url + EOL
        batch += PLAY + '0' + EOL
        batch += COMMAND_LIST_END + EOL
        self.conn.command(batch)

        attempts = 100
        attempt = 0
        duration = None
        while not self.player_mode != RADIO and not self.player_mode != STREAM and duration == None and attempt < attempts:
            time.sleep(0.1)
            duration = self.util.get_dictionary_value(self.status(),
                                                      "duration")
            attempt += 1
            logging.debug("atempt " + str(attempt))

        if file_name and track_time != "0" and track_time != "0.0":
            self.seek(track_time)
            self.dispatch_callback("player")

        if getattr(state, "pause", None):
            self.pause()

    def get_track_time(self, state):
        """ Return track seek time
        
        :param state: state object
        :return: track seek time
        """
        track_time = getattr(state, "track_time", None)
        if track_time != None:
            track_time = str(track_time)
            if ":" in track_time:
                track_time = track_time.replace(":", ".")
        else:
            track_time = "0"

        return track_time

    def stop(self, state=None):
        """ Stop playback """

        if self.conn:
            self.conn.command(STOP)

    def seek(self, time):
        """ Jump to the specified position in the track
        
        :param time: time position in track
        """
        with self.lock:
            self.conn.command(SEEKCUR + time)

    def pause(self):
        """ Pause playback """

        self.conn.command(PAUSE)

    def play_pause(self, pause_flag=None):
        """ Play/Pause playback 
        
        :param pause_flag: play/pause flag
        """
        if pause_flag:
            self.conn.command(PAUSE)
        else:
            self.conn.command(RESUME)

        with self.lock:
            if self.muted:
                self.muted = False
                self.mute()
                self.muted = True
            else:
                self.conn.command(SET_VOLUME + str(self.current_volume_level))

    def set_volume(self, level):
        """ Set volume level
        
        :param level: new volume level
        """
        with self.lock:
            self.current_volume_level = level
            self.conn.command(SET_VOLUME + str(level))
            if self.muted:
                self.muted = False

    def get_volume(self):
        """  Return current volume level 
        
        :return: volume level or -1 if not available
        """
        with self.lock:
            st = self.status()
            volume = '-1'

            try:
                volume = st[GET_VOLUME]
            except KeyError:
                pass

            if volume == "-1":
                with self.lock:
                    volume = self.current_volume_level

            return int(volume)

    def mute(self):
        """ Mute """

        with self.lock:
            self.muted = not self.muted

            if self.muted:
                v = self.get_volume()
                if v != 0:
                    self.current_volume_level = v
                self.conn.command(MUTE_2)
            else:
                self.conn.command(SET_VOLUME + " " +
                                  str(self.current_volume_level))

    def status(self):
        """ Return the result of the STATUS command """

        with self.lock:
            return self.conn.read_dictionary(STATUS)

    def current(self):
        """ Return the current song """

        with self.lock:
            return self.conn.read_dictionary(CURRENT_SONG)

    def shutdown(self):
        """ Shutdown the player """

        with self.lock:
            self.playing = False

    def get_current_track_time(self):
        """  Return current track time
        
        :return: current track time
        """
        s = self.status()
        t = None
        try:
            t = s[Player.DURATION]
        except:
            pass
        return t

    def get_current_playlist(self):
        """  Return current playlist
        
        :return: current playlist
        """
        with self.lock:
            d = self.conn.read_dictionary(RADIO_PLAYLIST)
            playlist = []
            for n in range(len(d)):
                i = self.conn.read_dictionary(PLAYLIST_INFO + " " + str(n))
                playlist.append(i["Title"])
            return playlist

    def load_playlist(self, state):
        """  Load new playlist
        
        :param state: state object defining playlist location
        :return: new playlist
        """
        self.conn.command(CLEAR)
        self.conn.command(LOAD_PLAYLIST + self.get_url(state))
        return self.get_current_playlist()

    def notify_end_of_track_listeners(self):
        """  Notify end of track listeners. Starts new thread to unblock player loop. """

        thread = threading.Thread(target=self.handle_eof)
        thread.start()

    def handle_eof(self):
        """  End of track notifier. Runs in separate thread. """

        for listener in self.end_of_track_listeners:
            listener()
Example #5
0
 def start_client(self):
     """ Start client thread """
     
     self.conn = MpdConnection(self.host, self.port)
     thread = threading.Thread(target = self.mpd_event_listener)
     thread.start()
Example #6
0
class Mpdsocket(Player):
    """
    Communicate with MPD process directly using TCP/IP socket.
    Due to some weird timeout issue 'mpc' client is in use by default rather than this one.
    """    
    lock = threading.RLock()
    def __init__(self):
        """ Initializer. Starts separate thread for listening MPD events.
        
        :param host: host where MPD process is running
        :param port: port to which MPD process is listening to
        """
        self.host = "localhost"
        self.port = 6600
        self.mute_flag = False
        self.volume_listeners = [] 
        self.player_listeners = []
        self.playing = True
        self.conn = None                
    
    def set_platform(self, linux):
        """ Set platform flag """
                
        self.linux = linux
        
    def set_proxy(self, proxy):
        """ mpd socket client doesn't use proxy """
        
        pass
    
    def start_client(self):
        """ Start client thread """
        
        self.conn = MpdConnection(self.host, self.port)
        thread = threading.Thread(target = self.mpd_event_listener)
        thread.start()
    
    def mpd_event_listener(self):
        """ Starts the loop for listening MPD events """
                        
        while self.playing:
            c = MpdConnection(self.host, self.port, reader_flags='r', encoding=None)
            c.connect()
            if not c.writer:
                continue
            c.writer.write(IDLE + "\n")
            c.writer.flush()
            line = None
            try:        
                line = c.reader.readline() # blocking line
            except:
                break
            if "player" in line:
                volume = self.get_volume()
                self.notify_volume_listeners(volume)
                current = self.current()
                current_title = None
                try:
                    current_title = current["Title"]
                except:
                    pass
                if current_title:
                    self.notify_player_listeners(current_title)                
    
    def add_volume_listener(self, listener):
        """ Add volume listener
         
        :param listener: volume event listener
        """ 
        with self.lock:
            if listener not in self.volume_listeners: 
                self.volume_listeners.append(listener)
     
    def remove_volume_listener(self, listener):
        """ Remove volume listener
         
        :param listener: volume event listener
        """     
        with self.lock:
            if listener in self.volume_listeners: 
                self.volume_listeners.remove(listener)
     
    def add_player_listener(self, listener):
        """ Add player status listener
        
        :param listener: player status listener
        """
        with self.lock:
            if listener not in self.player_listeners: 
                self.player_listeners.append(listener)
     
    def remove_player_listener(self, listener):
        """ Remove player status listener
        
        :param listener: player status listener
        """
        with self.lock:
            if listener in self.player_listeners: 
                self.player_listeners.remove(listener)
     
    def notify_volume_listeners(self, volume):
        """ Notify volume listeners about new volume level
        
        :param volume: new volume level 
        """
        for listener in self.volume_listeners:
            listener(volume)
             
    def notify_player_listeners(self, status):
        """ Notify player listeners about player status change
        
        :param volume: new player status 
        """
        for listener in self.player_listeners:
            listener(status)
    
    def play(self, state):
        """ Start playing specified station. First it cleans the playlist 
        then adds new station to the list and then starts playing that station
        
        :param state: button state which contains the station to play
        """
        self.conn.command(CLEAR)
        self.conn.command(ADD + state.url)
        self.conn.command(PLAY + '0')
        
    def stop(self):
        """ Stop playback """
        
        self.conn.command(STOP)
    
    def play_pause(self):
        """ Play/pause playback """
        
        status = self.status()
        state = status.get(Player.STATE)
        
        if state is not Player.PAUSED:
            self.conn.command(PAUSE)
        else:
            self.conn.command(RESUME) 
    
    def set_volume(self, level):
        """ Set volume level
        
        :param level: new volume level
        """
        self.conn.command(SET_VOLUME_2 + str(level))        
        if self.mute_flag:
            self.mute_flag = False
    
    def get_volume(self):
        """  Return current volume level 
        
        :return: volume level or -1 if not available
        """
        with self.lock:
            st = self.status()
            volume = '-1'
            
            try:
                volume = st[GET_VOLUME]
            except KeyError:
                pass
            
            return int(volume)
    
    def mute(self):
        """ Mute """
        
        self.mute_flag = not self.mute_flag
        
        if self.mute_flag:
            self.current_volume_level = self.get_volume() 
            self.conn.command(MUTE_2)
        else:
            self.conn.command(SET_VOLUME_2 + " " + str(self.current_volume_level))
        
    def status(self):
        """ Return the result of the STATUS command """
        
        with self.lock:
            return self.conn.read_dictionary(STATUS)
        
    def current(self):
        """ Return the current song """
        
        with self.lock:
            return self.conn.read_dictionary(CURRENT_SONG)

    def shutdown(self):
        """ Shutdoen the player """
        
        self.playing = False