internal async Task <DACPResponse> SubmitRequestAsync(DACPRequest request)
        {
            if (request.IncludeSessionID)
            {
                request.QueryParameters["session-id"] = SessionID.ToString();
            }

            string uri = request.GetURI();

            _log.Info("Submitting request for: " + uri);

            HttpResponseMessage response = await HttpClient.PostAsync(uri, request.HttpContent, request.CancellationToken).ConfigureAwait(false);

            if (!response.IsSuccessStatusCode)
            {
                _log.Info("Invalid response ({0}) for: {1}", response.StatusCode, uri);
                throw new DACPRequestException(response);
            }

            byte[] data = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);

            _log.Info("Received response for: " + uri);

            // Get the content of the first node
            IEnumerable <DACPNode> nodes = null;

            if (data.Length > 0)
            {
                data  = DACPUtility.GetResponseNodes(data, true).First().Value;
                nodes = DACPUtility.GetResponseNodes(data);
            }

            return(new DACPResponse(response, nodes));
        }
        protected 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 SubmitRequestAsync(request).ConfigureAwait(false);

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

                var nodes = DACPNodeDictionary.Parse(response.Nodes);
                CurrentLibraryUpdateNumber = nodes.GetInt("musr");
            }
            catch
            {
                return(false);
            }
            return(true);
        }
        protected async Task <bool> UpdateCurrentSongUserRatingAsync()
        {
            // Make sure we have all the values we need
            if (CurrentDatabaseID == 0 || CurrentContainerID == 0 || CurrentItemID == 0 || CurrentAlbumPersistentID == 0)
            {
                ClearCurrentSongUserRating();
                return(true);
            }

            // Make sure this is for the main DB
            if (!ShowUserRating || MainDatabase == null || CurrentDatabaseID != MainDatabase.ID)
            {
                ClearCurrentSongUserRating();
                return(true);
            }

            // If we're requesting the rating for a new song, clear out the old value
            if (CurrentItemID != _ratingUpdatedForSongID)
            {
                ClearCurrentSongUserRating();
            }

            DACPRequest request = new DACPRequest("/databases/{0}/containers/{1}/items", CurrentDatabaseID, CurrentContainerID);

            request.QueryParameters["meta"] = "dmap.itemid,dmap.containeritemid,daap.songuserrating";
            request.QueryParameters["type"] = "music";
            request.QueryParameters["sort"] = "album";
            var query = DACPQueryCollection.And(DACPQueryPredicate.Is("daap.songalbumid", CurrentAlbumPersistentID), DACPQueryPredicate.Is("dmap.itemid", CurrentItemID));

            request.QueryParameters["query"] = query.ToString();

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

                var mlcl = response.Nodes.First(n => n.Key == "mlcl");

                var songNodes = DACPUtility.GetResponseNodes(mlcl.Value);
                foreach (var songData in songNodes)
                {
                    var nodes = DACPNodeDictionary.Parse(songData.Value);
                    var id    = nodes.GetInt("miid");
                    if (id != CurrentItemID)
                    {
                        continue;
                    }
                    var rating = nodes.GetByte("asur");
                    SetCurrentSongUserRatingFromServer(rating);
                    break;
                }
            }
            catch
            {
                ClearCurrentSongUserRating();
                return(false);
            }
            return(true);
        }
        internal async Task <List <T> > GetListAsync <T>(DACPRequest request, Func <DACPNodeDictionary, T> itemGenerator, string listKey = DACPUtility.DefaultListKey)
        {
            try
            {
                var response = await SubmitRequestAsync(request).ConfigureAwait(false);

                return(DACPUtility.GetItemsFromNodes(response.Nodes, itemGenerator, listKey).ToList());
            }
            catch (Exception)
            {
                return(new List <T>());
            }
        }
        protected async Task <bool> SetVolumeLevelAsync(int value)
        {
            DACPRequest request = new DACPRequest("/ctrl-int/1/setproperty");

            request.QueryParameters["dmcp.volume"] = value.ToString();

            try
            {
                await SubmitRequestAsync(request).ConfigureAwait(false);
            }
            catch { return(false); }
            return(true);
        }
        internal async Task <IDACPList> GetAlphaGroupedListAsync <T>(DACPRequest request, Func <byte[], T> itemGenerator, string listKey = DACPUtility.DefaultListKey)
        {
            try
            {
                var response = await SubmitRequestAsync(request).ConfigureAwait(false);

                return(DACPUtility.GetAlphaGroupedDACPList(response.Nodes, itemGenerator, listKey));
            }
            catch (Exception)
            {
                return(new DACPList <T>(false));
            }
        }
예제 #7
0
        public async Task <bool> Play()
        {
            DACPRequest request = new DACPRequest("/ctrl-int/1/playqueue-edit");

            request.QueryParameters["command"] = "playnow";
            request.QueryParameters["index"]   = QueueIndex.ToString();

            try
            {
                await Server.SubmitRequestAsync(request).ConfigureAwait(false);
            }
            catch { return(false); }
            return(true);
        }
        protected async Task <bool> GetServerInfoAsync()
        {
            DACPRequest request = new DACPRequest("/server-info");

            request.IncludeSessionID = false;

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

                // Process response
                ServerVersionString = response.HTTPResponse.Headers.GetValues("DAAP-Server").FirstOrDefault();

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

                // Fixing an issue with Apple TV devices where \0 may be appended to the end of the library name
                string libraryName = nodes.GetString("minm");
                if (!string.IsNullOrEmpty(libraryName))
                {
                    libraryName = libraryName.Replace("\0", "");
                }
                LibraryName = libraryName;

                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"));
                    }
                    MACAddresses = macAddresses.ToArray();
                }
            }
            catch (Exception e)
            {
                HandleHTTPException(request, e);
                return(false);
            }

            return(true);
        }
        protected async Task <bool> SetUserRatingAsync(int rating, int songID)
        {
            DACPRequest request = new DACPRequest("/ctrl-int/1/setproperty");

            request.QueryParameters["dacp.userrating"] = rating.ToString();
            request.QueryParameters["database-spec"]   = DACPQueryPredicate.Is("dmap.persistentid", "0x" + CurrentDatabaseID.ToString("x16")).ToString();
            request.QueryParameters["item-spec"]       = DACPQueryPredicate.Is("dmap.itemid", "0x" + songID.ToString("x")).ToString();

            try
            {
                await SubmitRequestAsync(request).ConfigureAwait(false);
            }
            catch { return(false); }
            return(true);
        }
예제 #10
0
        public async Task <bool> SendGeniusShuffleCommandAsync()
        {
            if (!SupportsGeniusShuffle)
            {
                return(false);
            }

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

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

            try { await SubmitRequestAsync(request); }
            catch { return(false); }
            return(true);
        }
예제 #11
0
        protected async Task <bool> GetServerCapabilitiesAsync()
        {
            DACPRequest request = new DACPRequest("/ctrl-int");

            request.IncludeSessionID = false;

            try
            {
                var response = await SubmitRequestAsync(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)
                    {
                        SupportsPlayQueue = 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)
                    {
                        SupportsGeniusShuffle = true;
                    }
                }

                // Apple TV
                // TODO: Is this the best way to detect this?
                IsAppleTV = nodes.GetBool("ceDR");
            }
            catch (Exception e)
            {
                HandleHTTPException(request, e);
                return(false);
            }

            return(true);
        }
예제 #12
0
        protected async Task <bool> UpdateCurrentVolumeLevelAsync()
        {
            DACPRequest request = new DACPRequest("/ctrl-int/1/getproperty");

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

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

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

                CurrentVolume = (byte)nodes.GetInt("cmvo");
            }
            catch { return(false); }
            return(true);
        }
예제 #13
0
        public async Task <bool> SendiTunesRadioNeverPlayThisSongAsync()
        {
            if (!IsCurrentlyPlayingiTunesRadio)
            {
                return(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();

            try { await SubmitRequestAsync(request); }
            catch { return(false); }
            return(true);
        }
예제 #14
0
        protected async Task <bool> UpdateTrackTimeAsync()
        {
            DACPRequest request = new DACPRequest("/ctrl-int/1/getproperty");

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

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

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

                TrackTimeTotal     = nodes.GetInt("cast");
                TrackTimeRemaining = nodes.GetNullableInt("cant") ?? TrackTimeTotal;
            }
            catch { return(false); }
            return(true);
        }
예제 #15
0
        /// <summary>
        /// Requests an update for the keyboard state and session ID.
        /// </summary>
        private async Task <bool> RequestAppleTVKeyboardInfoUpdateAsync()
        {
            List <byte> contentBytes = new List <byte>();

            contentBytes.AddRange(DACPUtility.GetDACPFormattedBytes("cmcc", "0"));
            contentBytes.AddRange(DACPUtility.GetDACPFormattedBytes("cmbe", "PromptResendReq"));

            ByteArrayContent content = new ByteArrayContent(contentBytes.ToArray());

            DACPRequest request = new DACPRequest("/ctrl-int/1/controlpromptentry");

            request.HttpContent = content;

            try
            {
                await SubmitRequestAsync(request).ConfigureAwait(false);
            }
            catch { return(false); }
            return(true);
        }
예제 #16
0
        protected async Task <bool> UpdatePlayQueueContentsAsync()
        {
            if (!SupportsPlayQueue)
            {
                return(false);
            }

            DACPRequest request = new DACPRequest("/ctrl-int/1/playqueue-contents");

            request.QueryParameters["span"] = "50";

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

                HandlePlayQueueResponse(response.Nodes);
            }
            catch { return(false); }
            return(true);
        }
예제 #17
0
        /// <summary>
        /// Requests an update for the virtual trackpad connection parameters.
        /// </summary>
        private async Task <bool> RequestAppleTVTrackpadInfoUpdateAsync()
        {
            List <byte> contentBytes = new List <byte>();

            contentBytes.AddRange(DACPUtility.GetDACPFormattedBytes("cmcc", "0"));
            contentBytes.AddRange(DACPUtility.GetDACPFormattedBytes("cmbe", "DRPortInfoRequest"));
            contentBytes.AddRange(DACPUtility.GetDACPFormattedBytes("cmte", string.Format("{0},0x{1}", AppleTVEncryptionKey, PairingCode)));

            ByteArrayContent content = new ByteArrayContent(contentBytes.ToArray());

            DACPRequest request = new DACPRequest("/ctrl-int/1/controlpromptentry");

            request.HttpContent = content;

            try
            {
                await SubmitRequestAsync(request).ConfigureAwait(false);
            }
            catch { return(false); }
            return(true);
        }
예제 #18
0
        protected async Task <ConnectionResult> LoginAsync()
        {
            DACPRequest request = new DACPRequest("/login");

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

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

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

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

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

            return(ConnectionResult.Success);
        }
예제 #19
0
        private async Task <bool> SendAppleTVKeyboardStringUpdateCommandAsync(string value, bool done)
        {
            string command = (done) ? "PromptDone" : "PromptUpdate";

            List <byte> contentBytes = new List <byte>();

            contentBytes.AddRange(DACPUtility.GetDACPFormattedBytes("cmcc", _appleTVKeyboardSessionID));
            contentBytes.AddRange(DACPUtility.GetDACPFormattedBytes("cmbe", command));
            contentBytes.AddRange(DACPUtility.GetDACPFormattedBytes("cmte", value));

            ByteArrayContent content = new ByteArrayContent(contentBytes.ToArray());

            DACPRequest request = new DACPRequest("/ctrl-int/1/controlpromptentry");

            request.HttpContent = content;

            try
            {
                await SubmitRequestAsync(request).ConfigureAwait(false);
            }
            catch { return(false); }
            return(true);
        }
예제 #20
0
        protected async Task <bool> GetControlPromptUpdateAsync(CancellationToken cancellationToken)
        {
            DACPRequest request = new DACPRequest("/controlpromptupdate");

            request.QueryParameters["prompt-id"] = _controlPromptUpdateNumber.ToString();

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

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

                _controlPromptUpdateNumber = response.Nodes.First(n => n.Key == "miid").Value.GetInt32Value();

                // Parse response
                // This comes back as a list of string key/value pairs.
                var nodeDictionary = response.Nodes.Where(n => n.Key == "mdcl").Select(n => DACPNodeDictionary.Parse(n.Value)).ToDictionary(n => n.GetString("cmce"), n => n.GetString("cmcv"));

#if DEBUG
                if (_log.EffectiveLevel <= LogLevel.Trace)
                {
                    string logMessage = "ControlPromptUpdate Response:\r\n";
                    foreach (var kvp in nodeDictionary)
                    {
                        logMessage += string.Format("{0}: {1}\r\n", kvp.Key, kvp.Value);
                    }
                    _log.Trace(logMessage);
                }
#endif

                if (nodeDictionary.ContainsKey("kKeybMsgKey_MessageType"))
                {
                    switch (nodeDictionary["kKeybMsgKey_MessageType"])
                    {
                    case "0":     // Show keyboard
                        CurrentAppleTVKeyboardTitle           = nodeDictionary.GetValueOrDefault("kKeybMsgKey_Title");
                        CurrentAppleTVKeyboardSubText         = nodeDictionary.GetValueOrDefault("kKeybMsgKey_SubText");
                        CurrentAppleTVKeyboardString          = nodeDictionary.GetValueOrDefault("kKeybMsgKey_String");
                        _appleTVKeyboardSessionID             = nodeDictionary["kKeybMsgKey_SessionID"];
                        _appleTVKeyboardSecureText            = (nodeDictionary["kKeybMsgKey_SecureText"] == "1");
                        _appleTVKeyboardSecureTextCertificate = nodeDictionary.GetValueOrDefault("certificate");
                        _appleTVKeyboardSecureTextChallenge   = nodeDictionary.GetValueOrDefault("challenge");

                        if (_appleTVKeyboardSecureText)
                        {
                            CurrentAppleTVKeyboardType = AppleTVKeyboardType.Password;
                        }
                        else
                        {
                            CurrentAppleTVKeyboardType = (AppleTVKeyboardType)int.Parse(nodeDictionary["kKeybMsgKey_KeyboardType"]);
                        }
                        IsAppleTVKeyboardVisible = true;
                        break;

                    case "2":     // Hide keyboard
                        IsAppleTVKeyboardVisible = false;
                        break;

                    case "5":     // Trackpad interface update
                        _appleTVTrackpadPort = int.Parse(nodeDictionary["kKeybMsgKey_String"]) ^ AppleTVEncryptionKey;
                        _appleTVTrackpadKey  = BitUtility.NetworkToHostOrder(int.Parse(nodeDictionary["kKeybMsgKey_SubText"]) ^ AppleTVEncryptionKey);
                        _log.Info("Apple TV virtual trackpad parameters updated: Encryption key: {0:X8} Port: {1}", _appleTVTrackpadKey, _appleTVTrackpadPort);
                        break;
                    }
                }
            }
            catch
            {
                return(false);
            }
            return(true);
        }
예제 #21
0
        public async Task <bool> SendAppleTVKeyboardSecureTextAsync(string value)
        {
            if (!_appleTVKeyboardSecureText || string.IsNullOrEmpty(_appleTVKeyboardSecureTextCertificate) || string.IsNullOrEmpty(_appleTVKeyboardSecureTextChallenge))
            {
                return(false);
            }

            byte[] encryptedBytes;

            try
            {
                // Read the certificate
                byte[] certificateBytes = BitUtility.FromHexString(_appleTVKeyboardSecureTextCertificate);
                var    certificate      = new X509Certificate(certificateBytes);

                // X509Certificate gives us the ASN.1 DER-encoded RSA public key
                var publicKeyEncoded = certificate.GetPublicKey();
                if (publicKeyEncoded[0] != 0x30 || publicKeyEncoded[1] != 0x81)
                {
                    throw new Exception();
                }
                // Byte at index 2 is the length of the entire sequence, we can ignore this
                if (publicKeyEncoded[3] != 0x02 || publicKeyEncoded[4] != 0x81)
                {
                    throw new Exception();
                }

                // Length of the public key
                int length   = publicKeyEncoded[5];
                int position = 6;

                // Skip any padding at the beginning
                while (publicKeyEncoded[position] == 0x00)
                {
                    length--;
                    position++;
                }

                // Get the public key
                byte[] publicKey = new byte[length];
                System.Buffer.BlockCopy(publicKeyEncoded, position, publicKey, 0, length);

                // Length/position of exponent
                position += length;
                if (publicKeyEncoded[position] != 0x02)
                {
                    throw new Exception();
                }
                position++;
                length = publicKeyEncoded[position];
                position++;

                // Get the exponent
                byte[] exponent = new byte[length];
                System.Buffer.BlockCopy(publicKeyEncoded, position, exponent, 0, length);

                // Get bytes to encrypt
                var bytesToEncode = new List <byte>();
                bytesToEncode.AddRange(BitUtility.FromHexString(_appleTVKeyboardSecureTextChallenge));
                bytesToEncode.AddRange(Encoding.UTF8.GetBytes(value));

                // Set up RSA parameters
                var rsaParameters = new RSAParameters();
                rsaParameters.Modulus  = publicKey;
                rsaParameters.Exponent = exponent;

                // Create RSA provider
                using (var rsa = new RSACryptoServiceProvider())
                {
                    rsa.ImportParameters(rsaParameters);

                    // Encrypt the source data
                    encryptedBytes = rsa.Encrypt(bytesToEncode.ToArray(), false);
                }
            }
            catch { return(false); }

            // Create DACP request
            List <byte> contentBytes = new List <byte>();

            contentBytes.AddRange(DACPUtility.GetDACPFormattedBytes("cmcc", _appleTVKeyboardSessionID));
            contentBytes.AddRange(DACPUtility.GetDACPFormattedBytes("cmbe", "PromptDone"));
            contentBytes.AddRange(DACPUtility.GetDACPFormattedBytes("cmae", BitUtility.ToHexString(encryptedBytes)));

            ByteArrayContent content = new ByteArrayContent(contentBytes.ToArray());

            DACPRequest request = new DACPRequest("/ctrl-int/1/controlpromptentry");

            request.HttpContent = content;

            try
            {
                await SubmitRequestAsync(request).ConfigureAwait(false);
            }
            catch { return(false); }
            return(true);
        }
예제 #22
0
 internal Task <IDACPList> GetAlphaGroupedListAsync <T>(DACPRequest request, Func <DACPNodeDictionary, T> itemGenerator, string listKey = DACPUtility.DefaultListKey)
 {
     return(GetAlphaGroupedListAsync(request, b => itemGenerator(DACPNodeDictionary.Parse(b)), listKey));
 }
예제 #23
0
        protected 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);
                }
            }
            catch (Exception e)
            {
                HandleHTTPException(request, e);
                return(false);
            }

            return(true);
        }
예제 #24
0
 internal void HandleHTTPException(DACPRequest request, Exception e)
 {
     HandleHTTPException(request.GetURI(), e);
 }
예제 #25
0
        protected async Task <bool> GetPlayStatusUpdateAsync(CancellationToken cancellationToken)
        {
            // Do not pass the cancellation token to the HTTP request since cancelling 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 SubmitRequestAsync(request).ConfigureAwait(false);

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

                // Process response
                ThreadUtility.RunOnUIThread(() =>
                {
                    timerTrackTimeUpdate.Stop();
                });

                var nodes = DACPNodeDictionary.Parse(response.Nodes);
                _playStatusRevisionNumber = nodes.GetInt("cmsr");

                int oldSongID = CurrentItemID;

                if (nodes.ContainsKey("canp"))
                {
                    // Current song and container IDs
                    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;
                }
                CurrentSongName          = nodes.GetString("cann");
                CurrentArtist            = nodes.GetString("cana");
                CurrentAlbum             = nodes.GetString("canl");
                CurrentAlbumPersistentID = (UInt64)nodes.GetLong("asai");
                PlayState = (PlayStates)nodes.GetByte("caps");

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

                // Repeat
                int caar = nodes.GetInt("caar");
                IsRepeatOneAvailable = (caar & (1 << 1)) != 0;
                IsRepeatAllAvailable = (caar & (1 << 2)) != 0;
                RepeatState          = (RepeatStates)nodes.GetByte("carp");

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

                // Track length (ms)
                TrackTimeTotal = nodes.GetInt("cast");
                // Remaining track length (ms)
                TrackTimeRemaining = nodes.GetNullableInt("cant") ?? TrackTimeTotal;

                // dacp.visualizer
                VisualizerActive = nodes.GetBool("cavs");
                // dacp.visualizerenabled
                VisualizerAvailable = nodes.GetBool("cave");
                // dacp.fullscreen
                FullScreenModeActive = nodes.GetBool("cafs");
                // dacp.fullscreenenabled
                FullScreenModeAvailable = nodes.GetBool("cafe");

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

                    // caks = 1 when the next button is disabled, and 2 when it's enabled
                    IsiTunesRadioNextButtonEnabled = (nodes.GetByte("caks") == 2);

                    // "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.
                    IsiTunesRadioMenuEnabled = nodes.ContainsKey("aelb");

                    IsiTunesRadioSongFavorited = (nodes.GetByte("aels") == 2);
                }
                else
                {
                    IsCurrentlyPlayingiTunesRadio = false;
                }


                if (IsCurrentlyPlayingiTunesRadio)
                {
                    var caks = nodes.GetByte("caks");
                    IsiTunesRadioNextButtonEnabled = !(caks == 1);
                }

                if (!nodes.ContainsKey("casc") || nodes.GetBool("casc") == true)
                {
                    IsPlayPositionBarEnabled = true;
                }
                else
                {
                    IsPlayPositionBarEnabled = false;
                }

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

                // If the song ID changed, refresh the album art
                if (oldSongID != CurrentItemID)
                {
                    PropertyChanged.RaiseOnUIThread(this, "CurrentAlbumArtURL");
                }

                ThreadUtility.RunOnUIThread(() =>
                {
                    if (PlayState == PlayStates.Playing)
                    {
                        timerTrackTimeUpdate.Start();
                    }
                    else if (PlayState == PlayStates.FastForward || PlayState == PlayStates.Rewind)
                    {
                        BeginRepeatedTrackTimeRequest();
                    }
                });

                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();
            }
            catch { return(false); }
            return(true);
        }