From a7b0dd15341083040787d92e06d57e8f4458e1e3 Mon Sep 17 00:00:00 2001 From: LukePulverenti Luke Pulverenti luke pulverenti Date: Sun, 2 Sep 2012 01:30:25 -0400 Subject: [PATCH] Added a weather api --- MediaBrowser.Api/HttpHandlers/GenreHandler.cs | 2 +- .../HttpHandlers/GenresHandler.cs | 2 +- MediaBrowser.Api/HttpHandlers/ItemHandler.cs | 2 +- .../HttpHandlers/ItemListHandler.cs | 2 +- .../HttpHandlers/PersonHandler.cs | 2 +- .../PluginConfigurationHandler.cs | 2 +- .../HttpHandlers/PluginsHandler.cs | 2 +- .../HttpHandlers/StudioHandler.cs | 2 +- .../HttpHandlers/StudiosHandler.cs | 2 +- MediaBrowser.Api/HttpHandlers/UsersHandler.cs | 2 +- .../HttpHandlers/WeatherHandler.cs | 36 ++++ MediaBrowser.Api/HttpHandlers/YearHandler.cs | 2 +- MediaBrowser.Api/HttpHandlers/YearsHandler.cs | 2 +- MediaBrowser.Api/MediaBrowser.Api.csproj | 1 + MediaBrowser.Api/Plugin.cs | 4 + MediaBrowser.ApiInteraction/ApiClient.cs | 27 +++ .../Net/Handlers/BaseSerializationHandler.cs | 2 +- .../Configuration/ServerConfiguration.cs | 1 + MediaBrowser.Controller/Kernel.cs | 4 +- .../MediaBrowser.Controller.csproj | 3 + .../Weather/WeatherClient.cs | 191 ++++++++++++++++++ MediaBrowser.Model/MediaBrowser.Model.csproj | 3 + MediaBrowser.Model/Weather/WeatherForecast.cs | 30 +++ MediaBrowser.Model/Weather/WeatherInfo.cs | 14 ++ MediaBrowser.Model/Weather/WeatherStatus.cs | 38 ++++ 25 files changed, 364 insertions(+), 14 deletions(-) create mode 100644 MediaBrowser.Api/HttpHandlers/WeatherHandler.cs create mode 100644 MediaBrowser.Controller/Weather/WeatherClient.cs create mode 100644 MediaBrowser.Model/Weather/WeatherForecast.cs create mode 100644 MediaBrowser.Model/Weather/WeatherInfo.cs create mode 100644 MediaBrowser.Model/Weather/WeatherStatus.cs diff --git a/MediaBrowser.Api/HttpHandlers/GenreHandler.cs b/MediaBrowser.Api/HttpHandlers/GenreHandler.cs index c2b70e1066..eeadf1e453 100644 --- a/MediaBrowser.Api/HttpHandlers/GenreHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/GenreHandler.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Api.HttpHandlers /// /// Gets a single genre /// - public class GenreHandler : BaseJsonHandler + public class GenreHandler : BaseSerializationHandler { protected override Task GetObjectToSerialize() { diff --git a/MediaBrowser.Api/HttpHandlers/GenresHandler.cs b/MediaBrowser.Api/HttpHandlers/GenresHandler.cs index b5608c0a3d..9be2efa173 100644 --- a/MediaBrowser.Api/HttpHandlers/GenresHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/GenresHandler.cs @@ -9,7 +9,7 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Api.HttpHandlers { - public class GenresHandler : BaseJsonHandler + public class GenresHandler : BaseSerializationHandler { protected override Task GetObjectToSerialize() { diff --git a/MediaBrowser.Api/HttpHandlers/ItemHandler.cs b/MediaBrowser.Api/HttpHandlers/ItemHandler.cs index 4f2a9c68e5..35310f042b 100644 --- a/MediaBrowser.Api/HttpHandlers/ItemHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/ItemHandler.cs @@ -8,7 +8,7 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Api.HttpHandlers { - public class ItemHandler : BaseJsonHandler + public class ItemHandler : BaseSerializationHandler { protected override Task GetObjectToSerialize() { diff --git a/MediaBrowser.Api/HttpHandlers/ItemListHandler.cs b/MediaBrowser.Api/HttpHandlers/ItemListHandler.cs index 9d5e3eb580..09814b191d 100644 --- a/MediaBrowser.Api/HttpHandlers/ItemListHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/ItemListHandler.cs @@ -9,7 +9,7 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Api.HttpHandlers { - public class ItemListHandler : BaseJsonHandler + public class ItemListHandler : BaseSerializationHandler { protected override Task GetObjectToSerialize() { diff --git a/MediaBrowser.Api/HttpHandlers/PersonHandler.cs b/MediaBrowser.Api/HttpHandlers/PersonHandler.cs index 3c34efae50..0d496c2406 100644 --- a/MediaBrowser.Api/HttpHandlers/PersonHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/PersonHandler.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Api.HttpHandlers /// /// Gets a single Person /// - public class PersonHandler : BaseJsonHandler + public class PersonHandler : BaseSerializationHandler { protected override Task GetObjectToSerialize() { diff --git a/MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs b/MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs index fbc16109d8..6abfb9b2d8 100644 --- a/MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs @@ -7,7 +7,7 @@ using MediaBrowser.Model.Plugins; namespace MediaBrowser.Api.HttpHandlers { - public class PluginConfigurationHandler : BaseJsonHandler + public class PluginConfigurationHandler : BaseSerializationHandler { protected override Task GetObjectToSerialize() { diff --git a/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs b/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs index 1cb4e95f76..67246cbc6a 100644 --- a/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs @@ -10,7 +10,7 @@ namespace MediaBrowser.Api.HttpHandlers /// /// Provides information about installed plugins /// - public class PluginsHandler : BaseJsonHandler> + public class PluginsHandler : BaseSerializationHandler> { protected override Task> GetObjectToSerialize() { diff --git a/MediaBrowser.Api/HttpHandlers/StudioHandler.cs b/MediaBrowser.Api/HttpHandlers/StudioHandler.cs index af7e7fed0b..458dd9da13 100644 --- a/MediaBrowser.Api/HttpHandlers/StudioHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/StudioHandler.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Api.HttpHandlers /// /// Gets a single studio /// - public class StudioHandler : BaseJsonHandler + public class StudioHandler : BaseSerializationHandler { protected override Task GetObjectToSerialize() { diff --git a/MediaBrowser.Api/HttpHandlers/StudiosHandler.cs b/MediaBrowser.Api/HttpHandlers/StudiosHandler.cs index 486aa762b9..d9805c43c8 100644 --- a/MediaBrowser.Api/HttpHandlers/StudiosHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/StudiosHandler.cs @@ -9,7 +9,7 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Api.HttpHandlers { - public class StudiosHandler : BaseJsonHandler + public class StudiosHandler : BaseSerializationHandler { protected override Task GetObjectToSerialize() { diff --git a/MediaBrowser.Api/HttpHandlers/UsersHandler.cs b/MediaBrowser.Api/HttpHandlers/UsersHandler.cs index 44d23a208e..e7617fe881 100644 --- a/MediaBrowser.Api/HttpHandlers/UsersHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/UsersHandler.cs @@ -7,7 +7,7 @@ using MediaBrowser.Model.DTO; namespace MediaBrowser.Api.HttpHandlers { - class UsersHandler : BaseJsonHandler> + class UsersHandler : BaseSerializationHandler> { protected override Task> GetObjectToSerialize() { diff --git a/MediaBrowser.Api/HttpHandlers/WeatherHandler.cs b/MediaBrowser.Api/HttpHandlers/WeatherHandler.cs new file mode 100644 index 0000000000..b46473f119 --- /dev/null +++ b/MediaBrowser.Api/HttpHandlers/WeatherHandler.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading.Tasks; +using MediaBrowser.Common.Net.Handlers; +using MediaBrowser.Controller; +using MediaBrowser.Model.Weather; + +namespace MediaBrowser.Api.HttpHandlers +{ + class WeatherHandler : BaseSerializationHandler + { + protected override Task GetObjectToSerialize() + { + // If a specific zip code was requested on the query string, use that. Otherwise use the value from configuration + + string zipCode = QueryString["zipcode"]; + + if (string.IsNullOrWhiteSpace(zipCode)) + { + zipCode = Kernel.Instance.Configuration.WeatherZipCode; + } + + return Kernel.Instance.WeatherClient.GetWeatherInfoAsync(zipCode); + } + + /// + /// Tell the client to cache the weather info for 15 minutes + /// + public override TimeSpan CacheDuration + { + get + { + return TimeSpan.FromMinutes(15); + } + } + } +} diff --git a/MediaBrowser.Api/HttpHandlers/YearHandler.cs b/MediaBrowser.Api/HttpHandlers/YearHandler.cs index 0eb674e272..1b0c7f6704 100644 --- a/MediaBrowser.Api/HttpHandlers/YearHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/YearHandler.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Api.HttpHandlers /// /// Gets a single year /// - public class YearHandler : BaseJsonHandler + public class YearHandler : BaseSerializationHandler { protected override Task GetObjectToSerialize() { diff --git a/MediaBrowser.Api/HttpHandlers/YearsHandler.cs b/MediaBrowser.Api/HttpHandlers/YearsHandler.cs index ff05120a04..9a7325f649 100644 --- a/MediaBrowser.Api/HttpHandlers/YearsHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/YearsHandler.cs @@ -9,7 +9,7 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Api.HttpHandlers { - public class YearsHandler : BaseJsonHandler + public class YearsHandler : BaseSerializationHandler { protected override Task GetObjectToSerialize() { diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index d234beb15b..5d32210938 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -69,6 +69,7 @@ + diff --git a/MediaBrowser.Api/Plugin.cs b/MediaBrowser.Api/Plugin.cs index 97d4a58652..467b30ef15 100644 --- a/MediaBrowser.Api/Plugin.cs +++ b/MediaBrowser.Api/Plugin.cs @@ -101,6 +101,10 @@ namespace MediaBrowser.Api { return new StudioHandler(); } + else if (localPath.EndsWith("/api/weather", StringComparison.OrdinalIgnoreCase)) + { + return new WeatherHandler(); + } return null; } diff --git a/MediaBrowser.ApiInteraction/ApiClient.cs b/MediaBrowser.ApiInteraction/ApiClient.cs index 149529b809..99af250ca8 100644 --- a/MediaBrowser.ApiInteraction/ApiClient.cs +++ b/MediaBrowser.ApiInteraction/ApiClient.cs @@ -6,6 +6,7 @@ using System.Net.Http; using System.Threading.Tasks; using MediaBrowser.Model.DTO; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Weather; namespace MediaBrowser.ApiInteraction { @@ -564,6 +565,32 @@ namespace MediaBrowser.ApiInteraction } } + /// + /// Gets weather information for the default location as set in configuration + /// + public async Task GetWeatherInfo() + { + 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 GetWeatherInfo(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 /// diff --git a/MediaBrowser.Common/Net/Handlers/BaseSerializationHandler.cs b/MediaBrowser.Common/Net/Handlers/BaseSerializationHandler.cs index 1a366c158f..a0696d4a61 100644 --- a/MediaBrowser.Common/Net/Handlers/BaseSerializationHandler.cs +++ b/MediaBrowser.Common/Net/Handlers/BaseSerializationHandler.cs @@ -5,7 +5,7 @@ using MediaBrowser.Common.Serialization; namespace MediaBrowser.Common.Net.Handlers { - public abstract class BaseJsonHandler : BaseHandler + public abstract class BaseSerializationHandler : BaseHandler { public SerializationFormat SerializationFormat { diff --git a/MediaBrowser.Controller/Configuration/ServerConfiguration.cs b/MediaBrowser.Controller/Configuration/ServerConfiguration.cs index f6dde3aa1c..368c8ef371 100644 --- a/MediaBrowser.Controller/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Controller/Configuration/ServerConfiguration.cs @@ -5,5 +5,6 @@ namespace MediaBrowser.Controller.Configuration public class ServerConfiguration : BaseApplicationConfiguration { public bool EnableInternetProviders { get; set; } + public string WeatherZipCode { get; set; } } } diff --git a/MediaBrowser.Controller/Kernel.cs b/MediaBrowser.Controller/Kernel.cs index daf5fcd24d..1a161ccc98 100644 --- a/MediaBrowser.Controller/Kernel.cs +++ b/MediaBrowser.Controller/Kernel.cs @@ -14,6 +14,7 @@ using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; +using MediaBrowser.Controller.Weather; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Progress; @@ -24,6 +25,7 @@ namespace MediaBrowser.Controller public static Kernel Instance { get; private set; } public ItemController ItemController { get; private set; } + public WeatherClient WeatherClient { get; private set; } public IEnumerable Users { get; private set; } public Folder RootFolder { get; private set; } @@ -72,6 +74,7 @@ namespace MediaBrowser.Controller ItemController = new ItemController(); DirectoryWatchers = new DirectoryWatchers(); + WeatherClient = new WeatherClient(); ItemController.PreBeginResolvePath += ItemController_PreBeginResolvePath; ItemController.BeginResolvePath += ItemController_BeginResolvePath; @@ -228,7 +231,6 @@ namespace MediaBrowser.Controller user.Name = "Default User"; user.Id = Guid.Parse("5d1cf7fce25943b790d140095457a42b"); - //user.PrimaryImagePath = "D:\\Video\\TV\\Archer (2009)\\folder.jpg"; list.Add(user); diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index ab141c79f1..f72202fea8 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -37,6 +37,8 @@ + + False ..\packages\Rx-Core.2.0.20823\lib\Net45\System.Reactive.Core.dll @@ -77,6 +79,7 @@ + diff --git a/MediaBrowser.Controller/Weather/WeatherClient.cs b/MediaBrowser.Controller/Weather/WeatherClient.cs new file mode 100644 index 0000000000..c49c504773 --- /dev/null +++ b/MediaBrowser.Controller/Weather/WeatherClient.cs @@ -0,0 +1,191 @@ +using System; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Cache; +using System.Net.Http; +using System.Threading.Tasks; +using MediaBrowser.Common.Logging; +using MediaBrowser.Common.Serialization; +using MediaBrowser.Model.Weather; + +namespace MediaBrowser.Controller.Weather +{ + /// + /// Based on http://www.worldweatheronline.com/free-weather-feed.aspx + /// The classes in this file are a reproduction of the json output, which will then be converted to our weather model classes + /// + public class WeatherClient + { + private HttpClient HttpClient { get; set; } + + public WeatherClient() + { + WebRequestHandler handler = new WebRequestHandler(); + + handler.AutomaticDecompression = DecompressionMethods.Deflate; + handler.CachePolicy = new RequestCachePolicy(RequestCacheLevel.Revalidate); + + HttpClient = new HttpClient(handler); + } + + public async Task GetWeatherInfoAsync(string zipCode) + { + int numDays = 5; + string apiKey = "24902f60f1231941120109"; + + string url = "http://free.worldweatheronline.com/feed/weather.ashx?q=" + zipCode + "&format=json&num_of_days=" + numDays + "&key=" + apiKey; + + Logger.LogInfo("Accessing weather from " + url); + + using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + { + WeatherData data = JsonSerializer.DeserializeFromStream(stream).data; + + return GetWeatherInfo(data); + } + } + + /// + /// Converst the json output to our WeatherInfo model class + /// + private WeatherInfo GetWeatherInfo(WeatherData data) + { + WeatherInfo info = new WeatherInfo(); + + if (data.current_condition.Any()) + { + info.CurrentWeather = data.current_condition.First().ToWeatherStatus(); + } + + info.Forecasts = data.weather.Select(w => w.ToWeatherForecast()).ToArray(); + + return info; + } + } + + class WeatherResult + { + public WeatherData data { get; set; } + } + + public class WeatherData + { + public WeatherCondition[] current_condition { get; set; } + public DailyWeatherInfo[] weather { get; set; } + } + + public class WeatherCondition + { + public string temp_C { get; set; } + public string temp_F { get; set; } + public string humidity { get; set; } + public string weatherCode { get; set; } + + public WeatherStatus ToWeatherStatus() + { + return new WeatherStatus() + { + TemperatureCelsius = int.Parse(temp_C), + TemperatureFahrenheit = int.Parse(temp_F), + Humidity = int.Parse(humidity), + Condition = DailyWeatherInfo.GetCondition(weatherCode) + }; + } + } + + public class DailyWeatherInfo + { + public string date { get; set; } + public string precipMM { get; set; } + public string tempMaxC { get; set; } + public string tempMaxF { get; set; } + public string tempMinC { get; set; } + public string tempMinF { get; set; } + public string weatherCode { get; set; } + public string winddir16Point { get; set; } + public string winddirDegree { get; set; } + public string winddirection { get; set; } + public string windspeedKmph { get; set; } + public string windspeedMiles { get; set; } + + public WeatherForecast ToWeatherForecast() + { + return new WeatherForecast() + { + Date = DateTime.Parse(date), + HighTemperatureCelsius = int.Parse(tempMaxC), + HighTemperatureFahrenheit = int.Parse(tempMaxF), + LowTemperatureCelsius = int.Parse(tempMinC), + LowTemperatureFahrenheit = int.Parse(tempMinF), + Condition = GetCondition(weatherCode) + }; + } + + public static WeatherConditions GetCondition(string weatherCode) + { + switch (weatherCode) + { + case "362": + case "365": + case "320": + case "317": + case "182": + return WeatherConditions.Sleet; + case "338": + case "335": + case "332": + case "329": + case "326": + case "323": + case "377": + case "374": + case "371": + case "368": + case "395": + case "392": + case "350": + case "227": + case "179": + return WeatherConditions.Snow; + case "314": + case "311": + case "308": + case "305": + case "302": + case "299": + case "296": + case "293": + case "284": + case "281": + case "266": + case "263": + case "359": + case "356": + case "353": + case "185": + case "176": + return WeatherConditions.Rain; + case "260": + case "248": + return WeatherConditions.Fog; + case "389": + case "386": + case "200": + return WeatherConditions.Thunderstorm; + case "230": + return WeatherConditions.Blizzard; + case "143": + return WeatherConditions.Mist; + case "122": + return WeatherConditions.Overcast; + case "119": + return WeatherConditions.Cloudy; + case "115": + return WeatherConditions.PartlyCloudy; + default: + return WeatherConditions.Sunny; + } + } + } +} diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 9d9ae9f5f0..dabc6864ca 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -56,6 +56,9 @@ + + + diff --git a/MediaBrowser.Model/Weather/WeatherForecast.cs b/MediaBrowser.Model/Weather/WeatherForecast.cs new file mode 100644 index 0000000000..f77d92366a --- /dev/null +++ b/MediaBrowser.Model/Weather/WeatherForecast.cs @@ -0,0 +1,30 @@ +using System; +using ProtoBuf; + +namespace MediaBrowser.Model.Weather +{ + /// + /// Represents a weather forecase for a specific date + /// + [ProtoContract] + public class WeatherForecast + { + [ProtoMember(1)] + public DateTime Date { get; set; } + + [ProtoMember(2)] + public int HighTemperatureFahrenheit { get; set; } + + [ProtoMember(3)] + public int LowTemperatureFahrenheit { get; set; } + + [ProtoMember(4)] + public int HighTemperatureCelsius { get; set; } + + [ProtoMember(5)] + public int LowTemperatureCelsius { get; set; } + + [ProtoMember(6)] + public WeatherConditions Condition { get; set; } + } +} diff --git a/MediaBrowser.Model/Weather/WeatherInfo.cs b/MediaBrowser.Model/Weather/WeatherInfo.cs new file mode 100644 index 0000000000..7cad4d2484 --- /dev/null +++ b/MediaBrowser.Model/Weather/WeatherInfo.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +namespace MediaBrowser.Model.Weather +{ + [ProtoContract] + public class WeatherInfo + { + [ProtoMember(1)] + public WeatherStatus CurrentWeather { get; set; } + + [ProtoMember(2)] + public WeatherForecast[] Forecasts { get; set; } + } +} diff --git a/MediaBrowser.Model/Weather/WeatherStatus.cs b/MediaBrowser.Model/Weather/WeatherStatus.cs new file mode 100644 index 0000000000..7019d319b7 --- /dev/null +++ b/MediaBrowser.Model/Weather/WeatherStatus.cs @@ -0,0 +1,38 @@ +using ProtoBuf; + +namespace MediaBrowser.Model.Weather +{ + /// + /// Represents the current weather status + /// + [ProtoContract] + public class WeatherStatus + { + [ProtoMember(1)] + public int TemperatureFahrenheit { get; set; } + + [ProtoMember(2)] + public int TemperatureCelsius { get; set; } + + [ProtoMember(3)] + public int Humidity { get; set; } + + [ProtoMember(4)] + public WeatherConditions Condition { get; set; } + } + + public enum WeatherConditions + { + Sunny, + PartlyCloudy, + Cloudy, + Overcast, + Mist, + Snow, + Rain, + Sleet, + Fog, + Blizzard, + Thunderstorm + } +}