using MediaBrowser.Model.Connectivity; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Web; using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace MediaBrowser.ApiInteraction { /// /// Provides api methods that are usable on all platforms /// public abstract class BaseApiClient : IDisposable { /// /// Gets the logger. /// /// The logger. protected ILogger Logger { get; private set; } /// /// Initializes a new instance of the class. /// /// The logger. /// logger protected BaseApiClient(ILogger logger) { if (logger == null) { throw new ArgumentNullException("logger"); } Logger = logger; DataSerializer.Configure(); } /// /// Gets or sets the server host name (myserver or 192.168.x.x) /// /// The name of the server host. public string ServerHostName { get; set; } /// /// Gets or sets the port number used by the API /// /// The server API port. public int ServerApiPort { get; set; } /// /// Gets or sets the type of the client. /// /// The type of the client. public ClientType ClientType { get; set; } /// /// Gets or sets the name of the device. /// /// The name of the device. public string DeviceName { get; set; } private Guid? _currentUserId; /// /// Gets or sets the current user id. /// /// The current user id. public virtual Guid? CurrentUserId { get { return _currentUserId; } set { _currentUserId = value; ResetAuthorizationHeader(); } } /// /// Gets the current api url based on hostname and port. /// /// The API URL. protected string ApiUrl { get { return string.Format("http://{0}:{1}/mediabrowser", ServerHostName, ServerApiPort); } } private SerializationFormats _serializationFormat = SerializationFormats.Protobuf; /// /// Gets the default data format to request from the server /// /// The serialization format. public SerializationFormats SerializationFormat { get { return _serializationFormat; } set { _serializationFormat = value; } } /// /// Resets the authorization header. /// private void ResetAuthorizationHeader() { if (!CurrentUserId.HasValue) { SetAuthorizationHeader(null); return; } var header = string.Format("UserId=\"{0}\", Client=\"{1}\"", CurrentUserId.Value, ClientType); if (!string.IsNullOrEmpty(DeviceName)) { header += string.Format(", Device=\"{0}\"", DeviceName); } SetAuthorizationHeader(header); } /// /// Sets the authorization header. /// /// The header. protected abstract void SetAuthorizationHeader(string header); /// /// Gets the API URL. /// /// The handler. /// System.String. /// handler protected string GetApiUrl(string handler) { return GetApiUrl(handler, new QueryStringDictionary()); } /// /// Gets the API URL. /// /// The handler. /// The query string. /// System.String. /// handler protected string GetApiUrl(string handler, QueryStringDictionary queryString) { if (string.IsNullOrEmpty(handler)) { throw new ArgumentNullException("handler"); } if (queryString == null) { throw new ArgumentNullException("queryString"); } return queryString.GetUrl(ApiUrl + "/" + handler); } /// /// Creates a url to return a list of items /// /// The query. /// The type of list to retrieve. /// System.String. /// query protected string GetItemListUrl(ItemQuery query, string listType = null) { if (query == null) { throw new ArgumentNullException("query"); } var dict = new QueryStringDictionary { }; dict.AddIfNotNullOrEmpty("listtype", listType); dict.AddIfNotNullOrEmpty("ParentId", query.ParentId); dict.AddIfNotNull("startindex", query.StartIndex); dict.AddIfNotNull("limit", query.Limit); if (query.SortBy != null) { dict["sortBy"] = string.Join(",", query.SortBy.Select(s => s.ToString())); } if (query.SortOrder.HasValue) { dict["sortOrder"] = query.SortOrder.ToString(); } if (query.Fields != null) { dict.Add("fields", query.Fields.Select(f => f.ToString())); } if (query.Filters != null) { dict.Add("Filters", query.Filters.Select(f => f.ToString())); } if (query.ImageTypes != null) { dict.Add("ImageTypes", query.ImageTypes.Select(f => f.ToString())); } dict.Add("recursive", query.Recursive); dict.AddIfNotNull("genres", query.Genres); dict.AddIfNotNull("studios", query.Studios); dict.AddIfNotNull("ExcludeItemTypes", query.ExcludeItemTypes); dict.AddIfNotNull("IncludeItemTypes", query.IncludeItemTypes); dict.AddIfNotNullOrEmpty("person", query.Person); dict.AddIfNotNullOrEmpty("personType", query.PersonType); dict.AddIfNotNull("years", query.Years); dict.AddIfNotNullOrEmpty("indexBy", query.IndexBy); dict.AddIfNotNullOrEmpty("dynamicSortBy", query.DynamicSortBy); dict.AddIfNotNullOrEmpty("SearchTerm", query.SearchTerm); return GetApiUrl("Users/" + query.UserId + "/Items", dict); } /// /// Gets the image URL. /// /// The base URL. /// The options. /// The query params. /// System.String. /// options private string GetImageUrl(string baseUrl, ImageOptions options, QueryStringDictionary queryParams) { if (options == null) { throw new ArgumentNullException("options"); } if (queryParams == null) { throw new ArgumentNullException("queryParams"); } if (options.ImageIndex.HasValue) { baseUrl += "/" + options.ImageIndex.Value; } queryParams.AddIfNotNull("width", options.Width); queryParams.AddIfNotNull("height", options.Height); queryParams.AddIfNotNull("maxWidth", options.MaxWidth); queryParams.AddIfNotNull("maxHeight", options.MaxHeight); queryParams.AddIfNotNull("Quality", options.Quality); queryParams.AddIfNotNull("tag", options.Tag); return GetApiUrl(baseUrl, queryParams); } /// /// Gets the image URL. /// /// The item. /// The options. /// System.String. /// item public string GetImageUrl(BaseItemDto item, ImageOptions options) { if (item == null) { throw new ArgumentNullException("item"); } if (options == null) { throw new ArgumentNullException("options"); } var index = options.ImageIndex ?? 0; if (options.ImageType == ImageType.Backdrop) { options.Tag = item.BackdropImageTags[index]; } else if (options.ImageType == ImageType.ChapterImage) { options.Tag = item.Chapters[index].ImageTag; } else { options.Tag = item.ImageTags[options.ImageType]; } return GetImageUrl(item.Id, options); } /// /// Gets an image url that can be used to download an image from the api /// /// The Id of the item /// The options. /// System.String. /// itemId public string GetImageUrl(string itemId, ImageOptions options) { if (string.IsNullOrEmpty(itemId)) { throw new ArgumentNullException("itemId"); } var url = "Items/" + itemId + "/Images/" + options.ImageType; return GetImageUrl(url, options, new QueryStringDictionary()); } /// /// Gets the user image URL. /// /// The user. /// The options. /// System.String. /// user public string GetUserImageUrl(UserDto user, ImageOptions options) { if (user == null) { throw new ArgumentNullException("user"); } if (options == null) { throw new ArgumentNullException("options"); } options.Tag = user.PrimaryImageTag; return GetUserImageUrl(user.Id, options); } /// /// Gets an image url that can be used to download an image from the api /// /// The Id of the user /// The options. /// System.String. /// userId public string GetUserImageUrl(Guid userId, ImageOptions options) { if (userId == Guid.Empty) { throw new ArgumentNullException("userId"); } var url = "Users/" + userId + "/Images/" + options.ImageType; return GetImageUrl(url, options, new QueryStringDictionary()); } /// /// Gets the person image URL. /// /// The item. /// The options. /// System.String. /// item public string GetPersonImageUrl(BaseItemPerson item, ImageOptions options) { if (item == null) { throw new ArgumentNullException("item"); } if (options == null) { throw new ArgumentNullException("options"); } options.Tag = item.PrimaryImageTag; return GetPersonImageUrl(item.Name, options); } /// /// Gets the person image URL. /// /// The item. /// The options. /// System.String. /// item public string GetPersonImageUrl(BaseItemDto item, ImageOptions options) { if (item == null) { throw new ArgumentNullException("item"); } if (options == null) { throw new ArgumentNullException("options"); } options.Tag = item.ImageTags[ImageType.Primary]; return GetPersonImageUrl(item.Name, options); } /// /// Gets an image url that can be used to download an image from the api /// /// The name of the person /// The options. /// System.String. /// name public string GetPersonImageUrl(string name, ImageOptions options) { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException("name"); } var url = "Persons/" + name + "/Images/" + options.ImageType; return GetImageUrl(url, options, new QueryStringDictionary()); } /// /// Gets the year image URL. /// /// The item. /// The options. /// System.String. /// item public string GetYearImageUrl(BaseItemDto item, ImageOptions options) { if (item == null) { throw new ArgumentNullException("item"); } if (options == null) { throw new ArgumentNullException("options"); } options.Tag = item.ImageTags[ImageType.Primary]; return GetYearImageUrl(int.Parse(item.Name), options); } /// /// Gets an image url that can be used to download an image from the api /// /// The year. /// The options. /// System.String. public string GetYearImageUrl(int year, ImageOptions options) { var url = "Years/" + year + "/Images/" + options.ImageType; return GetImageUrl(url, options, new QueryStringDictionary()); } /// /// Gets the genre image URL. /// /// The item. /// The options. /// System.String. /// item public string GetGenreImageUrl(BaseItemDto item, ImageOptions options) { if (item == null) { throw new ArgumentNullException("item"); } if (options == null) { throw new ArgumentNullException("options"); } options.Tag = item.ImageTags[ImageType.Primary]; return GetGenreImageUrl(item.Name, options); } /// /// Gets an image url that can be used to download an image from the api /// /// The name. /// The options. /// System.String. /// name public string GetGenreImageUrl(string name, ImageOptions options) { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException("name"); } var url = "Genres/" + name + "/Images/" + options.ImageType; return GetImageUrl(url, options, new QueryStringDictionary()); } /// /// Gets the studio image URL. /// /// The item. /// The options. /// System.String. /// item public string GetStudioImageUrl(BaseItemDto item, ImageOptions options) { if (item == null) { throw new ArgumentNullException("item"); } if (options == null) { throw new ArgumentNullException("options"); } options.Tag = item.ImageTags[ImageType.Primary]; return GetStudioImageUrl(item.Name, options); } /// /// Gets an image url that can be used to download an image from the api /// /// The name. /// The options. /// System.String. /// name public string GetStudioImageUrl(string name, ImageOptions options) { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException("name"); } var url = "Studios/" + name + "/Images/" + options.ImageType; return GetImageUrl(url, options, new QueryStringDictionary()); } /// /// 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. /// The options. /// System.String[][]. /// item public string[] GetBackdropImageUrls(BaseItemDto item, ImageOptions options) { if (item == null) { throw new ArgumentNullException("item"); } if (options == null) { throw new ArgumentNullException("options"); } options.ImageType = ImageType.Backdrop; string backdropItemId; List backdropImageTags; if (item.BackdropCount == 0) { backdropItemId = item.ParentBackdropItemId; backdropImageTags = item.ParentBackdropImageTags; } else { backdropItemId = item.Id; backdropImageTags = item.BackdropImageTags; } if (string.IsNullOrEmpty(backdropItemId)) { return new string[] { }; } var files = new string[backdropImageTags.Count]; for (var i = 0; i < backdropImageTags.Count; i++) { options.ImageIndex = i; options.Tag = backdropImageTags[i]; files[i] = GetImageUrl(backdropItemId, options); } 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. /// The options. /// System.String. /// item public string GetLogoImageUrl(BaseItemDto item, ImageOptions options) { if (item == null) { throw new ArgumentNullException("item"); } if (options == null) { throw new ArgumentNullException("options"); } options.ImageType = ImageType.Logo; var logoItemId = item.HasLogo ? item.Id : item.ParentLogoItemId; var imageTag = item.HasLogo ? item.ImageTags[ImageType.Logo] : item.ParentLogoImageTag; if (!string.IsNullOrEmpty(logoItemId)) { options.Tag = imageTag; return GetImageUrl(logoItemId, options); } return null; } /// /// Gets the url needed to stream an audio file /// /// The options. /// System.String. /// options public string GetAudioStreamUrl(StreamOptions options) { if (options == null) { throw new ArgumentNullException("options"); } var handler = "audio." + options.OutputFileExtension.TrimStart('.'); return GetMediaStreamUrl(handler, options, new QueryStringDictionary()); } /// /// Gets the url needed to stream a video file /// /// The options. /// System.String. /// options public string GetVideoStreamUrl(VideoStreamOptions options) { if (options == null) { throw new ArgumentNullException("options"); } var handler = "video." + options.OutputFileExtension.TrimStart('.'); return GetVideoStreamUrl(handler, options); } /// /// Formulates a url for streaming audio using the HLS protocol /// /// The options. /// System.String. /// options public string GetHlsAudioStreamUrl(StreamOptions options) { if (options == null) { throw new ArgumentNullException("options"); } return GetMediaStreamUrl("audio.m3u8", options, new QueryStringDictionary()); } /// /// Formulates a url for streaming video using the HLS protocol /// /// The options. /// System.String. /// options public string GetHlsVideoStreamUrl(VideoStreamOptions options) { if (options == null) { throw new ArgumentNullException("options"); } return GetVideoStreamUrl("video.m3u8", options); } /// /// Gets the video stream URL. /// /// The handler. /// The options. /// System.String. private string GetVideoStreamUrl(string handler, VideoStreamOptions options) { var queryParams = new QueryStringDictionary(); if (options.VideoCodec.HasValue) { queryParams["VideoCodec"] = options.VideoCodec.Value.ToString(); } queryParams.AddIfNotNull("VideoBitRate", options.VideoBitRate); queryParams.AddIfNotNull("Width", options.Width); queryParams.AddIfNotNull("Height", options.Height); queryParams.AddIfNotNull("MaxWidth", options.MaxWidth); queryParams.AddIfNotNull("MaxHeight", options.MaxHeight); queryParams.AddIfNotNull("FrameRate", options.FrameRate); queryParams.AddIfNotNull("AudioStreamIndex", options.AudioStreamIndex); queryParams.AddIfNotNull("VideoStreamIndex", options.VideoStreamIndex); queryParams.AddIfNotNull("SubtitleStreamIndex", options.SubtitleStreamIndex); return GetMediaStreamUrl(handler, options, queryParams); } /// /// Gets the media stream URL. /// /// The handler. /// The options. /// The query params. /// System.String. /// handler private string GetMediaStreamUrl(string handler, StreamOptions options, QueryStringDictionary queryParams) { if (string.IsNullOrEmpty(handler)) { throw new ArgumentNullException("handler"); } if (options == null) { throw new ArgumentNullException("options"); } if (queryParams == null) { throw new ArgumentNullException("queryParams"); } queryParams.Add("id", options.ItemId); if (options.AudioCodec.HasValue) { queryParams["audioCodec"] = options.AudioCodec.Value.ToString(); } queryParams.AddIfNotNull("audiochannels", options.MaxAudioChannels); queryParams.AddIfNotNull("audiosamplerate", options.MaxAudioSampleRate); queryParams.AddIfNotNull("AudioBitRate", options.AudioBitRate); queryParams.AddIfNotNull("StartTimeTicks", options.StartTimeTicks); queryParams.AddIfNotNull("Static", options.Static); return GetApiUrl(handler, queryParams); } /// /// Deserializes from stream. /// /// /// The stream. /// ``0. protected T DeserializeFromStream(Stream stream) where T : class { return (T)DataSerializer.DeserializeFromStream(stream, SerializationFormat, typeof(T)); } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { } } }