using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Http; using System.Threading.Tasks; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.DTO; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Weather; namespace MediaBrowser.ApiInteraction { public class ApiClient : IDisposable { public ApiClient(HttpClientHandler handler) { handler.AutomaticDecompression = DecompressionMethods.Deflate; HttpClient = new HttpClient(handler); } /// /// Gets or sets the server host name (myserver or 192.168.x.x) /// public string ServerHostName { get; set; } /// /// Gets or sets the port number used by the API /// public int ServerApiPort { get; set; } /// /// Gets the current api url based on hostname and port. /// protected string ApiUrl { get { return string.Format("http://{0}:{1}/mediabrowser/api", ServerHostName, ServerApiPort); } } /// /// Gets the data format to request from the server /// private SerializationFormat SerializationFormat { get { // First try Protobuf since it has the best performance if (DataSerializer.CanDeserializeProtobuf) { return ApiInteraction.SerializationFormat.Protobuf; } // Next best is jsv if (DataSerializer.CanDeserializeJsv) { return ApiInteraction.SerializationFormat.Jsv; } return ApiInteraction.SerializationFormat.Json; } } public HttpClient HttpClient { get; private set; } public IDataSerializer DataSerializer { get; set; } /// /// Gets an image url that can be used to download an image from the api /// /// The Id of the item /// The type of image requested /// The image index, if there are multiple. Currently only applies to backdrops. Supply null or 0 for first backdrop. /// Use if a fixed width is required. Aspect ratio will be preserved. /// Use if a fixed height is required. Aspect ratio will be preserved. /// Use if a max width is required. Aspect ratio will be preserved. /// Use if a max height is required. Aspect ratio will be preserved. /// Quality level, from 0-100. Currently only applies to JPG. The default value should suffice. public string GetImageUrl(Guid itemId, ImageType imageType, int? imageIndex = null, int? width = null, int? height = null, int? maxWidth = null, int? maxHeight = null, int? quality = null) { string url = ApiUrl + "/image"; url += "?id=" + itemId.ToString(); url += "&type=" + imageType.ToString(); if (imageIndex.HasValue) { url += "&index=" + imageIndex; } if (width.HasValue) { url += "&width=" + width; } if (height.HasValue) { url += "&height=" + height; } if (maxWidth.HasValue) { url += "&maxWidth=" + maxWidth; } if (maxHeight.HasValue) { url += "&maxHeight=" + maxHeight; } if (quality.HasValue) { url += "&quality=" + quality; } return url; } /// /// Gets an image url that can be used to download an image from the api /// /// The Id of the user /// Use if a fixed width is required. Aspect ratio will be preserved. /// Use if a fixed height is required. Aspect ratio will be preserved. /// Use if a max width is required. Aspect ratio will be preserved. /// Use if a max height is required. Aspect ratio will be preserved. /// Quality level, from 0-100. Currently only applies to JPG. The default value should suffice. public string GetUserImageUrl(Guid userId, int? width = null, int? height = null, int? maxWidth = null, int? maxHeight = null, int? quality = null) { string url = ApiUrl + "/image"; url += "?userId=" + userId.ToString(); if (width.HasValue) { url += "&width=" + width; } if (height.HasValue) { url += "&height=" + height; } if (maxWidth.HasValue) { url += "&maxWidth=" + maxWidth; } if (maxHeight.HasValue) { url += "&maxHeight=" + maxHeight; } if (quality.HasValue) { url += "&quality=" + quality; } return url; } /// /// Gets an image url that can be used to download an image from the api /// /// The name of the person /// Use if a fixed width is required. Aspect ratio will be preserved. /// Use if a fixed height is required. Aspect ratio will be preserved. /// Use if a max width is required. Aspect ratio will be preserved. /// Use if a max height is required. Aspect ratio will be preserved. /// Quality level, from 0-100. Currently only applies to JPG. The default value should suffice. public string GetPersonImageUrl(string name, int? width = null, int? height = null, int? maxWidth = null, int? maxHeight = null, int? quality = null) { string url = ApiUrl + "/image"; url += "?personname=" + name; if (width.HasValue) { url += "&width=" + width; } if (height.HasValue) { url += "&height=" + height; } if (maxWidth.HasValue) { url += "&maxWidth=" + maxWidth; } if (maxHeight.HasValue) { url += "&maxHeight=" + maxHeight; } if (quality.HasValue) { url += "&quality=" + quality; } return url; } /// /// Gets an image url that can be used to download an image from the api /// /// The year /// Use if a fixed width is required. Aspect ratio will be preserved. /// Use if a fixed height is required. Aspect ratio will be preserved. /// Use if a max width is required. Aspect ratio will be preserved. /// Use if a max height is required. Aspect ratio will be preserved. /// Quality level, from 0-100. Currently only applies to JPG. The default value should suffice. public string GetYearImageUrl(int year, int? width = null, int? height = null, int? maxWidth = null, int? maxHeight = null, int? quality = null) { string url = ApiUrl + "/image"; url += "?year=" + year; if (width.HasValue) { url += "&width=" + width; } if (height.HasValue) { url += "&height=" + height; } if (maxWidth.HasValue) { url += "&maxWidth=" + maxWidth; } if (maxHeight.HasValue) { url += "&maxHeight=" + maxHeight; } if (quality.HasValue) { url += "&quality=" + quality; } return url; } /// /// Gets an image url that can be used to download an image from the api /// /// The name of the genre /// Use if a fixed width is required. Aspect ratio will be preserved. /// Use if a fixed height is required. Aspect ratio will be preserved. /// Use if a max width is required. Aspect ratio will be preserved. /// Use if a max height is required. Aspect ratio will be preserved. /// Quality level, from 0-100. Currently only applies to JPG. The default value should suffice. public string GetGenreImageUrl(string name, int? width = null, int? height = null, int? maxWidth = null, int? maxHeight = null, int? quality = null) { string url = ApiUrl + "/image"; url += "?genre=" + name; if (width.HasValue) { url += "&width=" + width; } if (height.HasValue) { url += "&height=" + height; } if (maxWidth.HasValue) { url += "&maxWidth=" + maxWidth; } if (maxHeight.HasValue) { url += "&maxHeight=" + maxHeight; } if (quality.HasValue) { url += "&quality=" + quality; } return url; } /// /// Gets an image url that can be used to download an image from the api /// /// The name of the studio /// Use if a fixed width is required. Aspect ratio will be preserved. /// Use if a fixed height is required. Aspect ratio will be preserved. /// Use if a max width is required. Aspect ratio will be preserved. /// Use if a max height is required. Aspect ratio will be preserved. /// Quality level, from 0-100. Currently only applies to JPG. The default value should suffice. public string GetStudioImageUrl(string name, int? width = null, int? height = null, int? maxWidth = null, int? maxHeight = null, int? quality = null) { string url = ApiUrl + "/image"; url += "?studio=" + name; if (width.HasValue) { url += "&width=" + width; } if (height.HasValue) { url += "&height=" + height; } if (maxWidth.HasValue) { url += "&maxWidth=" + maxWidth; } if (maxHeight.HasValue) { url += "&maxHeight=" + maxHeight; } if (quality.HasValue) { url += "&quality=" + quality; } return url; } /// /// This is a helper to get a list of backdrop url's from a given ApiBaseItemWrapper. If the actual item does not have any backdrops it will return backdrops from the first parent that does. /// /// A given item. /// Use if a fixed width is required. Aspect ratio will be preserved. /// Use if a fixed height is required. Aspect ratio will be preserved. /// Use if a max width is required. Aspect ratio will be preserved. /// Use if a max height is required. Aspect ratio will be preserved. /// Quality level, from 0-100. Currently only applies to JPG. The default value should suffice. public IEnumerable GetBackdropImageUrls(DTOBaseItem item, int? width = null, int? height = null, int? maxWidth = null, int? maxHeight = null, int? quality = null) { Guid? backdropItemId = null; int backdropCount = 0; if (item.BackdropCount == 0) { backdropItemId = item.ParentBackdropItemId; backdropCount = item.ParentBackdropCount ?? 0; } else { backdropItemId = item.Id; backdropCount = item.BackdropCount; } if (backdropItemId == null) { return new string[] { }; } List files = new List(); for (int i = 0; i < backdropCount; i++) { files.Add(GetImageUrl(backdropItemId.Value, ImageType.Backdrop, i, width, height, maxWidth, maxHeight, quality)); } return files; } /// /// This is a helper to get the logo image url from a given ApiBaseItemWrapper. If the actual item does not have a logo, it will return the logo from the first parent that does, or null. /// /// A given item. /// Use if a fixed width is required. Aspect ratio will be preserved. /// Use if a fixed height is required. Aspect ratio will be preserved. /// Use if a max width is required. Aspect ratio will be preserved. /// Use if a max height is required. Aspect ratio will be preserved. /// Quality level, from 0-100. Currently only applies to JPG. The default value should suffice. public string GetLogoImageUrl(DTOBaseItem item, int? width = null, int? height = null, int? maxWidth = null, int? maxHeight = null, int? quality = null) { Guid? logoItemId = item.HasLogo ? item.Id : item.ParentLogoItemId; if (logoItemId.HasValue) { return GetImageUrl(logoItemId.Value, ImageType.Logo, null, width, height, maxWidth, maxHeight, quality); } return null; } /// /// Gets an image stream based on a url /// public Task GetImageStreamAsync(string url) { return GetStreamAsync(url); } /// /// Gets a BaseItem /// public async Task GetItemAsync(Guid id, Guid userId) { string url = ApiUrl + "/item?userId=" + userId.ToString(); if (id != Guid.Empty) { url += "&id=" + id.ToString(); } using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets all Users /// public async Task> GetAllUsersAsync() { string url = ApiUrl + "/users"; using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets all Genres /// public async Task> GetAllGenresAsync(Guid userId) { string url = ApiUrl + "/genres?userId=" + userId.ToString(); using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets all Years /// public async Task> GetAllYearsAsync(Guid userId) { string url = ApiUrl + "/years?userId=" + userId.ToString(); using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets all items that contain a given Year /// public async Task> GetItemsWithYearAsync(string name, Guid userId) { string url = ApiUrl + "/itemlist?listtype=itemswithyear&userId=" + userId.ToString() + "&name=" + name; using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets all items that contain a given Genre /// public async Task> GetItemsWithGenreAsync(string name, Guid userId) { string url = ApiUrl + "/itemlist?listtype=itemswithgenre&userId=" + userId.ToString() + "&name=" + name; using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets all items that contain a given Person /// public async Task> GetItemsWithPersonAsync(string name, Guid userId) { string url = ApiUrl + "/itemlist?listtype=itemswithperson&userId=" + userId.ToString() + "&name=" + name; using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets all items that contain a given Person /// public async Task> GetItemsWithPersonAsync(string name, string personType, Guid userId) { string url = ApiUrl + "/itemlist?listtype=itemswithperson&userId=" + userId.ToString() + "&name=" + name; url += "&persontype=" + personType; using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets all studious /// public async Task> GetAllStudiosAsync(Guid userId) { string url = ApiUrl + "/studios?userId=" + userId.ToString(); using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets all items that contain a given Studio /// public async Task> GetItemsWithStudioAsync(string name, Guid userId) { string url = ApiUrl + "/itemlist?listtype=itemswithstudio&userId=" + userId.ToString() + "&name=" + name; using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets a studio /// public async Task GetStudioAsync(Guid userId, string name) { string url = ApiUrl + "/studio?userId=" + userId.ToString() + "&name=" + name; using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets a genre /// public async Task GetGenreAsync(Guid userId, string name) { string url = ApiUrl + "/genre?userId=" + userId.ToString() + "&name=" + name; using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets a person /// public async Task GetPersonAsync(Guid userId, string name) { string url = ApiUrl + "/person?userId=" + userId.ToString() + "&name=" + name; using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets a year /// public async Task GetYearAsync(Guid userId, int year) { string url = ApiUrl + "/year?userId=" + userId.ToString() + "&year=" + year; using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets a list of plugins installed on the server /// public async Task GetInstalledPluginsAsync() { string url = ApiUrl + "/plugins"; using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets a list of plugins installed on the server /// public Task GetPluginAssemblyAsync(PluginInfo plugin) { string url = ApiUrl + "/pluginassembly?assemblyfilename=" + plugin.AssemblyFileName; return GetStreamAsync(url); } /// /// Gets the current server configuration /// public async Task GetServerConfigurationAsync() { string url = ApiUrl + "/ServerConfiguration"; // At the moment this can't be retrieved in protobuf format SerializationFormat format = DataSerializer.CanDeserializeJsv ? SerializationFormat.Jsv : ApiInteraction.SerializationFormat.Json; using (Stream stream = await GetSerializedStreamAsync(url, format).ConfigureAwait(false)) { return DeserializeFromStream(stream, format); } } /// /// Gets weather information for the default location as set in configuration /// public async Task GetPluginConfigurationAsync(PluginInfo plugin, Type configurationType) { string url = ApiUrl + "/PluginConfiguration?assemblyfilename=" + plugin.AssemblyFileName; // At the moment this can't be retrieved in protobuf format SerializationFormat format = DataSerializer.CanDeserializeJsv ? SerializationFormat.Jsv : ApiInteraction.SerializationFormat.Json; using (Stream stream = await GetSerializedStreamAsync(url, format).ConfigureAwait(false)) { return DeserializeFromStream(stream, format, configurationType); } } /// /// Gets weather information for the default location as set in configuration /// public async Task GetDefaultUserAsync() { string url = ApiUrl + "/defaultuser"; using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets weather information for the default location as set in configuration /// public async Task GetWeatherInfoAsync() { string url = ApiUrl + "/weather"; using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// Gets weather information for a specific zip code /// public async Task GetWeatherInfoAsync(string zipCode) { string url = ApiUrl + "/weather?zipcode=" + zipCode; using (Stream stream = await GetSerializedStreamAsync(url).ConfigureAwait(false)) { return DeserializeFromStream(stream); } } /// /// This is a helper around getting a stream from the server that contains serialized data /// private Task GetSerializedStreamAsync(string url) { return GetSerializedStreamAsync(url, SerializationFormat); } /// /// This is a helper around getting a stream from the server that contains serialized data /// private Task GetSerializedStreamAsync(string url, SerializationFormat serializationFormat) { if (url.IndexOf('?') == -1) { url += "?dataformat=" + serializationFormat.ToString().ToLower(); } else { url += "&dataformat=" + serializationFormat.ToString().ToLower(); } return GetStreamAsync(url); } private T DeserializeFromStream(Stream stream) { return DeserializeFromStream(stream, SerializationFormat); } private T DeserializeFromStream(Stream stream, SerializationFormat format) { if (format == ApiInteraction.SerializationFormat.Protobuf) { return DataSerializer.DeserializeProtobufFromStream(stream); } if (format == ApiInteraction.SerializationFormat.Jsv) { return DataSerializer.DeserializeJsvFromStream(stream); } return DataSerializer.DeserializeJsonFromStream(stream); } private object DeserializeFromStream(Stream stream, SerializationFormat format, Type type) { if (format == ApiInteraction.SerializationFormat.Protobuf) { return DataSerializer.DeserializeProtobufFromStream(stream, type); } if (format == ApiInteraction.SerializationFormat.Jsv) { return DataSerializer.DeserializeJsvFromStream(stream, type); } return DataSerializer.DeserializeJsonFromStream(stream, type); } /// /// This is just a helper around HttpClient /// private Task GetStreamAsync(string url) { return HttpClient.GetStreamAsync(url); } public void Dispose() { HttpClient.Dispose(); } } public enum SerializationFormat { Json, Jsv, Protobuf } }