fixes #358 - Weather validation in Server configuration

pull/702/head
Luke Pulverenti 11 years ago
parent 0acc257354
commit 189618a751

@ -62,7 +62,7 @@ namespace MediaBrowser.Api
_libraryManager,
_userDataRepository,
Logger,
request, item => item is BaseGame,
request, item => item is Game,
SimilarItemsHelper.GetSimiliarityScore);
return ToOptimizedResult(result);

@ -189,7 +189,7 @@ namespace MediaBrowser.Api
{
AlbumCount = items.OfType<MusicAlbum>().Count(),
EpisodeCount = items.OfType<Episode>().Count(),
GameCount = items.OfType<BaseGame>().Count(),
GameCount = items.OfType<Game>().Count(),
MovieCount = items.OfType<Movie>().Count(),
SeriesCount = items.OfType<Series>().Count(),
SongCount = items.OfType<Audio>().Count(),
@ -285,7 +285,7 @@ namespace MediaBrowser.Api
item.ProviderIds = request.ProviderIds;
var game = item as BaseGame;
var game = item as Game;
if (game != null)
{

@ -115,7 +115,6 @@
<Compile Include="UserService.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="VideosService.cs" />
<Compile Include="WeatherService.cs" />
<Compile Include="WebSocket\LogFileWebSocketListener.cs" />
<Compile Include="WebSocket\SessionInfoWebSocketListener.cs" />
<Compile Include="WebSocket\SystemInfoWebSocketListener.cs" />

@ -169,7 +169,7 @@ namespace MediaBrowser.Api.UserLibrary
SeriesCount = items.OfType<Series>().Count(),
GameCount = items.OfType<BaseGame>().Count()
GameCount = items.OfType<Game>().Count()
};
return ToOptimizedResult(counts);

@ -160,7 +160,7 @@ namespace MediaBrowser.Api.UserLibrary
SeriesCount = items.OfType<Series>().Count(),
GameCount = items.OfType<BaseGame>().Count(),
GameCount = items.OfType<Game>().Count(),
SongCount = items.OfType<Audio>().Count(),

@ -132,7 +132,7 @@ namespace MediaBrowser.Api.UserLibrary
SeriesCount = items.OfType<Series>().Count(),
GameCount = items.OfType<BaseGame>().Count(),
GameCount = items.OfType<Game>().Count(),
SongCount = items.OfType<Audio>().Count(),

@ -1,41 +0,0 @@
using MediaBrowser.Controller;
using MediaBrowser.Model.Weather;
using ServiceStack.ServiceHost;
using System.Linq;
using System.Threading;
namespace MediaBrowser.Api
{
/// <summary>
/// Class Weather
/// </summary>
[Route("/Weather", "GET")]
[Api(Description = "Gets weather information for a given location")]
public class GetWeather : IReturn<WeatherInfo>
{
/// <summary>
/// Gets or sets the location.
/// </summary>
/// <value>The location.</value>
[ApiMember(Name = "Location", Description = "Us zip / City, State, Country / City, Country", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Location { get; set; }
}
/// <summary>
/// Class WeatherService
/// </summary>
public class WeatherService : BaseApiService
{
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetWeather request)
{
var result = Kernel.Instance.WeatherProviders.First().GetWeatherInfoAsync(request.Location, CancellationToken.None).Result;
return ToOptimizedResult(result);
}
}
}

@ -514,15 +514,20 @@ namespace MediaBrowser.Controller.Dto
}
}
var game = item as BaseGame;
var game = item as Game;
if (game != null)
{
dto.Players = game.PlayersSupported;
dto.GameSystem = game.GameSystem;
SetGameProperties(dto, game);
}
}
private void SetGameProperties(BaseItemDto dto, Game item)
{
dto.Players = item.PlayersSupported;
dto.GameSystem = item.GameSystem;
}
/// <summary>
/// Since it can be slow to make all of these calculations independently, this method will provide a way to do them all at once
/// </summary>

@ -1,7 +1,5 @@
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.MediaInfo;
using MediaBrowser.Controller.Weather;
using System.Collections.Generic;
namespace MediaBrowser.Controller
{
@ -28,12 +26,6 @@ namespace MediaBrowser.Controller
/// <value>The FFMPEG controller.</value>
public FFMpegManager FFMpegManager { get; set; }
/// <summary>
/// Gets the list of currently registered weather prvoiders
/// </summary>
/// <value>The weather providers.</value>
public IEnumerable<IWeatherProvider> WeatherProviders { get; set; }
/// <summary>
/// Creates a kernel based on a Data path, which is akin to our current programdata path
/// </summary>

@ -161,7 +161,6 @@
<Compile Include="Sorting\IBaseItemComparer.cs" />
<Compile Include="Sorting\IUserBaseItemComparer.cs" />
<Compile Include="Updates\IInstallationManager.cs" />
<Compile Include="Weather\IWeatherProvider.cs" />
<Compile Include="Providers\BaseItemXmlParser.cs" />
</ItemGroup>
<ItemGroup>

@ -1,20 +0,0 @@
using MediaBrowser.Model.Weather;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Weather
{
/// <summary>
/// Interface IWeatherProvider
/// </summary>
public interface IWeatherProvider
{
/// <summary>
/// Gets the weather info async.
/// </summary>
/// <param name="location">The location.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{WeatherInfo}.</returns>
Task<WeatherInfo> GetWeatherInfoAsync(string location, CancellationToken cancellationToken);
}
}

@ -316,18 +316,6 @@
<Compile Include="..\MediaBrowser.Model\Updates\PackageVersionInfo.cs">
<Link>Updates\PackageVersionInfo.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Weather\WeatherForecast.cs">
<Link>Weather\WeatherForecast.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Weather\WeatherInfo.cs">
<Link>Weather\WeatherInfo.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Weather\WeatherStatus.cs">
<Link>Weather\WeatherStatus.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Weather\WeatherUnits.cs">
<Link>Weather\WeatherUnits.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Web\QueryStringDictionary.cs">
<Link>Web\QueryStringDictionary.cs</Link>
</Compile>

@ -8,7 +8,6 @@ using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Weather;
using System;
using System.Collections.Generic;
using System.IO;
@ -224,22 +223,6 @@ namespace MediaBrowser.Model.ApiClient
/// <returns>Task{List{ParentalRating}}.</returns>
Task<List<ParentalRating>> GetParentalRatingsAsync();
/// <summary>
/// Gets weather information for the default location as set in configuration
/// </summary>
/// <returns>Task{WeatherInfo}.</returns>
Task<WeatherInfo> GetWeatherInfoAsync();
/// <summary>
/// Gets weather information for a specific location
/// Location can be a US zipcode, or "city,state", "city,state,country", "city,country"
/// It can also be an ip address, or "latitude,longitude"
/// </summary>
/// <param name="location">The location.</param>
/// <returns>Task{WeatherInfo}.</returns>
/// <exception cref="ArgumentNullException">location</exception>
Task<WeatherInfo> GetWeatherInfoAsync(string location);
/// <summary>
/// Gets local trailers for an item
/// </summary>

@ -1,5 +1,4 @@
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Weather;
using System;
namespace MediaBrowser.Model.Configuration
@ -33,12 +32,6 @@ namespace MediaBrowser.Model.Configuration
/// <value><c>true</c> if [enable internet providers]; otherwise, <c>false</c>.</value>
public bool EnableInternetProviders { get; set; }
/// <summary>
/// Gets or sets the zip code to use when displaying weather
/// </summary>
/// <value>The weather location.</value>
public string WeatherLocation { get; set; }
/// <summary>
/// Gets or sets the item by name path.
/// </summary>
@ -50,12 +43,6 @@ namespace MediaBrowser.Model.Configuration
/// </summary>
/// <value>The display name of the season zero.</value>
public string SeasonZeroDisplayName { get; set; }
/// <summary>
/// Gets or sets the weather unit to use when displaying weather
/// </summary>
/// <value>The weather unit.</value>
public WeatherUnits WeatherUnit { get; set; }
/// <summary>
/// Gets or sets the metadata refresh days.

@ -134,10 +134,6 @@
<Compile Include="Tasks\TaskTriggerInfo.cs" />
<Compile Include="Updates\PackageInfo.cs" />
<Compile Include="Updates\PackageVersionInfo.cs" />
<Compile Include="Weather\WeatherForecast.cs" />
<Compile Include="Weather\WeatherInfo.cs" />
<Compile Include="Weather\WeatherStatus.cs" />
<Compile Include="Weather\WeatherUnits.cs" />
<Compile Include="Web\QueryStringDictionary.cs" />
<None Include="FodyWeavers.xml" />
</ItemGroup>

@ -1,46 +0,0 @@
using System;
namespace MediaBrowser.Model.Weather
{
/// <summary>
/// Represents a weather forecast for a specific date
/// </summary>
public class WeatherForecast
{
/// <summary>
/// Gets or sets the date.
/// </summary>
/// <value>The date.</value>
public DateTime Date { get; set; }
/// <summary>
/// Gets or sets the high temperature fahrenheit.
/// </summary>
/// <value>The high temperature fahrenheit.</value>
public int HighTemperatureFahrenheit { get; set; }
/// <summary>
/// Gets or sets the low temperature fahrenheit.
/// </summary>
/// <value>The low temperature fahrenheit.</value>
public int LowTemperatureFahrenheit { get; set; }
/// <summary>
/// Gets or sets the high temperature celsius.
/// </summary>
/// <value>The high temperature celsius.</value>
public int HighTemperatureCelsius { get; set; }
/// <summary>
/// Gets or sets the low temperature celsius.
/// </summary>
/// <value>The low temperature celsius.</value>
public int LowTemperatureCelsius { get; set; }
/// <summary>
/// Gets or sets the condition.
/// </summary>
/// <value>The condition.</value>
public WeatherConditions Condition { get; set; }
}
}

@ -1,29 +0,0 @@

namespace MediaBrowser.Model.Weather
{
/// <summary>
/// Class WeatherInfo
/// </summary>
public class WeatherInfo
{
/// <summary>
/// Gets or sets the current weather.
/// </summary>
/// <value>The current weather.</value>
public WeatherStatus CurrentWeather { get; set; }
/// <summary>
/// Gets or sets the forecasts.
/// </summary>
/// <value>The forecasts.</value>
public WeatherForecast[] Forecasts { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="WeatherInfo"/> class.
/// </summary>
public WeatherInfo()
{
Forecasts = new WeatherForecast[] {};
}
}
}

@ -1,84 +0,0 @@

namespace MediaBrowser.Model.Weather
{
/// <summary>
/// Represents the current weather status
/// </summary>
public class WeatherStatus
{
/// <summary>
/// Gets or sets the temperature fahrenheit.
/// </summary>
/// <value>The temperature fahrenheit.</value>
public int TemperatureFahrenheit { get; set; }
/// <summary>
/// Gets or sets the temperature celsius.
/// </summary>
/// <value>The temperature celsius.</value>
public int TemperatureCelsius { get; set; }
/// <summary>
/// Gets or sets the humidity.
/// </summary>
/// <value>The humidity.</value>
public int Humidity { get; set; }
/// <summary>
/// Gets or sets the condition.
/// </summary>
/// <value>The condition.</value>
public WeatherConditions Condition { get; set; }
}
/// <summary>
/// Enum WeatherConditions
/// </summary>
public enum WeatherConditions
{
/// <summary>
/// The sunny
/// </summary>
Sunny,
/// <summary>
/// The partly cloudy
/// </summary>
PartlyCloudy,
/// <summary>
/// The cloudy
/// </summary>
Cloudy,
/// <summary>
/// The overcast
/// </summary>
Overcast,
/// <summary>
/// The mist
/// </summary>
Mist,
/// <summary>
/// The snow
/// </summary>
Snow,
/// <summary>
/// The rain
/// </summary>
Rain,
/// <summary>
/// The sleet
/// </summary>
Sleet,
/// <summary>
/// The fog
/// </summary>
Fog,
/// <summary>
/// The blizzard
/// </summary>
Blizzard,
/// <summary>
/// The thunderstorm
/// </summary>
Thunderstorm
}
}

@ -1,18 +0,0 @@

namespace MediaBrowser.Model.Weather
{
/// <summary>
/// Enum WeatherUnits
/// </summary>
public enum WeatherUnits
{
/// <summary>
/// The fahrenheit
/// </summary>
Fahrenheit,
/// <summary>
/// The celsius
/// </summary>
Celsius
}
}

@ -185,7 +185,6 @@
<Compile Include="Updates\InstallationManager.cs" />
<Compile Include="WebSocket\AlchemyServer.cs" />
<Compile Include="WebSocket\AlchemyWebSocket.cs" />
<Compile Include="WorldWeatherOnline\WeatherProvider.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj">

@ -1,360 +0,0 @@
using System.Globalization;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Weather;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Weather;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.WorldWeatherOnline
{
/// <summary>
/// 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
/// </summary>
public class WeatherProvider : IWeatherProvider
{
/// <summary>
/// Gets or sets the logger.
/// </summary>
/// <value>The logger.</value>
private ILogger Logger { get; set; }
/// <summary>
/// Gets the json serializer.
/// </summary>
/// <value>The json serializer.</value>
protected IJsonSerializer JsonSerializer { get; private set; }
/// <summary>
/// The _HTTP client
/// </summary>
private IHttpClient HttpClient { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="WeatherProvider" /> class.
/// </summary>
/// <param name="jsonSerializer">The json serializer.</param>
/// <param name="httpClient">The HTTP client.</param>
/// <param name="logger">The logger.</param>
/// <exception cref="System.ArgumentNullException">logger</exception>
public WeatherProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger)
{
if (logger == null)
{
throw new ArgumentNullException("logger");
}
if (httpClient == null)
{
throw new ArgumentNullException("httpClient");
}
if (jsonSerializer == null)
{
throw new ArgumentNullException("jsonSerializer");
}
JsonSerializer = jsonSerializer;
HttpClient = httpClient;
Logger = logger;
}
/// <summary>
/// The _weather semaphore
/// </summary>
private readonly SemaphoreSlim _weatherSemaphore = new SemaphoreSlim(10, 10);
/// <summary>
/// Gets the weather info async.
/// </summary>
/// <param name="location">The location.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{WeatherInfo}.</returns>
/// <exception cref="System.ArgumentNullException">location</exception>
public async Task<WeatherInfo> GetWeatherInfoAsync(string location, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(location))
{
throw new ArgumentNullException("location");
}
if (cancellationToken == null)
{
throw new ArgumentNullException("cancellationToken");
}
const int numDays = 5;
const string apiKey = "24902f60f1231941120109";
var url = "http://free.worldweatheronline.com/feed/weather.ashx?q=" + location + "&format=json&num_of_days=" + numDays + "&key=" + apiKey;
Logger.Info("Accessing weather from " + url);
using (var stream = await HttpClient.Get(url, _weatherSemaphore, cancellationToken).ConfigureAwait(false))
{
var data = JsonSerializer.DeserializeFromStream<WeatherResult>(stream).data;
return GetWeatherInfo(data);
}
}
/// <summary>
/// Converst the json output to our WeatherInfo model class
/// </summary>
/// <param name="data">The data.</param>
/// <returns>WeatherInfo.</returns>
private WeatherInfo GetWeatherInfo(WeatherData data)
{
var info = new WeatherInfo();
if (data.current_condition != null)
{
var condition = data.current_condition.FirstOrDefault();
if (condition != null)
{
info.CurrentWeather = condition.ToWeatherStatus();
}
}
if (data.weather != null)
{
info.Forecasts = data.weather.Select(w => w.ToWeatherForecast()).ToArray();
}
return info;
}
}
/// <summary>
/// Class WeatherResult
/// </summary>
class WeatherResult
{
/// <summary>
/// Gets or sets the data.
/// </summary>
/// <value>The data.</value>
public WeatherData data { get; set; }
}
/// <summary>
/// Class WeatherData
/// </summary>
public class WeatherData
{
/// <summary>
/// Gets or sets the current_condition.
/// </summary>
/// <value>The current_condition.</value>
public WeatherCondition[] current_condition { get; set; }
/// <summary>
/// Gets or sets the weather.
/// </summary>
/// <value>The weather.</value>
public DailyWeatherInfo[] weather { get; set; }
}
/// <summary>
/// Class WeatherCondition
/// </summary>
public class WeatherCondition
{
/// <summary>
/// Gets or sets the temp_ C.
/// </summary>
/// <value>The temp_ C.</value>
public string temp_C { get; set; }
/// <summary>
/// Gets or sets the temp_ F.
/// </summary>
/// <value>The temp_ F.</value>
public string temp_F { get; set; }
/// <summary>
/// Gets or sets the humidity.
/// </summary>
/// <value>The humidity.</value>
public string humidity { get; set; }
/// <summary>
/// Gets or sets the weather code.
/// </summary>
/// <value>The weather code.</value>
public string weatherCode { get; set; }
protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
/// <summary>
/// To the weather status.
/// </summary>
/// <returns>WeatherStatus.</returns>
public WeatherStatus ToWeatherStatus()
{
return new WeatherStatus
{
TemperatureCelsius = int.Parse(temp_C, UsCulture),
TemperatureFahrenheit = int.Parse(temp_F, UsCulture),
Humidity = int.Parse(humidity, UsCulture),
Condition = DailyWeatherInfo.GetCondition(weatherCode)
};
}
}
/// <summary>
/// Class DailyWeatherInfo
/// </summary>
public class DailyWeatherInfo
{
/// <summary>
/// Gets or sets the date.
/// </summary>
/// <value>The date.</value>
public string date { get; set; }
/// <summary>
/// Gets or sets the precip MM.
/// </summary>
/// <value>The precip MM.</value>
public string precipMM { get; set; }
/// <summary>
/// Gets or sets the temp max C.
/// </summary>
/// <value>The temp max C.</value>
public string tempMaxC { get; set; }
/// <summary>
/// Gets or sets the temp max F.
/// </summary>
/// <value>The temp max F.</value>
public string tempMaxF { get; set; }
/// <summary>
/// Gets or sets the temp min C.
/// </summary>
/// <value>The temp min C.</value>
public string tempMinC { get; set; }
/// <summary>
/// Gets or sets the temp min F.
/// </summary>
/// <value>The temp min F.</value>
public string tempMinF { get; set; }
/// <summary>
/// Gets or sets the weather code.
/// </summary>
/// <value>The weather code.</value>
public string weatherCode { get; set; }
/// <summary>
/// Gets or sets the winddir16 point.
/// </summary>
/// <value>The winddir16 point.</value>
public string winddir16Point { get; set; }
/// <summary>
/// Gets or sets the winddir degree.
/// </summary>
/// <value>The winddir degree.</value>
public string winddirDegree { get; set; }
/// <summary>
/// Gets or sets the winddirection.
/// </summary>
/// <value>The winddirection.</value>
public string winddirection { get; set; }
/// <summary>
/// Gets or sets the windspeed KMPH.
/// </summary>
/// <value>The windspeed KMPH.</value>
public string windspeedKmph { get; set; }
/// <summary>
/// Gets or sets the windspeed miles.
/// </summary>
/// <value>The windspeed miles.</value>
public string windspeedMiles { get; set; }
protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
/// <summary>
/// To the weather forecast.
/// </summary>
/// <returns>WeatherForecast.</returns>
public WeatherForecast ToWeatherForecast()
{
return new WeatherForecast
{
Date = DateTime.Parse(date, UsCulture),
HighTemperatureCelsius = int.Parse(tempMaxC, UsCulture),
HighTemperatureFahrenheit = int.Parse(tempMaxF, UsCulture),
LowTemperatureCelsius = int.Parse(tempMinC, UsCulture),
LowTemperatureFahrenheit = int.Parse(tempMinF, UsCulture),
Condition = GetCondition(weatherCode)
};
}
/// <summary>
/// Gets the condition.
/// </summary>
/// <param name="weatherCode">The weather code.</param>
/// <returns>WeatherConditions.</returns>
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;
}
}
}
}

@ -23,7 +23,6 @@ using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Controller.Updates;
using MediaBrowser.Controller.Weather;
using MediaBrowser.IsoMounter;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
@ -311,7 +310,6 @@ namespace MediaBrowser.ServerApplication
ApplicationPaths, ItemRepository);
Parallel.Invoke(
() => ServerKernel.FFMpegManager = new FFMpegManager(ApplicationPaths, MediaEncoder, LibraryManager, Logger, ItemRepository),
() => ServerKernel.WeatherProviders = GetExports<IWeatherProvider>(),
() => ServerKernel.ImageManager.ImageEnhancers = GetExports<IImageEnhancer>().OrderBy(e => e.Priority).ToArray(),
() => LocalizedStrings.StringFiles = GetExports<LocalizedStringData>(),
SetStaticProperties

@ -1245,24 +1245,6 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
});
};
/**
* Gets weather info
* @param {String} location - us zip code / city, state, country / city, country
* Omit location to get weather info using stored server configuration value
*/
self.getWeatherInfo = function (location) {
var url = self.getUrl("weather", {
location: location
});
return self.ajax({
type: "GET",
url: url,
dataType: "json"
});
};
/**
* Gets all users from the server
*/

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.124" targetFramework="net45" />
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.125" targetFramework="net45" />
<package id="ServiceStack.Common" version="3.9.54" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.54" targetFramework="net45" />
</packages>
Loading…
Cancel
Save