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); }
public static IDacpList GetAlphaGroupedDacpList <T>(List <T> items, IEnumerable <DacpNode> headers, bool useGroupMinimums = true) { // Determine whether we need to process the headers bool processHeaders = false; // Make sure headers were returned if (headers != null) { if (useGroupMinimums) { // Only show headers if we have at least 10 items if (items.Count >= 10) { // Also make sure we have more than 1 group headers = headers.ToList(); if (((IList)headers).Count >= 2) { processHeaders = true; } } } else { processHeaders = true; } } // If we're not processing headers, just return the items in a list if (!processHeaders) { return(items.ToDacpList()); } var result = new DacpList <ItemGroup <T> >(true); // Create groups for each letter var groupsByChar = new Dictionary <char, ItemGroup <T> >(AlphaGroupChars.Length); foreach (char c in AlphaGroupChars) { var group = new ItemGroup <T>(c.ToString()); result.Add(group); groupsByChar[c] = group; } // Add the items to their groups foreach (var header in headers.Select(n => DacpNodeDictionary.Parse(n.Value))) { char headerChar = GetKeyChar(header.GetString("mshc")); int skip = header.GetInt("mshi"); int take = header.GetInt("mshn"); groupsByChar[headerChar].AddRange(items.Skip(skip).Take(take)); } return(result); }
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); }
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> 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> 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 static IEnumerable <T> GetItemsFromNodes <T>(IEnumerable <DacpNode> nodes, Func <DacpNodeDictionary, T> itemGenerator, string listKey = DefaultListKey) { return(GetItemsFromNodes(nodes, d => itemGenerator(DacpNodeDictionary.Parse(d)), listKey)); }
public static IEnumerable <T> GetItemsFromNodes <T>(byte[] data, Func <DacpNodeDictionary, T> itemGenerator, string listKey = DefaultListKey) { return(GetItemsFromNodes(GetResponseNodes(data), d => itemGenerator(DacpNodeDictionary.Parse(d)), listKey)); }
public static IDacpList GetAlphaGroupedDacpList <T>(IEnumerable <DacpNode> nodes, Func <DacpNodeDictionary, T> itemGenerator, out List <T> items, string listKey = DefaultListKey, bool useGroupMinimums = true) { return(GetAlphaGroupedDacpList(nodes, d => itemGenerator(DacpNodeDictionary.Parse(d)), out items, listKey, useGroupMinimums)); }