private async Task <ConnectionResult> LoginAsync()
        {
            DacpRequest request = new DacpRequest("/login");

            request.QueryParameters["pairing-guid"] = "0x" + PairingCode;
            request.IncludeSessionID = false;

            try
            {
                var response = await SendRequestAsync(request).ConfigureAwait(false);

                // Process response
                var nodes = DacpNodeDictionary.Parse(response.Nodes);

                if (!nodes.ContainsKey("mlid"))
                {
                    return(ConnectionResult.InvalidPIN);
                }

                SessionID = nodes.GetInt("mlid");
                UpdateNowPlayingAlbumArtUri();
            }
            catch (DacpRequestException e)
            {
                int statusCode = (int)e.Response.StatusCode;
                if (statusCode >= 500 && statusCode <= 599)
                {
                    return(ConnectionResult.InvalidPIN);
                }
                return(ConnectionResult.ConnectionError);
            }
            catch { return(ConnectionResult.ConnectionError); }

            return(ConnectionResult.Success);
        }
        internal Task <bool> SetPropertyAsync(string propertyName, string value)
        {
            DacpRequest request = new DacpRequest("/ctrl-int/1/setproperty");

            request.QueryParameters[propertyName] = value;
            return(SendCommandAsync(request));
        }
 internal async Task <DacpResponse> SendRequestAsync(DacpRequest request)
 {
     if (request.IncludeSessionID)
     {
         request.QueryParameters["session-id"] = SessionID.ToString();
     }
     return(await SendRequestAsync(request.GetURI(), request.CancellationToken).ConfigureAwait(false));
 }
 internal async Task <bool> SendCommandAsync(DacpRequest request)
 {
     try
     {
         await SendRequestAsync(request).ConfigureAwait(false);
     }
     catch { return(false); }
     return(true);
 }
Beispiel #5
0
        public async Task <bool> DisableAsync()
        {
            DacpRequest request = new DacpRequest("/ctrl-int/1/setspeakers");

            request.QueryParameters["speaker-id"] = string.Join(",", Client.Speakers.Where(s => s.IsActive && s != this).Select(s => "0x" + s.ID.ToString("x")));

            try { await Client.SendRequestAsync(request).ConfigureAwait(false); }
            catch { return(false); }
            return(true);
        }
Beispiel #6
0
        public async Task <bool> EnableSingleAsync()
        {
            DacpRequest request = new DacpRequest("/ctrl-int/1/setspeakers");

            request.QueryParameters["speaker-id"] = "0x" + ID.ToString("x");

            try { await Client.SendRequestAsync(request).ConfigureAwait(false); }
            catch { return(false); }
            return(true);
        }
        public Task <bool> SendPreviousTrackCommandAsync()
        {
            // If the track doesn't change, we won't receive a play status update
            int totalMS = (int)CurrentTrackDuration.TotalMilliseconds;

            UpdateTrackTime(totalMS, totalMS);

            DacpRequest request = new DacpRequest("/ctrl-int/1/previtem");

            return(SendCommandAsync(request));
        }
        internal async Task <IDacpList> GetAlphaGroupedListAsync <T>(DacpRequest request, Func <byte[], T> itemGenerator, string listKey = DacpUtility.DefaultListKey)
        {
            try
            {
                var response = await SendRequestAsync(request).ConfigureAwait(false);

                return(DacpUtility.GetAlphaGroupedDacpList(response.Nodes, itemGenerator, listKey));
            }
            catch (Exception)
            {
                return(new DacpList <T>(false));
            }
        }
        internal async Task <List <T> > GetListAsync <T>(DacpRequest request, Func <DacpNodeDictionary, T> itemGenerator, string listKey = DacpUtility.DefaultListKey)
        {
            try
            {
                var response = await SendRequestAsync(request).ConfigureAwait(false);

                return(DacpUtility.GetItemsFromNodes(response.Nodes, itemGenerator, listKey).ToList());
            }
            catch (Exception)
            {
                return(new List <T>());
            }
        }
        public Task <bool> SendGeniusShuffleCommandAsync()
        {
            if (!ServerSupportsGeniusShuffle)
            {
                return(Task.FromResult(false));
            }

            DacpRequest request = new DacpRequest("/ctrl-int/1/genius-shuffle");

            // Apple's Remote seems to always set "span" to "$Q"
            request.QueryParameters["span"] = "$Q";

            return(SendCommandAsync(request));
        }
        public Task <bool> SendiTunesRadioNeverPlayThisSongCommandAsync()
        {
            if (!IsPlayingiTunesRadio)
            {
                return(Task.FromResult(false));
            }

            DacpRequest request = new DacpRequest("/ctrl-int/1/setproperty");

            request.QueryParameters["com.apple.itunes.liked-state"] = "3";
            request.QueryParameters["database-spec"] = DacpQueryPredicate.Is("dmap.itemid", "0x" + CurrentDatabaseID.ToString("x")).ToString();
            request.QueryParameters["item-spec"]     = DacpQueryPredicate.Is("dmap.itemid", "0x" + CurrentItemID.ToString("x")).ToString();

            return(SendCommandAsync(request));
        }
        private async Task <bool> GetSpeakersAsync()
        {
            DacpRequest request = new DacpRequest("/ctrl-int/1/getspeakers");

            try
            {
                var response = await SendRequestAsync(request).ConfigureAwait(false);

                var speakerNodes = response.Nodes.Where(n => n.Key == "mdcl").Select(n => DacpNodeDictionary.Parse(n.Value)).ToList();

                var speakers = Speakers;

                // Determine whether we need to replace the list of speakers
                bool replaceSpeakers = false;
                if (speakers == null || speakers.Count != speakerNodes.Count)
                {
                    replaceSpeakers = true;
                }
                else
                {
                    // Determine whether we still have the same speaker IDs
                    for (int i = 0; i < speakers.Count; i++)
                    {
                        if (speakers[i].ID != (UInt64)speakerNodes[i].GetLong("msma"))
                        {
                            replaceSpeakers = true;
                            break;
                        }
                    }
                }

                // Create the new list of speakers or update the existing speakers
                if (replaceSpeakers)
                {
                    Speakers = speakerNodes.Select(n => new AirPlaySpeaker(this, n)).ToList();
                }
                else
                {
                    for (int i = 0; i < speakers.Count; i++)
                    {
                        speakers[i].ProcessNodes(speakerNodes[i]);
                    }
                }
            }
            catch { return(false); }
            return(true);
        }
        private async Task <bool> GetVolumeLevelAsync()
        {
            DacpRequest request = new DacpRequest("/ctrl-int/1/getproperty");

            request.QueryParameters["properties"] = "dmcp.volume";

            try
            {
                var response = await SendRequestAsync(request).ConfigureAwait(false);

                var nodes = DacpNodeDictionary.Parse(response.Nodes);

                CurrentVolumeLevel = nodes.GetInt("cmvo");
            }
            catch { return(false); }
            return(true);
        }
Beispiel #14
0
        private async Task <bool> SetVolumeLevelAsync(int value, bool replaceMasterVolume)
        {
            DacpRequest request = new DacpRequest("/ctrl-int/1/setproperty");

            request.QueryParameters["dmcp.volume"] = value.ToString();
            if (replaceMasterVolume)
            {
                request.QueryParameters["include-speaker-id"] = ID.ToString();
            }
            else
            {
                request.QueryParameters["speaker-id"] = ID.ToString();
            }

            try { await Client.SendRequestAsync(request).ConfigureAwait(false); }
            catch { return(false); }
            return(true);
        }
        private async Task <bool> GetTrackTimePositionAsync()
        {
            DacpRequest request = new DacpRequest("/ctrl-int/1/getproperty");

            request.QueryParameters["properties"] = "dacp.playingtime";

            try
            {
                var response = await SendRequestAsync(request).ConfigureAwait(false);

                var nodes = DacpNodeDictionary.Parse(response.Nodes);

                int totalMS     = nodes.GetInt("cast");
                int?remainingMS = nodes.GetNullableInt("cant");
                UpdateTrackTime(totalMS, remainingMS);
            }
            catch { return(false); }
            return(true);
        }
        private async Task <bool> GetLibraryUpdateAsync(CancellationToken cancellationToken)
        {
            DacpRequest request = new DacpRequest("/update");

            request.QueryParameters["revision-number"]    = CurrentLibraryUpdateNumber.ToString();
            request.QueryParameters["daap-no-disconnect"] = "1";

            try
            {
                var response = await SendRequestAsync(request).ConfigureAwait(false);

                if (cancellationToken.IsCancellationRequested)
                {
                    return(false);
                }

                var nodes = DacpNodeDictionary.Parse(response.Nodes);
                CurrentLibraryUpdateNumber = nodes.GetInt("musr");
            }
            catch { return(false); }
            return(true);
        }
        private async Task <bool> GetServerCapabilitiesAsync()
        {
            DacpRequest request = new DacpRequest("/ctrl-int");

            request.IncludeSessionID = false;

            try
            {
                var response = await SendRequestAsync(request).ConfigureAwait(false);

                // Process response
                var mlcl  = DacpUtility.GetResponseNodes(response.Nodes.First(n => n.Key == "mlcl").Value);
                var nodes = DacpNodeDictionary.Parse(mlcl.First(n => n.Key == "mlit").Value);

                if (nodes.ContainsKey("ceSX"))
                {
                    Int64 ceSX = nodes.GetLong("ceSX");

                    // Bit 0: Supports Play Queue
                    if ((ceSX & (1 << 0)) != 0)
                    {
                        ServerSupportsPlayQueue = true;
                    }

                    // Bit 1: iTunes Radio? Appeared in iTunes 11.1.2 with the iTunes Radio DB.
                    // Apple's Remote for iOS doesn't seem to use this bit to determine whether iTunes Radio is available.
                    // Instead, it looks for an iTunes Radio database and checks whether it has any containers.

                    // Bit 2: Genius Shuffle Enabled/Available
                    if ((ceSX & (1 << 2)) != 0)
                    {
                        ServerSupportsGeniusShuffle = true;
                    }
                }
            }
            catch { return(false); }
            return(true);
        }
        private async Task <bool> GetServerInfoAsync()
        {
            DacpRequest request = new DacpRequest("/server-info");

            request.IncludeSessionID = false;

            try
            {
                var response = await SendRequestAsync(request).ConfigureAwait(false);

                // Process response
                ServerVersion = response.HTTPResponse.Headers.GetValueOrDefault("DAAP-Server");

                var nodes = DacpNodeDictionary.Parse(response.Nodes);
                ServerName = nodes.GetString("minm");
                //ServerVersion = nodes.GetInt("aeSV");
                //ServerDMAPVersion = nodes.GetInt("mpro");
                //ServerDAAPVersion = nodes.GetInt("apro");

                // MAC addresses
                if (nodes.ContainsKey("msml"))
                {
                    List <string> macAddresses = new List <string>();
                    var           addressNodes = DacpUtility.GetResponseNodes(nodes["msml"]).Where(n => n.Key == "msma").Select(n => n.Value);
                    foreach (var addressNode in addressNodes)
                    {
                        var address = BitConverter.ToInt64(addressNode, 0);
                        address = address >> 16;
                        macAddresses.Add(address.ToString("X12"));
                    }
                    ServerMacAddresses = macAddresses.ToArray();
                }
            }
            catch { return(false); }
            return(true);
        }
        private async Task <bool> GetPlayStatusUpdateAsync(CancellationToken cancellationToken)
        {
            // Do not pass the cancellation token to the HTTP request since canceling a request will cause iTunes to close the current session.
            DacpRequest request = new DacpRequest("/ctrl-int/1/playstatusupdate");

            request.QueryParameters["revision-number"] = _playStatusRevisionNumber.ToString();

            try
            {
                var response = await SendRequestAsync(request).ConfigureAwait(false);

                // Do we still need to process this response?
                if (cancellationToken.IsCancellationRequested)
                {
                    return(false);
                }

                // Process response
                var nodes = DacpNodeDictionary.Parse(response.Nodes);
                _playStatusRevisionNumber = nodes.GetInt("cmsr");

                // Current item and container IDs
                if (nodes.ContainsKey("canp"))
                {
                    byte[] value = nodes["canp"];

                    byte[] dbID            = { value[0], value[1], value[2], value[3] };
                    byte[] containerID     = { value[4], value[5], value[6], value[7] };
                    byte[] containerItemID = { value[8], value[9], value[10], value[11] };
                    byte[] itemID          = { value[12], value[13], value[14], value[15] };
                    CurrentDatabaseID      = dbID.GetInt32Value();
                    CurrentContainerID     = containerID.GetInt32Value();
                    CurrentContainerItemID = containerItemID.GetInt32Value();
                    CurrentItemID          = itemID.GetInt32Value();
                }
                else
                {
                    CurrentDatabaseID      = 0;
                    CurrentContainerID     = 0;
                    CurrentContainerItemID = 0;
                    CurrentItemID          = 0;
                }
                CurrentItemSignature = string.Format("{0}|{1}|{2}|{3}", CurrentDatabaseID, CurrentContainerID, CurrentContainerItemID, CurrentItemID);

                // Current item info
                CurrentArtistName        = nodes.GetString("cana");
                CurrentAlbumName         = nodes.GetString("canl");
                CurrentSongName          = nodes.GetString("cann");
                CurrentAlbumPersistentID = (UInt64)nodes.GetLong("asai");

                // Play state
                CurrentPlayState = (PlayState)nodes.GetByte("caps");

                // Track time
                UpdateTrackTime(nodes.GetInt("cast"), nodes.GetNullableInt("cant"));

                // Shuffle
                int caas = nodes.GetInt("caas");
                IsShuffleAvailable = (caas & (1 << 1)) != 0;
                CurrentShuffleMode = nodes.GetBool("cash");

                // Repeat
                int caar = nodes.GetInt("caar");
                IsRepeatOneAvailable = (caar & (1 << 1)) != 0;
                IsRepeatAllAvailable = (caar & (1 << 2)) != 0;
                IsRepeatAvailable    = (IsRepeatOneAvailable || IsRepeatAllAvailable);
                CurrentRepeatMode    = (RepeatMode)nodes.GetByte("carp");

                //CurrentMediaKind = nodes.GetInt("cmmk");
                //ShowUserRating = nodes.GetBool("casu");

                // dacp.visualizer
                IsVisualizerEnabled = nodes.GetBool("cavs");
                // dacp.visualizerenabled
                IsVisualizerAvailable = nodes.GetBool("cave");
                // dacp.fullscreen
                IsFullScreenModeEnabled = nodes.GetBool("cafs");
                // dacp.fullscreenenabled
                IsFullScreenModeAvailable = nodes.GetBool("cafe");

                // iTunes Radio
                iTunesRadioControlState newiTunesRadioControlState = 0;
                if (iTunesRadioDatabase != null && iTunesRadioDatabase.ID == CurrentDatabaseID)
                {
                    IsPlayingiTunesRadio          = true;
                    CurrentiTunesRadioStationName = nodes.GetString("ceNR");

                    // caks = 1 when the next button is disabled, and 2 when it's enabled
                    if (nodes.GetByte("caks") != 1)
                    {
                        newiTunesRadioControlState |= iTunesRadioControlState.NextButtonEnabled;
                    }

                    // "aelb" indicates whether the star button (iTunes Radio menu) should be enabled, but this only seems to be set to true
                    // when connected via Home Sharing. This parameter is missing when an ad is playing, so use this to determine whether
                    // the menu should be enabled.
                    if (nodes.ContainsKey("aelb"))
                    {
                        newiTunesRadioControlState |= iTunesRadioControlState.MenuEnabled;
                    }

                    if (nodes.GetByte("aels") == 2)
                    {
                        newiTunesRadioControlState |= iTunesRadioControlState.CurrentSongFavorited;
                    }
                }
                else
                {
                    IsPlayingiTunesRadio = false;
                }

                CurrentiTunesRadioControlState = newiTunesRadioControlState;

                IsTrackTimePositionBarEnabled = nodes.GetBool("casc", true);

                // Genius Shuffle
                IsPlayingGeniusShuffle = nodes.GetBool("ceGs");
                // There are two other nodes related to Genius Shuffle, "ceGS" and "aeGs" (currently unknown)

                if (CurrentPlayState == PlayState.FastForward || CurrentPlayState == PlayState.Rewind)
                {
                    BeginRepeatedTrackTimeRequests();
                }

                await GetVolumeLevelAsync().ConfigureAwait(false);
                await GetSpeakersAsync().ConfigureAwait(false);

                //var volumeTask = UpdateCurrentVolumeLevelAsync();
                //var userRatingTask = UpdateCurrentSongUserRatingAsync();
                //var playQueueTask = UpdatePlayQueueContentsAsync();

                //Task[] tasks = new[] { volumeTask, userRatingTask, playQueueTask };

#if WP7
                //await TaskEx.WhenAll(tasks).ConfigureAwait(false);
#else
                //await Task.WhenAll(tasks).ConfigureAwait(false);
#endif

                //SubmitGetSpeakersRequest();

                PlayStatusUpdated.RaiseOnUIThread(this, new EventArgs());
            }
            catch { return(false); }
            return(true);
        }
 internal Task <IDacpList> GetAlphaGroupedListAsync <T>(DacpRequest request, Func <DacpNodeDictionary, T> itemGenerator, string listKey = DacpUtility.DefaultListKey)
 {
     return(GetAlphaGroupedListAsync(request, b => itemGenerator(DacpNodeDictionary.Parse(b)), listKey));
 }
        public Task <bool> SendPlayResumeCommandAsync()
        {
            DacpRequest request = new DacpRequest("/ctrl-int/1/playresume");

            return(SendCommandAsync(request));
        }
        public Task <bool> SendBeginFastForwardCommandAsync()
        {
            DacpRequest request = new DacpRequest("/ctrl-int/1/beginff");

            return(SendCommandAsync(request));
        }
        public Task <bool> SendBeginRewindCommandAsync()
        {
            DacpRequest request = new DacpRequest("/ctrl-int/1/beginrew");

            return(SendCommandAsync(request));
        }
        public Task <bool> SendNextTrackCommandAsync()
        {
            DacpRequest request = new DacpRequest("/ctrl-int/1/nextitem");

            return(SendCommandAsync(request));
        }
        private async Task <bool> GetDatabasesAsync()
        {
            DacpRequest request = new DacpRequest("/databases");

            try
            {
                var databases = await GetListAsync(request, n => DacpDatabase.GetDatabase(this, n)).ConfigureAwait(false);

                if (databases == null || databases.Count == 0)
                {
                    return(false);
                }

                List <DacpDatabase> newSharedDatabases = new List <DacpDatabase>();

                for (int i = 0; i < databases.Count; i++)
                {
                    var db = databases[i];

                    // The main database will be first in the list
                    if (i == 0)
                    {
                        if (MainDatabase != null && MainDatabase.ID == db.ID)
                        {
                            continue;
                        }

                        bool success = await db.RequestContainersAsync().ConfigureAwait(false);

                        if (!success)
                        {
                            return(false);
                        }
                        MainDatabase = db;
                        continue;
                    }

                    // Shared database
                    if (db.Type == DatabaseType.Shared)
                    {
                        newSharedDatabases.Add(db);
                        continue;
                    }

                    // Internet Radio
                    if (db.Type == DatabaseType.InternetRadio)
                    {
                        if (InternetRadioDatabase != null && InternetRadioDatabase.ID == db.ID)
                        {
                            continue;
                        }

                        InternetRadioDatabase = db;
                        continue;
                    }

                    // iTunes Radio
                    if (db.Type == DatabaseType.iTunesRadio)
                    {
                        if (iTunesRadioDatabase != null && iTunesRadioDatabase.ID == db.ID)
                        {
                            continue;
                        }

                        iTunesRadioDatabase = (iTunesRadioDatabase)db;
                        // Attempt to load the stations asynchronously to determine whether iTunes Radio is enabled.
                        var task = iTunesRadioDatabase.RequestStationsAsync();
                        continue;
                    }
                }

                // Update shared databases
                Dictionary <int, DacpDatabase> removedSharedDBs = SharedDatabases.ToDictionary(db => db.ID);
                foreach (var sharedDB in newSharedDatabases)
                {
                    removedSharedDBs.Remove(sharedDB.ID);
                    if (SharedDatabases.Any(db => db.ID == sharedDB.ID))
                    {
                        continue;
                    }
                    SharedDatabases.Add(sharedDB);
                }
                foreach (DacpDatabase db in removedSharedDBs.Values)
                {
                    SharedDatabases.Remove(db);
                }

                Databases.Clear();
                Databases.AddRange(databases);
            }
            catch { return(false); }
            return(true);
        }