Author: titmuss
Date: Mon Jun 16 03:13:43 2008
New Revision: 2563

URL: http://svn.slimdevices.com?rev=2563&root=Jive&view=rev
Log:
Bug: 2558
Description:
Allow udap discovery and selecting SC using udap from Choose Player (while 
keeping the state in the 
Player object, and not in the Choose Player applet).


Modified:
    
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/ChooseMusicSource/ChooseMusicSourceApplet.lua
    
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/ChooseMusicSource/ChooseMusicSourceMeta.lua
    
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SelectPlayer/SelectPlayerApplet.lua
    
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SlimBrowser/SlimBrowserApplet.lua
    
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SqueezeDiscovery/SqueezeDiscoveryApplet.lua
    
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/jive/net/Udap.lua
    
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/jive/slim/Player.lua

Modified: 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/ChooseMusicSource/ChooseMusicSourceApplet.lua
URL: 
http://svn.slimdevices.com/7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/ChooseMusicSource/ChooseMusicSourceApplet.lua?rev=2563&root=Jive&r1=2562&r2=2563&view=diff
==============================================================================
--- 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/ChooseMusicSource/ChooseMusicSourceApplet.lua
 (original)
+++ 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/ChooseMusicSource/ChooseMusicSourceApplet.lua
 Mon Jun 16 03:13:43 2008
@@ -54,6 +54,12 @@
 local CONNECT_TIMEOUT = 30
 
 
+-- service to select server for a player
+function selectMusicSource(self)
+       self:settingsShow()
+end
+
+
 -- main setting menu
 function settingsShow(self)
 
@@ -62,17 +68,7 @@
        menu:setComparator(SimpleMenu.itemComparatorWeightAlpha)
        window:addWidget(menu)
 
-
-       -- Bug 7862
-       -- Workaround to allow changing music source using udap until
-       -- refactoring for bug 6683
        local current = appletManager:callService("getCurrentPlayer")
-       if current and not current:isConnected() and current:canUdap() and 
appletManager:hasApplet("SetupSqueezebox") then
-               local setupSqueezebox = 
appletManager:loadApplet("SetupSqueezebox")
-               setupSqueezebox:startSqueezeboxSetup(current:getMacAddress(), 
nil)
-               return
-       end
-
 
        self.serverMenu = menu
        self.serverList = {}

Modified: 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/ChooseMusicSource/ChooseMusicSourceMeta.lua
URL: 
http://svn.slimdevices.com/7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/ChooseMusicSource/ChooseMusicSourceMeta.lua?rev=2563&root=Jive&r1=2562&r2=2563&view=diff
==============================================================================
--- 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/ChooseMusicSource/ChooseMusicSourceMeta.lua
 (original)
+++ 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/ChooseMusicSource/ChooseMusicSourceMeta.lua
 Mon Jun 16 03:13:43 2008
@@ -28,23 +28,25 @@
 oo.class(_M, AppletMeta)
 
 
-function jiveVersion(self)
+function jiveVersion(meta)
        return 1, 1
 end
 
 
-function defaultSettings(self)
+function defaultSettings(meta)
        return {
                poll = { ["255.255.255.255"] = "255.255.255.255" }
        }
 end
 
 
-function registerApplet(self)
+function registerApplet(meta)
+       meta:registerService("selectMusicSource")
+
        -- set the poll list for discovery of slimservers based on our settings
        if appletManager:hasService("setPollList") then
-               appletManager:callService("setPollList", 
self:getSettings().poll)
-               jiveMain:addItem(self:menuItem('appletSlimservers', 'settings', 
"SLIMSERVER_SERVERS", function(applet, ...) applet:settingsShow(...) end, 60))
+               appletManager:callService("setPollList", 
meta:getSettings().poll)
+               jiveMain:addItem(meta:menuItem('appletSlimservers', 'settings', 
"SLIMSERVER_SERVERS", function(applet, ...) applet:settingsShow(...) end, 60))
        end
 end
 

Modified: 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SelectPlayer/SelectPlayerApplet.lua
URL: 
http://svn.slimdevices.com/7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SelectPlayer/SelectPlayerApplet.lua?rev=2563&root=Jive&r1=2562&r2=2563&view=diff
==============================================================================
--- 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SelectPlayer/SelectPlayerApplet.lua
 (original)
+++ 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SelectPlayer/SelectPlayerApplet.lua
 Mon Jun 16 03:13:43 2008
@@ -33,7 +33,6 @@
 local Label              = require("jive.ui.Label")
 local Framework          = require("jive.ui.Framework")
 
-local Udap               = require("jive.net.Udap")
 local hasWireless, Wireless  = pcall(require, "jive.net.Wireless")
 
 local log                = require("jive.utils.log").logger("applets.setup")
@@ -76,34 +75,38 @@
 end
 
 
-function notify_playerDelete(self, playerObj)
-       local mac = playerObj.id
+function notify_playerDelete(self, player)
+       local mac = player.id
        manageSelectPlayerMenu(self)
        if self.playerMenu and self.playerItem[mac] then
                self.playerMenu:removeItem(self.playerItem[mac])
                self.playerItem[mac] = nil
        end
 
-       self:_updateServerItem(playerObj:getSlimServer())
-end
-
-
-function notify_playerNew(self, playerObj)
+       if player:getSlimServer() then
+               self:_updateServerItem(player:getSlimServer())
+       end
+end
+
+
+function notify_playerNew(self, player)
        -- get number of players. if number of players is > 1, add menu item
-       local mac = playerObj.id
+       local mac = player.id
 
        manageSelectPlayerMenu(self)
        if self.playerMenu then
-               self:_addPlayerItem(playerObj)
-       end
-
-       self:_updateServerItem(playerObj:getSlimServer())
-end
-
-
-function notify_playerCurrent(self, playerObj)
+               self:_addPlayerItem(player)
+       end
+
+       if player:getSlimServer() then
+               self:_updateServerItem(player:getSlimServer())
+       end
+end
+
+
+function notify_playerCurrent(self, player)
        if self.playerMenu then
-               self.selectedPlayer = playerObj
+               self.selectedPlayer = player
                self:manageSelectPlayerMenu()
        end
 end
@@ -216,7 +219,7 @@
 function _refreshPlayerItem(self, player)
        local mac = player.id
 
-       if player:isConnected() then
+       if player:isConnected() or player:needsMusicSource() then
                local item = self.playerItem[mac]
                if not item then
                        -- add player
@@ -326,7 +329,7 @@
 
        self.selectedPlayer = appletManager:callService("getCurrentPlayer")
        for mac, player in appletManager:callService("iteratePlayers") do
-               if player:isConnected() then
+               if player:isConnected() or player:needsMusicSource() then
                        _addPlayerItem(self, player)
                end
        end
@@ -356,73 +359,17 @@
 
        window:addListener(EVENT_WINDOW_ACTIVE,
                           function()
-                                  self:_scanActive()
                                   self:_scan()
                           end)
 
-       window:addListener(EVENT_WINDOW_INACTIVE,
-                          function()
-                                  self:_scanInactive()
-                          end)
-
        self:tieAndShowWindow(window)
        return window
-end
-
-
-function _scanActive(self)
-       -- socket for udap discovery
-       self.udap = Udap(jnt)
-       if not self.udapSink then
-               self.udapSink = self.udap:addSink(function(chunk, err)
-                                                         self:_udapSink(chunk, 
err)
-                                                 end)
-       end
-end
-
-
-function _scanInactive(self)
-       if self.udapSink then
-               self.udap:removeSink(self.udapSink)
-               self.udapSink = nil
-       end
-end
-
-
-function _udapSink(self, chunk, err)
-       if chunk == nil then
-               return -- ignore errors
-       end
-
-       local pkt = Udap.parseUdap(chunk.data)
-
-       if pkt.uapMethod ~= "adv_discover"
-               or pkt.ucp.device_status ~= "wait_slimserver"
-               or pkt.ucp.type ~= "squeezebox" then
-               -- we are only looking for squeezeboxen trying to connect to SC
-               return
-       end
-
-       local mac = pkt.source
-       local name = pkt.ucp.name ~= "" and pkt.ucp.name
-       if not self.scanResults[mac] then
-               self.scanResults[mac] = {
-                       lastScan = os.time(),
-                       udap = true,
-               }
-
-               self:_addSqueezeboxItem(mac, name)
-       end
 end
 
 
 function _scan(self)
        -- SqueezeCenter and player discovery
        appletManager:callService("discoverPlayers")
-
-       -- udap discovery
-       local packet = Udap.createAdvancedDiscover(nil, 1)
-       self.udap:send(function() return packet end, "255.255.255.255")
 
        -- scan for players in setup state
        if hasWireless then
@@ -480,6 +427,11 @@
        -- set the current player
        appletManager:callService("setCurrentPlayer", player)
 
+       -- udap setup needed?
+       if player:needsMusicSource() then
+               appletManager:callService("selectMusicSource")
+       end
+
        return true
 end
 

Modified: 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SlimBrowser/SlimBrowserApplet.lua
URL: 
http://svn.slimdevices.com/7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SlimBrowser/SlimBrowserApplet.lua?rev=2563&root=Jive&r1=2562&r2=2563&view=diff
==============================================================================
--- 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SlimBrowser/SlimBrowserApplet.lua
 (original)
+++ 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SlimBrowser/SlimBrowserApplet.lua
 Mon Jun 16 03:13:43 2008
@@ -2131,13 +2131,13 @@
                self.volume:setPlayer(player)
        end
 
-    -- update the scanner object
-    self.scanner:setPlayer(player)
-
-       -- nothing to do if we don't have a player
+       -- update the scanner object
+       self.scanner:setPlayer(player)
+
+       -- nothing to do if we don't have a player or server
        -- NOTE don't move this, the code above needs to run when disconnecting
        -- for all players.
-       if not player then
+       if not player or not player:getSlimServer() then
                return
        end
 

Modified: 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SqueezeDiscovery/SqueezeDiscoveryApplet.lua
URL: 
http://svn.slimdevices.com/7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SqueezeDiscovery/SqueezeDiscoveryApplet.lua?rev=2563&root=Jive&r1=2562&r2=2563&view=diff
==============================================================================
--- 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SqueezeDiscovery/SqueezeDiscoveryApplet.lua
 (original)
+++ 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/applets/SqueezeDiscovery/SqueezeDiscoveryApplet.lua
 Mon Jun 16 03:13:43 2008
@@ -34,6 +34,7 @@
 local Timer         = require("jive.ui.Timer")
 
 local SocketUdp     = require("jive.net.SocketUdp")
+local Udap          = require("jive.net.Udap")
 
 local Player        = require("jive.slim.Player")
 local SlimServer    = require("jive.slim.SlimServer")
@@ -118,6 +119,27 @@
 end
 
 
+function _udapSink(self, chunk, err)
+       if chunk == nil then
+               return -- ignore errors
+       end
+
+       local pkt = Udap.parseUdap(chunk.data)
+
+       if pkt.uapMethod ~= "adv_discover"
+               or pkt.ucp.device_status ~= "wait_slimserver"
+               or pkt.ucp.type ~= "squeezebox" then
+               -- we are only looking for squeezeboxen trying to connect to SC
+               return
+       end
+
+       local playerId = string.gsub(pkt.source, 
"(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)", "%1:%2:%3:%4:%5:%6")
+
+       local player = Player(jnt, playerId)
+       player:updateUdap(pkt)
+end
+
+
 -- removes old servers
 local function _squeezeCenterCleanup(self)
        local now = Framework:getTicks()
@@ -129,16 +151,27 @@
                        activeServer ~= server and
                        now - server:getLastSeen() > DISCOVERY_TIMEOUT then
                
-                       log:info("Removing server ", server:getName())
+                       log:info("Removing server ", server)
                        server:free()
                end
        end
-
-       if log:isDebug() then
-               self:_debug()
-       end
-end
-
+end
+
+
+-- removes old unconfigured players
+local function _playerCleanup(self)
+       local now = Framework:getTicks()
+
+       for i, player in Player.iterate() do
+               if not player:getSlimServer() and
+                       self.currentPlayer ~= player and
+                       now - player:getLastSeen() > DISCOVERY_TIMEOUT then
+               
+                       log:info("Removing player ", player)
+                       player:free(false)
+               end
+       end
+end
 
 
 -- init
@@ -153,7 +186,15 @@
 
        -- slim discovery socket
        obj.socket = SocketUdp(jnt,
-                              function(...) _slimDiscoverySink(obj, ...) end)
+               function(...)
+                       _slimDiscoverySink(obj, ...)
+               end)
+
+       -- udap discovery socket
+       obj.udap = Udap(jnt, 
+               function(chunk, err)
+                       self:_udapSink(chunk, err)
+               end)
 
        -- discovery timer
        obj.timer = Timer(DISCOVERY_PERIOD,
@@ -176,13 +217,16 @@
 
 
 function _discover(self)
-       log:debug("discover")
-
        -- Broadcast SqueezeCenter discovery
        for i, address in pairs(self.poll) do
-               log:debug("sending to address ", address)
+               log:debug("sending slim discovery to ", address)
                self.socket:send(_slimDiscoverySource, address, PORT)
        end
+
+       -- UDAP discovery
+       local packet = Udap.createAdvancedDiscover(nil, 1)
+       log:debug("sending udap discovery to 255.255.255.255")
+       self.udap:send(function() return packet end, "255.255.255.255")
 
        -- Special case Squeezenetwork
        if jnt:getUUID() then
@@ -193,6 +237,9 @@
        -- Remove SqueezeCenters that have not been seen for a while
        _squeezeCenterCleanup(self)
 
+       -- Remove unconfigured Players that have not been seen for a while
+       _playerCleanup(self)
+
 
        if self.state == 'probing' and
                Framework:getTicks() > self.probeUntil then
@@ -204,6 +251,9 @@
                end
        end
 
+       if log:isDebug() then
+               self:_debug()
+       end
 
        self.timer:restart(DISCOVERY_PERIOD)
 end
@@ -262,7 +312,7 @@
        end
        log:info("Players:")
        for i, player in Player.iterate() do
-               log:info("\t", player:getName(), " server=", 
player:getSlimServer(), " connected=", player:isConnected())
+               log:info("\t", player:getName(), " [", player:getId(), "] 
server=", player:getSlimServer(), " connected=", player:isConnected(), " 
timeout=", DISCOVERY_TIMEOUT - (now - player:getLastSeen()))
        end
        log:info("----")
 end

Modified: 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/jive/net/Udap.lua
URL: 
http://svn.slimdevices.com/7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/jive/net/Udap.lua?rev=2563&root=Jive&r1=2562&r2=2563&view=diff
==============================================================================
--- 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/jive/net/Udap.lua
 (original)
+++ 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/jive/net/Udap.lua
 Mon Jun 16 03:13:43 2008
@@ -108,8 +108,11 @@
        uuid = _ucpString,
 }
 
-function __init(self, jnt)
+function __init(self, jnt, sink)
        if _instance then
+               if sink then
+                       _instance:addSink(sink)
+               end
                return _instance
        end
 
@@ -124,6 +127,12 @@
                                       end
                                       return 1
                               end)
+
+       if sink then
+               obj:addSink(sink)
+       end
+
+       _instance = obj
 
        return obj
 end
@@ -289,6 +298,8 @@
                bcast = 1
                mac = "000000000000"
        end
+
+       mac = string.gsub(mac, "[^%x]", "")
 
        for i=1,12,2 do
                macstr[#macstr + 1] = string.char(tonumber(string.sub(mac, i, 
i+1), 16))

Modified: 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/jive/slim/Player.lua
URL: 
http://svn.slimdevices.com/7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/jive/slim/Player.lua?rev=2563&root=Jive&r1=2562&r2=2563&view=diff
==============================================================================
--- 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/jive/slim/Player.lua
 (original)
+++ 
7.1/branches/discovery-refactor/squeezeplay/src/squeezeplay/share/jive/slim/Player.lua
 Mon Jun 16 03:13:43 2008
@@ -51,6 +51,8 @@
 local Window         = require("jive.ui.Window")
 local Group          = require("jive.ui.Group")
 
+local Udap           = require("jive.net.Udap")
+
 local debug          = require("jive.utils.debug")
 local strings        = require("jive.utils.strings")
 local log            = require("jive.utils.log").logger("player")
@@ -68,6 +70,13 @@
 
 -- jive.slim.Player is a base class
 module(..., oo.class)
+
+
+local DEVICE_IDS = {
+       [4] = "squeezebox2",
+       [5] = "transporter",
+       [7] = "receiver",
+}
 
 
 -- list of players index by id. this weak table is used to enforce
@@ -170,6 +179,7 @@
 
                uuid = false,
                slimServer = false,
+               config = false,
 
                -- player info from SC
                info = {},
@@ -211,6 +221,7 @@
        self.info = {}
 
        -- Update player info, cast to fix perl bugs :)
+       self.config = true
        self.info.uuid = tostring(playerInfo.uuid)
        self.info.name = tostring(playerInfo.name)
        self.info.model = tostring(playerInfo.model)
@@ -220,8 +231,7 @@
        self.info.isUpgrading = tonumber(playerInfo.player_is_upgrading) == 1
        self.info.pin = tostring(playerInfo.pin)
 
--- XXXX
-       debug.dump(self.info)
+       self.lastSeen = Framework:getTicks()
 
        -- PIN is removed from serverstatus after a player is linked
        if self.info.pin and not playerInfo.pin then
@@ -235,7 +245,9 @@
                        self:free(self.slimServer)
                end
 
-               -- the
+               -- modify the old state, as the player was not connected
+               -- to new SqueezeCenter. this makes sure the playerConnected
+               -- callback happens.
                oldInfo.connected = false
 
                -- player is now active
@@ -275,6 +287,32 @@
 end
 
 
+-- Update player state from a UDAP packet
+function updateUdap(self, udap)
+
+       self.config = "needsServer"
+       if udap.ucp.name == "" then
+               self.info.name = nil
+       else
+               self.info.name = tostring(udap.ucp.name)
+       end
+       self.info.model = DEVICE_IDS[tonumber(udap.ucp.device_id)]
+       self.info.connected = false
+
+       self.lastSeen = Framework:getTicks()
+
+       -- The player is no longer connected to SqueezeCenter
+       if self.slimServer then
+               self:free(self.slimServer)
+       end
+
+       -- player is now active
+       playerList[self.id] = self
+
+       self.jnt:notify('playerNew', self)
+end
+
+
 --[[
 
 =head2 jive.slim.Player:free(slimServer)
@@ -284,8 +322,6 @@
 =cut
 --]]
 function free(self, slimServer)
-       _assert(slimServer)
-
        if self.slimServer ~= slimServer then
                -- ignore, we are not connected to this server
                return
@@ -298,9 +334,11 @@
 
        self.jnt:notify('playerDelete', self)
 
-       self.slimServer:_deletePlayer(self)
-       self:offStage()
-       self.slimServer = nil
+       if self.slimserver then
+               self.slimServer:_deletePlayer(self)
+               self:offStage()
+               self.slimServer = false
+       end
 
        -- The global players table uses weak values, it will be removed
        -- when all references are freed.
@@ -427,7 +465,7 @@
 =cut
 --]]
 function __tostring(self)
-       return "Player {" .. self.info.name .. "}"
+       return "Player {" .. self:getName() .. "}"
 end
 
 
@@ -440,7 +478,11 @@
 =cut
 --]]
 function getName(self)
-       return self.info.name
+       if self.info.name then
+               return self.info.name
+       else
+               return "Squeezebox " .. string.gsub(string.sub(self.id, 10), 
":", "")
+       end
 end
 
 
@@ -985,8 +1027,86 @@
 
 -- tell the player to connect to another server
 function connectToServer(self, server)
-       local ip, port = server:getIpPort()
-       self:send({'connect', ip})
+
+       log:warn("*******************")
+
+       if self.config == "needsServer" then
+               log:warn("UDAP ####################")
+
+               _udapConnect(self, server)
+               return
+
+       elseif self.slimServer then
+               local ip, port = server:getIpPort()
+               self:send({'connect', ip})
+               return true
+
+       else
+               log:warn("No method to connect ", self, " to ", server)
+               return false
+       end
+end
+
+
+function parseip(str)
+       local ip = 0
+       for w in string.gmatch(str, "%d+") do
+               ip = ip << 8
+               ip = ip | tonumber(w)
+       end
+       return ip
+end
+
+
+function _udapConnect(self, server)
+       local data = {}
+
+       if server:isSqueezeNetwork() then
+               local sn_hostname = jnt:getSNHostname()
+
+               if sn_hostname == "www.squeezenetwork.com" then
+                       data.server_address = Udap.packNumber(1, 4)
+               elseif sn_hostname == "www.beta.squeezenetwork.com" then
+                       data.server_address = Udap.packNumber(1, 4)
+                       -- XXX the above should be this when "serv 2" in all 
firmware:
+                       -- data.server_address = Udap.packNumber(2, 4)
+               else
+                       -- for locally edited values (SN developers)
+                       local ip = socket.dns.toip(sn_hostname)
+                       data.server_address = Udap.packNumber(parseip(ip), 4)
+               end
+
+               log:info("SN server_address=", data.server_address)
+
+               -- set slimserver address to 0.0.0.1 to workaround a bug in
+               -- squeezebox firmware
+               data.slimserver_address = Udap.packNumber(parseip("0.0.0.1"), 4)
+       else
+               local serverip = server:getIpPort()
+
+               log:info("SC slimserver_address=", serverip)
+
+               data.server_address = Udap.packNumber(0, 4)
+               data.slimserver_address = Udap.packNumber(parseip(serverip), 4)
+       end
+
+       udap = Udap(self.jnt)
+
+       -- configure squeezebox network
+       -- XXXX move seqno management into Udap class
+       local seqno = 1
+       local packet = udap.createSetData(self.id, seqno, data)
+
+       -- send three udp packets in case the wireless network drops them
+       -- XXXX make udap class retry packets until ackd
+       udap:send(function() return packet end, "255.255.255.255")
+       udap:send(function() return packet end, "255.255.255.255")
+       udap:send(function() return packet end, "255.255.255.255")
+end
+
+
+function getLastSeen(self)
+       return self.lastSeen
 end
 
 
@@ -995,6 +1115,11 @@
 end
 
 
+function needsMusicSource(self)
+       return self.config == "needsServer"
+end
+
+
 --[[
 
 =head1 LICENSE

_______________________________________________
Jive-checkins mailing list
[email protected]
http://lists.slimdevices.com/cgi-bin/mailman/listinfo/jive-checkins

Reply via email to