Beispiel #1
0
    def __init__(self, **kwargs):
        super(SqueezePlayerScreen, self).__init__(**kwargs)

        # We need the path to this folder to load icons
        scr = sys.modules[self.__class__.__module__].__file__
        self.plugindir = os.path.dirname(scr)

        # Set variables based on the parameters in the config file
        p = kwargs["params"]
        self.host = p["host"]["address"]
        self.webport = p["host"]["webport"]
        self.telnetport = p["host"]["telnetport"]
        self.cur_track["name"] = "Loading..."

        # Get reference to the box layout.
        self.bx = self.ids.squeeze_box

        # Create an object to handle retrieving artwork URLs
        self.awr = ArtworkResolver(host=self.host,
                                   port=self.webport,
                                   default="images/10x10_transparent.png")

        # Initialise some variables that we'll need later
        self.backendonline = False
        self.lms = None
        self.squeezeplayers = []
        self.cur_player = None
        self.now_playing = None
        self.currenttrack = None
        self.ct = {}
        self.inactive = True
        self.timer = None
        self.cbs = None
        self.sync_groups = []
        self.checker = None
Beispiel #2
0
class SqueezePlayerScreen(Screen):
    """Main screen object for SqueezePlayer.

       This screen handles the main initial contact with the server and sets
       up child objects as required.
    """
    cur_track = DictProperty({"name": "Loading..."})
    currentArt = StringProperty("images/10x10_transparent.png")

    def __init__(self, **kwargs):
        super(SqueezePlayerScreen, self).__init__(**kwargs)

        # We need the path to this folder to load icons
        scr = sys.modules[self.__class__.__module__].__file__
        self.plugindir = os.path.dirname(scr)

        # Set variables based on the parameters in the config file
        p = kwargs["params"]
        self.host = p["host"]["address"]
        self.webport = p["host"]["webport"]
        self.telnetport = p["host"]["telnetport"]
        self.cur_track["name"] = "Loading..."

        # Get reference to the box layout.
        self.bx = self.ids.squeeze_box

        # Create an object to handle retrieving artwork URLs
        self.awr = ArtworkResolver(host=self.host,
                                   port=self.webport,
                                   default="images/10x10_transparent.png")

        # Initialise some variables that we'll need later
        self.backendonline = False
        self.lms = None
        self.squeezeplayers = []
        self.cur_player = None
        self.now_playing = None
        self.currenttrack = None
        self.ct = {}
        self.inactive = True
        self.timer = None
        self.cbs = None
        self.sync_groups = []
        self.checker = None

    def on_enter(self):
        """Start the screen running."""
        self.timer = Clock.schedule_once(self.update, 0.1)
        if self.now_playing:
            self.now_playing.start()

    def on_leave(self):
        """Stop the screen."""
        Clock.unschedule(self.timer)
        if self.now_playing:
            self.now_playing.quit()

    def lmsLogon(self, host, port):
        """Log on to the Logitect Server and return a Server object."""
        try:
            sc = LMSServer(hostname=host, port=port)
            sc.connect()
        except:
            sc = None
        return sc

    def changePlayer(self, player):
        """Method to change the current player and update the screen."""
        if player != self.cur_player:
            self.cur_player = player
            self.squeezePlayer = self.getPlayer(self.cur_player)
            self.now_playing.player = self.squeezePlayer
            self.playlist_changed()
            self.track_changed()
            self.now_playing.update_players(self.squeezeplayers)

    def getSqueezePlayers(self, server):
        """Method to return list of currently active players."""
        try:
            sq = server.get_players()
        except:
            sq = None
        return sq

    def getPlayer(self, cur_player):
        """Method to return the current player. If current player is no longer
           available, this method returns the first available player."""
        pl = {x.get_ref(): x for x in self.squeezeplayers}

        if cur_player in pl:
            return pl[cur_player]

        else:
            return self.squeezeplayers[0]

    # Get current track information
    def getCurrentTrackInfo(self, playlist, pos):
        """Method to update the current playing track info with extra info."""
        track = {}

        # Need to check if there's a playlist, if not this would cause a crash
        if playlist:
            track = playlist[pos]
            track["pos"] = pos + 1
            track["elapsed"] = self.squeezePlayer.get_time_elapsed()

            # Get the artwork - get large version if possible...
            track["art"] = self.awr.getURL(track, size=(800, 800))

            # ...as we'll use as background too
            self.currentArt = track["art"]

        # No playlist so send some dummy info
        else:
            track = {"artist": "Playlist is empty",
                     "album": "Playlist is empty",
                     "title": "Playlist is empty",
                     "elapsed": 0,
                     "duration": 1,
                     "art": "10x10_transparent.png",
                     "pos": 0}

        return track

    def getCallbackServer(self):
        """Method to create and set up a callback server."""
        # Create the server
        cbs = LMSCallbackServer(hostname=self.host, port=self.telnetport)

        # Se up our callbacks
        cbs.add_callback(cbs.VOLUME_CHANGE, self.volume_change)
        cbs.add_callback(cbs.CLIENT_ALL, self.client_event)
        cbs.add_callback(cbs.PLAY_PAUSE, self.play_pause)
        cbs.add_callback(cbs.PLAYLIST_CHANGED, self.playlist_changed)
        cbs.add_callback(cbs.PLAYLIST_CHANGE_TRACK, self.track_changed)
        cbs.add_callback(cbs.SYNC, self.sync_event)

        # Deamonise the object so it dies if the main program dies.
        cbs.daemon = True

        return cbs

    def getCallbackPlayer(self, event):
        """Return the player reference from the callback event."""
        return self.cur_player if event is None else event.split(" ")[0]

    def checkCallbackServer(self, *args):
        """Checks if there's still a connection to the server and deletes
           callback server instance if there isn't.
        """
        self.cbs.check_connection()

    def cur_or_sync(self, ref):
        """Method to determine if the event player is our player or in a sync
           group with our player.
        """
        if ref == self.cur_player:
            return True

        else:
            for gr in self.sync_groups:
                if ref in gr and self.cur_player in gr:
                    return True

        return False

    def volume_change(self, event=None):
        """Method to handle callback for volume change event.

           Event should be:
             [player_ref] mixer volume [vol_amount]
        """
        if self.getCallbackPlayer(event) == self.cur_player:
            vol = self.squeezePlayer.get_volume()
            self.now_playing.vol_change(vol, False)

    def client_event(self, event=None):
        """Method to handle callback for client event.

           Expected events are:
             [player_ref] client new
             [player_ref] client disconnect
             [player_ref] client reconnect
             [player_ref] client forget
        """
        # Get the list of current players
        self.squeezeplayers = self.getSqueezePlayers(self.lms)

        # If there are none
        if not self.squeezeplayers:

            # update the screen to tell the user
            self.drawNoPlayer()

        # If our player disconnected, then we need to show a new one
        elif (self.getCallbackPlayer(event) == self.cur_player and
                event.split()[2] == "forget"):

            # get the player and update the screen
            self.squeezePlayer = self.getPlayer(self.cur_player)
            self.changePlayer(self.squeezePlayer.get_ref())

        # If this is the first player connecting
        elif self.squeezeplayers and not self.now_playing:

            # Get the player details
            self.squeezePlayer = self.getPlayer(self.cur_player)
            self.cur_player = self.squeezePlayer.get_ref()

            # Draw the screen
            self.createPlayerScreen()
            self.drawSqueezePlayers(self.squeezeplayers)

        # Another player connected/disconnected
        else:

            # Update the list of players
            self.now_playing.update_players(self.squeezeplayers)

        # Update list of sync groups
        if self.squeezeplayers:
            self.sync_groups = self.lms.get_sync_groups()

    def play_pause(self, event=None):
        """Method to handle callback for play pause event.

           Expected event is:
             [player_ref] playlist pause 0|1
        """
        # We're only interested in our sync group
        if (self.cur_or_sync(self.getCallbackPlayer(event)) and
                self.now_playing):

            # Update the now playing screen
            paused = (event.split()[3] == "1")
            self.now_playing.play_pause(paused)

    def playlist_changed(self, event=None):
        """Method to handle callback for a playlist change.

           Expected events are:
             [player_ref] playlist addtracks
             [player_ref] playlist loadtracks
             [player_ref] playlist delete
        """
        # If our playlist has changed
        if (self.cur_or_sync(self.getCallbackPlayer(event)) and
                self.now_playing):

            # Update the screen
            self.now_playing.updatePlaylist(self.getCurrentPlaylist())

            try:
                ev = event.split()
                if ev[2] == "clear":
                    # We know there are no tracks.
                    self.ct = self.getCurrentTrackInfo({}, 0)
                    self.now_playing.update(self.ct)

            except (IndexError, AttributeError):
                pass

    def track_changed(self, event=None):
        """Method to handle track change callback.

           Expected event:
             [player_ref] playlist newsong [playlist_position]
        """
        # We're only interested in our sync group
        if (self.cur_or_sync(self.getCallbackPlayer(event)) and
                self.now_playing):

            # Work out where we are in the playlist
            pos = int(self.squeezePlayer.playlist_get_position())
            self.playlistposition = pos
            plyl = {"pos": self.playlistposition,
                    "playlist": self.playlist}

            # Get the info for the current track
            self.ct = self.getCurrentTrackInfo(self.playlist,
                                               self.playlistposition)

            # Update the screen
            self.now_playing.update(self.ct)

    def sync_event(self, event=None):
        """Method to handle sync callback.

           Expected event:
             [player_ref] sync
        """
        self.now_playing.updatePlaylist(self.getCurrentPlaylist())
        self.squeezeplayers = self.getSqueezePlayers(self.lms)
        self.sync_groups = self.lms.get_sync_groups()
        pos = int(self.squeezePlayer.playlist_get_position())
        self.playlistposition = pos
        plyl = {"pos": self.playlistposition,
                "playlist": self.playlist}
        self.ct = self.getCurrentTrackInfo(self.playlist,
                                           self.playlistposition)
        self.now_playing.update(self.ct)

    def drawNoServer(self):
        """Method to tell the user that there's no server."""
        # Clear the screen
        self.bx.clear_widgets()
        self.now_playing = None

        # Create the label and display it.
        lb = Label(text="No Squeezeserver found. Please check your settings.")
        self.bx.add_widget(lb)

    def drawNoPlayer(self):
        """Method to tell the user that there are no players."""
        # Clear the screen
        self.bx.clear_widgets()
        self.now_playing = None

        # Create the label and display it.
        lb = Label(text="There are no players connected to your server.")
        self.bx.add_widget(lb)

    def checkForPlayers(self):
        """Before the callback server is running we need to run regular checks
           on the network."""

        # Clear the screen if there was something there
        if self.inactive:
            self.bx.clear_widgets()

        # Get list of players
        self.squeezeplayers = self.getSqueezePlayers(self.lms)

        # If there are players we need to set up our screen
        if self.squeezeplayers:
            self.squeezePlayer = self.getPlayer(self.cur_player)
            self.cur_player = self.squeezePlayer.get_ref()
            self.sync_groups = self.lms.get_sync_groups()
            self.inactive = False
            self.createPlayerScreen()
            self.drawSqueezePlayers(self.squeezeplayers)

        # If not, then we should say there are no players
        else:
            self.drawNoPlayer()
            self.inactive = True

    def getCurrentPlaylist(self):
        """Method to return the playlist for the current player."""
        # Get the playlist and current position
        self.playlist = self.squeezePlayer.playlist_get_info(taglist=TAGLIST)
        try:
            pos = int(self.squeezePlayer.playlist_get_position())
        except ValueError:
            pos = 0

        self.playlistposition = pos

        # Combine into a dict
        plyl = {"pos": self.playlistposition,
                "playlist": self.playlist}

        return plyl

    def createPlayerScreen(self):
        """Method to create the Now Playing screen."""
        # Clear the screen
        self.bx.clear_widgets()

        # Get the playlist
        plyl = self.getCurrentPlaylist()

        # Get the current track info
        self.ct = self.getCurrentTrackInfo(self.playlist,
                                           self.playlistposition)

        # Create the Now Playing object
        self.now_playing = SqueezeNowPlaying(cur_track=self.ct,
                                             height=480,
                                             size_hint_y=None,
                                             player=self.squeezePlayer,
                                             playlist=plyl,
                                             plugindir=self.plugindir,
                                             sq_root=self)

        # Add to the screen
        self.bx.add_widget(self.now_playing)

        # Make sure we start off showing the "Now Playing" section
        for c in self.now_playing.children:
            if c.title == "Now Playing":
                c.collapse = False

    def drawSqueezePlayers(self, sps):
        """Method to trigger the update of the list of squeeze players."""
        if self.now_playing:
            self.now_playing.update_players(sps)

    def update(self, *args):
        """Method to be run on clock interval."""
        # Set default update time
        interval = 5

        # Check if there is a CallbackServer instance.
        if not self.cbs:

            # No CallbackServer. Can we connect to LMS?
            self.lms = self.lmsLogon(self.host, self.telnetport)

            # If so then we can set up a few things
            if self.lms:

                # Create a CallbackServer instance...
                self.cbs = self.getCallbackServer()

                # ...and start it running
                self.cbs.start()

                # Set up a timer to check if the server is active
                check = self.checkCallbackServer
                self.checker = Clock.schedule_interval(check, 5)

                # If we don't have a Now Playing screen initialised
                if not self.now_playing:

                    # then we need to make one
                    self.checkForPlayers()

                    # We've got a callback server running so we don't need
                    # a regular interval now but we may want to test the
                    # connection every 15 seconds or so
                    interval = 15

            else:

                # There's no active server found
                self.drawNoServer()
                self.inactive = True

        else:

            # If the callback server has died (e.g. no connection)...
            if not self.cbs.isAlive():

                # Stop checking for the connection
                Clock.unschedule(self.checker)

                # Stop timers for now playing screen
                if self.now_playing:
                    self.now_playing.quit()

                # Remove the callback server
                del self.cbs
                self.cbs = None

        self.timer = Clock.schedule_once(self.update, interval)