Merge pull request #1873 from MediaBrowser/dev

Dev
This commit is contained in:
Luke 2016-06-23 13:05:53 -04:00 committed by GitHub
commit cc9145b398
27 changed files with 435 additions and 117 deletions

View File

@ -9,7 +9,9 @@ using ServiceStack.Web;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using CommonIO; using CommonIO;
using MediaBrowser.Controller.MediaEncoding;
namespace MediaBrowser.Api namespace MediaBrowser.Api
{ {
@ -71,6 +73,14 @@ namespace MediaBrowser.Api
} }
[Route("/System/MediaEncoder/Path", "POST", Summary = "Updates the path to the media encoder")]
[Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)]
public class UpdateMediaEncoderPath : IReturnVoid
{
[ApiMember(Name = "Path", Description = "Path", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Path { get; set; }
}
public class ConfigurationService : BaseApiService public class ConfigurationService : BaseApiService
{ {
/// <summary> /// <summary>
@ -86,14 +96,22 @@ namespace MediaBrowser.Api
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IProviderManager _providerManager; private readonly IProviderManager _providerManager;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IMediaEncoder _mediaEncoder;
public ConfigurationService(IJsonSerializer jsonSerializer, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IProviderManager providerManager, ILibraryManager libraryManager) public ConfigurationService(IJsonSerializer jsonSerializer, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IProviderManager providerManager, ILibraryManager libraryManager, IMediaEncoder mediaEncoder)
{ {
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_configurationManager = configurationManager; _configurationManager = configurationManager;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_providerManager = providerManager; _providerManager = providerManager;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_mediaEncoder = mediaEncoder;
}
public void Post(UpdateMediaEncoderPath request)
{
var task = _mediaEncoder.UpdateEncoderPath(request.Path);
Task.WaitAll(task);
} }
/// <summary> /// <summary>

View File

@ -38,6 +38,12 @@ namespace MediaBrowser.Api
{ {
} }
[Route("/Items/RemoteSearch/Trailer", "POST")]
[Authenticated]
public class GetTrailerRemoteSearchResults : RemoteSearchQuery<TrailerInfo>, IReturn<List<RemoteSearchResult>>
{
}
[Route("/Items/RemoteSearch/AdultVideo", "POST")] [Route("/Items/RemoteSearch/AdultVideo", "POST")]
[Authenticated] [Authenticated]
public class GetAdultVideoRemoteSearchResults : RemoteSearchQuery<ItemLookupInfo>, IReturn<List<RemoteSearchResult>> public class GetAdultVideoRemoteSearchResults : RemoteSearchQuery<ItemLookupInfo>, IReturn<List<RemoteSearchResult>>
@ -132,6 +138,13 @@ namespace MediaBrowser.Api
return ToOptimizedResult(infos); return ToOptimizedResult(infos);
} }
public async Task<object> Post(GetTrailerRemoteSearchResults request)
{
var result = await _providerManager.GetRemoteSearchResults<Trailer, TrailerInfo>(request, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
public async Task<object> Post(GetMovieRemoteSearchResults request) public async Task<object> Post(GetMovieRemoteSearchResults request)
{ {
var result = await _providerManager.GetRemoteSearchResults<Movie, MovieInfo>(request, CancellationToken.None).ConfigureAwait(false); var result = await _providerManager.GetRemoteSearchResults<Movie, MovieInfo>(request, CancellationToken.None).ConfigureAwait(false);
@ -181,11 +194,9 @@ namespace MediaBrowser.Api
return ToOptimizedResult(result); return ToOptimizedResult(result);
} }
public async Task<object> Get(GetRemoteSearchImage request) public Task<object> Get(GetRemoteSearchImage request)
{ {
var result = GetRemoteImage(request).ConfigureAwait(false); return GetRemoteImage(request);
return result;
} }
public void Post(ApplySearchCriteria request) public void Post(ApplySearchCriteria request)

View File

@ -11,6 +11,7 @@ using ServiceStack;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.MediaEncoding;
namespace MediaBrowser.Api namespace MediaBrowser.Api
{ {
@ -52,14 +53,16 @@ namespace MediaBrowser.Api
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly IConnectManager _connectManager; private readonly IConnectManager _connectManager;
private readonly ILiveTvManager _liveTvManager; private readonly ILiveTvManager _liveTvManager;
private readonly IMediaEncoder _mediaEncoder;
public StartupWizardService(IServerConfigurationManager config, IServerApplicationHost appHost, IUserManager userManager, IConnectManager connectManager, ILiveTvManager liveTvManager) public StartupWizardService(IServerConfigurationManager config, IServerApplicationHost appHost, IUserManager userManager, IConnectManager connectManager, ILiveTvManager liveTvManager, IMediaEncoder mediaEncoder)
{ {
_config = config; _config = config;
_appHost = appHost; _appHost = appHost;
_userManager = userManager; _userManager = userManager;
_connectManager = connectManager; _connectManager = connectManager;
_liveTvManager = liveTvManager; _liveTvManager = liveTvManager;
_mediaEncoder = mediaEncoder;
} }
public void Post(ReportStartupWizardComplete request) public void Post(ReportStartupWizardComplete request)
@ -75,7 +78,8 @@ namespace MediaBrowser.Api
return new StartupInfo return new StartupInfo
{ {
SupportsRunningAsService = info.SupportsRunningAsService SupportsRunningAsService = info.SupportsRunningAsService,
HasMediaEncoder = !string.IsNullOrWhiteSpace(_mediaEncoder.EncoderPath)
}; };
} }
@ -231,6 +235,7 @@ namespace MediaBrowser.Api
public class StartupInfo public class StartupInfo
{ {
public bool SupportsRunningAsService { get; set; } public bool SupportsRunningAsService { get; set; }
public bool HasMediaEncoder { get; set; }
} }
public class StartupUser public class StartupUser

View File

@ -19,7 +19,7 @@ namespace MediaBrowser.Api.System
/// Class GetSystemInfo /// Class GetSystemInfo
/// </summary> /// </summary>
[Route("/System/Info", "GET", Summary = "Gets information about the server")] [Route("/System/Info", "GET", Summary = "Gets information about the server")]
[Authenticated(EscapeParentalControl = true)] [Authenticated(EscapeParentalControl = true, AllowBeforeStartupWizard = true)]
public class GetSystemInfo : IReturn<SystemInfo> public class GetSystemInfo : IReturn<SystemInfo>
{ {

View File

@ -119,11 +119,17 @@ namespace MediaBrowser.Api.UserLibrary
// Default list type = children // Default list type = children
var folder = item as Folder;
if (folder == null)
{
folder = user == null ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder();
}
if (!string.IsNullOrEmpty(request.Ids)) if (!string.IsNullOrEmpty(request.Ids))
{ {
request.Recursive = true; request.Recursive = true;
var query = GetItemsQuery(request, user); var query = GetItemsQuery(request, user);
var result = await ((Folder)item).GetItems(query).ConfigureAwait(false); var result = await folder.GetItems(query).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(request.SortBy)) if (string.IsNullOrWhiteSpace(request.SortBy))
{ {
@ -138,22 +144,22 @@ namespace MediaBrowser.Api.UserLibrary
if (request.Recursive) if (request.Recursive)
{ {
return await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); return await folder.GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
} }
if (user == null) if (user == null)
{ {
return await ((Folder)item).GetItems(GetItemsQuery(request, null)).ConfigureAwait(false); return await folder.GetItems(GetItemsQuery(request, null)).ConfigureAwait(false);
} }
var userRoot = item as UserRootFolder; var userRoot = item as UserRootFolder;
if (userRoot == null) if (userRoot == null)
{ {
return await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); return await folder.GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
} }
IEnumerable<BaseItem> items = ((Folder)item).GetChildren(user, true); IEnumerable<BaseItem> items = folder.GetChildren(user, true);
var itemsArray = items.ToArray(); var itemsArray = items.ToArray();

View File

@ -130,5 +130,7 @@ namespace MediaBrowser.Controller.MediaEncoding
string EscapeSubtitleFilterPath(string path); string EscapeSubtitleFilterPath(string path);
void Init(); void Init();
Task UpdateEncoderPath(string path);
} }
} }

View File

@ -23,6 +23,7 @@ using System.Threading.Tasks;
using CommonIO; using CommonIO;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
namespace MediaBrowser.MediaEncoding.Encoder namespace MediaBrowser.MediaEncoding.Encoder
{ {
@ -118,6 +119,35 @@ namespace MediaBrowser.MediaEncoding.Encoder
} }
} }
public async Task UpdateEncoderPath(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentNullException("path");
}
if (!File.Exists(path) && !Directory.Exists(path))
{
throw new ResourceNotFoundException();
}
var newPaths = GetEncoderPaths(path);
if (string.IsNullOrWhiteSpace(newPaths.Item1))
{
throw new ResourceNotFoundException("ffmpeg not found");
}
if (string.IsNullOrWhiteSpace(newPaths.Item2))
{
throw new ResourceNotFoundException("ffprobe not found");
}
var config = GetEncodingOptions();
config.EncoderAppPath = path;
ConfigurationManager.SaveConfiguration("encoding", config);
Init();
}
private void ConfigureEncoderPaths() private void ConfigureEncoderPaths()
{ {
if (_hasExternalEncoder) if (_hasExternalEncoder)
@ -131,46 +161,60 @@ namespace MediaBrowser.MediaEncoding.Encoder
{ {
appPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg"); appPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg");
} }
var newPaths = GetEncoderPaths(appPath);
if (!string.IsNullOrWhiteSpace(appPath)) if (!string.IsNullOrWhiteSpace(newPaths.Item1) && !string.IsNullOrWhiteSpace(newPaths.Item2))
{ {
if (Directory.Exists(appPath)) FFMpegPath = newPaths.Item1;
{ FFProbePath = newPaths.Item2;
SetPathsFromDirectory(appPath);
}
else if (File.Exists(appPath))
{
FFMpegPath = appPath;
SetProbePathFromEncoderPath(appPath);
}
} }
LogPaths(); LogPaths();
} }
private void SetPathsFromDirectory(string path) private Tuple<string, string> GetEncoderPaths(string configuredPath)
{
var appPath = configuredPath;
if (!string.IsNullOrWhiteSpace(appPath))
{
if (Directory.Exists(appPath))
{
return GetPathsFromDirectory(appPath);
}
if (File.Exists(appPath))
{
return new Tuple<string, string>(appPath, GetProbePathFromEncoderPath(appPath));
}
}
return new Tuple<string, string>(null, null);
}
private Tuple<string,string> GetPathsFromDirectory(string path)
{ {
// Since we can't predict the file extension, first try directly within the folder // Since we can't predict the file extension, first try directly within the folder
// If that doesn't pan out, then do a recursive search // If that doesn't pan out, then do a recursive search
var files = Directory.GetFiles(path); var files = Directory.GetFiles(path);
FFMpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase)); var ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase));
FFProbePath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase)); var ffprobePath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase));
if (string.IsNullOrWhiteSpace(FFMpegPath) || !File.Exists(FFMpegPath)) if (string.IsNullOrWhiteSpace(ffmpegPath) || !File.Exists(ffmpegPath))
{ {
files = Directory.GetFiles(path, "*", SearchOption.AllDirectories); files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
FFMpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase)); ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase));
SetProbePathFromEncoderPath(FFMpegPath); ffprobePath = GetProbePathFromEncoderPath(ffmpegPath);
}
} }
private void SetProbePathFromEncoderPath(string appPath) return new Tuple<string, string>(ffmpegPath, ffprobePath);
}
private string GetProbePathFromEncoderPath(string appPath)
{ {
FFProbePath = Directory.GetFiles(Path.GetDirectoryName(appPath)) return Directory.GetFiles(Path.GetDirectoryName(appPath))
.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase)); .FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase));
} }

View File

@ -1136,6 +1136,9 @@
<Compile Include="..\MediaBrowser.Model\Sync\SyncTarget.cs"> <Compile Include="..\MediaBrowser.Model\Sync\SyncTarget.cs">
<Link>Sync\SyncTarget.cs</Link> <Link>Sync\SyncTarget.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\System\Architecture.cs">
<Link>System\Architecture.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\System\LogFile.cs"> <Compile Include="..\MediaBrowser.Model\System\LogFile.cs">
<Link>System\LogFile.cs</Link> <Link>System\LogFile.cs</Link>
</Compile> </Compile>

View File

@ -1101,6 +1101,9 @@
<Compile Include="..\MediaBrowser.Model\Sync\SyncTarget.cs"> <Compile Include="..\MediaBrowser.Model\Sync\SyncTarget.cs">
<Link>Sync\SyncTarget.cs</Link> <Link>Sync\SyncTarget.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\System\Architecture.cs">
<Link>System\Architecture.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\System\LogFile.cs"> <Compile Include="..\MediaBrowser.Model\System\LogFile.cs">
<Link>System\LogFile.cs</Link> <Link>System\LogFile.cs</Link>
</Compile> </Compile>

View File

@ -395,6 +395,7 @@
<Compile Include="Sync\SyncProfileOption.cs" /> <Compile Include="Sync\SyncProfileOption.cs" />
<Compile Include="Sync\SyncQualityOption.cs" /> <Compile Include="Sync\SyncQualityOption.cs" />
<Compile Include="Sync\SyncTarget.cs" /> <Compile Include="Sync\SyncTarget.cs" />
<Compile Include="System\Architecture.cs" />
<Compile Include="System\LogFile.cs" /> <Compile Include="System\LogFile.cs" />
<Compile Include="System\PublicSystemInfo.cs" /> <Compile Include="System\PublicSystemInfo.cs" />
<Compile Include="Updates\CheckForUpdateResult.cs" /> <Compile Include="Updates\CheckForUpdateResult.cs" />

View File

@ -0,0 +1,9 @@
namespace MediaBrowser.Model.System
{
public enum Architecture
{
X86 = 0,
X64 = 1,
Arm = 2
}
}

View File

@ -154,6 +154,8 @@ namespace MediaBrowser.Model.System
public bool HasExternalEncoder { get; set; } public bool HasExternalEncoder { get; set; }
public Architecture SystemArchitecture { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SystemInfo" /> class. /// Initializes a new instance of the <see cref="SystemInfo" /> class.
/// </summary> /// </summary>

View File

@ -54,6 +54,11 @@ namespace MediaBrowser.Providers.Movies
var name = idInfo.Name; var name = idInfo.Name;
var year = idInfo.Year; var year = idInfo.Year;
if (string.IsNullOrWhiteSpace(name))
{
return new List<RemoteSearchResult>();
}
var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original"; var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
@ -123,19 +128,23 @@ namespace MediaBrowser.Providers.Movies
}); });
} }
private async Task<List<RemoteSearchResult>> GetSearchResults(string name, string type, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) private Task<List<RemoteSearchResult>> GetSearchResults(string name, string type, int? year, string language, string baseImageUrl, CancellationToken cancellationToken)
{ {
switch (type) switch (type)
{ {
case "tv": case "tv":
return await GetSearchResultsTv(name, year, language, baseImageUrl, cancellationToken); return GetSearchResultsTv(name, year, language, baseImageUrl, cancellationToken);
default: default:
return await GetSearchResultsGeneric(name, type, year, language, baseImageUrl, cancellationToken); return GetSearchResultsGeneric(name, type, year, language, baseImageUrl, cancellationToken);
} }
} }
private async Task<List<RemoteSearchResult>> GetSearchResultsGeneric(string name, string type, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) private async Task<List<RemoteSearchResult>> GetSearchResultsGeneric(string name, string type, int? year, string language, string baseImageUrl, CancellationToken cancellationToken)
{ {
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("name");
}
var url3 = string.Format(Search3, WebUtility.UrlEncode(name), ApiKey, language, type); var url3 = string.Format(Search3, WebUtility.UrlEncode(name), ApiKey, language, type);
@ -189,6 +198,11 @@ namespace MediaBrowser.Providers.Movies
private async Task<List<RemoteSearchResult>> GetSearchResultsTv(string name, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) private async Task<List<RemoteSearchResult>> GetSearchResultsTv(string name, int? year, string language, string baseImageUrl, CancellationToken cancellationToken)
{ {
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("name");
}
var url3 = string.Format(Search3, WebUtility.UrlEncode(name), ApiKey, language, "tv"); var url3 = string.Format(Search3, WebUtility.UrlEncode(name), ApiKey, language, "tv");
using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions

View File

@ -339,7 +339,14 @@ namespace MediaBrowser.Providers.Music
{ {
var urls = await RefreshMzbUrls().ConfigureAwait(false); var urls = await RefreshMzbUrls().ConfigureAwait(false);
_chosenUrl = urls[new Random().Next(0, urls.Count - 1)]; if (urls.Count > 1)
{
_chosenUrl = urls[new Random().Next(0, urls.Count)];
}
else
{
_chosenUrl = urls[0];
}
} }
return _chosenUrl; return _chosenUrl;
@ -361,6 +368,7 @@ namespace MediaBrowser.Providers.Music
{ {
list = _json.DeserializeFromStream<List<MbzUrl>>(stream); list = _json.DeserializeFromStream<List<MbzUrl>>(stream);
} }
_lastMbzUrlQueryTicks = DateTime.UtcNow.Ticks;
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -93,7 +93,7 @@ namespace MediaBrowser.Providers.Music
{ {
if (string.Equals(child.Name, "name", StringComparison.OrdinalIgnoreCase)) if (string.Equals(child.Name, "name", StringComparison.OrdinalIgnoreCase))
{ {
name = node.InnerText; name = child.InnerText;
break; break;
} }
} }

View File

@ -6,6 +6,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -114,6 +115,106 @@ namespace MediaBrowser.Providers.Omdb
ParseAdditionalMetadata(item, result); ParseAdditionalMetadata(item, result);
} }
public async Task<bool> FetchEpisodeData(BaseItem item, int episodeNumber, int seasonNumber, string imdbId, string language, string country, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(imdbId))
{
throw new ArgumentNullException("imdbId");
}
var seasonResult = await GetSeasonRootObject(imdbId, seasonNumber, cancellationToken);
RootObject result = null;
foreach (var episode in seasonResult.Episodes)
{
if (episode.Episode == episodeNumber)
{
result = episode;
break;
}
}
if (result == null)
{
return false;
}
// Only take the name and rating if the user's language is set to english, since Omdb has no localization
if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
{
item.Name = result.Title;
if (string.Equals(country, "us", StringComparison.OrdinalIgnoreCase))
{
item.OfficialRating = result.Rated;
}
}
int year;
if (!string.IsNullOrEmpty(result.Year)
&& int.TryParse(result.Year, NumberStyles.Number, _usCulture, out year)
&& year >= 0)
{
item.ProductionYear = year;
}
var hasCriticRating = item as IHasCriticRating;
if (hasCriticRating != null)
{
// Seeing some bogus RT data on omdb for series, so filter it out here
// RT doesn't even have tv series
int tomatoMeter;
if (!string.IsNullOrEmpty(result.tomatoMeter)
&& int.TryParse(result.tomatoMeter, NumberStyles.Integer, _usCulture, out tomatoMeter)
&& tomatoMeter >= 0)
{
hasCriticRating.CriticRating = tomatoMeter;
}
if (!string.IsNullOrEmpty(result.tomatoConsensus)
&& !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase))
{
hasCriticRating.CriticRatingSummary = WebUtility.HtmlDecode(result.tomatoConsensus);
}
}
int voteCount;
if (!string.IsNullOrEmpty(result.imdbVotes)
&& int.TryParse(result.imdbVotes, NumberStyles.Number, _usCulture, out voteCount)
&& voteCount >= 0)
{
item.VoteCount = voteCount;
}
float imdbRating;
if (!string.IsNullOrEmpty(result.imdbRating)
&& float.TryParse(result.imdbRating, NumberStyles.Any, _usCulture, out imdbRating)
&& imdbRating >= 0)
{
item.CommunityRating = imdbRating;
}
if (!string.IsNullOrEmpty(result.Website))
{
item.HomePageUrl = result.Website;
}
if (!string.IsNullOrWhiteSpace(result.imdbID))
{
item.SetProviderId(MetadataProviders.Imdb, result.imdbID);
}
ParseAdditionalMetadata(item, result);
return true;
}
internal async Task<RootObject> GetRootObject(string imdbId, CancellationToken cancellationToken) internal async Task<RootObject> GetRootObject(string imdbId, CancellationToken cancellationToken)
{ {
var path = await EnsureItemInfo(imdbId, cancellationToken); var path = await EnsureItemInfo(imdbId, cancellationToken);
@ -133,6 +234,40 @@ namespace MediaBrowser.Providers.Omdb
return result; return result;
} }
internal async Task<SeasonRootObject> GetSeasonRootObject(string imdbId, int seasonId, CancellationToken cancellationToken)
{
var path = await EnsureSeasonInfo(imdbId, seasonId, cancellationToken);
string resultString;
using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072))
{
using (var reader = new StreamReader(stream, new UTF8Encoding(false)))
{
resultString = reader.ReadToEnd();
resultString = resultString.Replace("\"N/A\"", "\"\"");
}
}
var result = _jsonSerializer.DeserializeFromString<SeasonRootObject>(resultString);
return result;
}
internal static bool IsValidSeries(Dictionary<string, string> seriesProviderIds)
{
string id;
if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id) && !string.IsNullOrEmpty(id))
{
// This check should ideally never be necessary but we're seeing some cases of this and haven't tracked them down yet.
if (!string.IsNullOrWhiteSpace(id))
{
return true;
}
}
return false;
}
private async Task<string> EnsureItemInfo(string imdbId, CancellationToken cancellationToken) private async Task<string> EnsureItemInfo(string imdbId, CancellationToken cancellationToken)
{ {
if (string.IsNullOrWhiteSpace(imdbId)) if (string.IsNullOrWhiteSpace(imdbId))
@ -173,6 +308,46 @@ namespace MediaBrowser.Providers.Omdb
return path; return path;
} }
private async Task<string> EnsureSeasonInfo(string seriesImdbId, int seasonId, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(seriesImdbId))
{
throw new ArgumentNullException("imdbId");
}
var imdbParam = seriesImdbId.StartsWith("tt", StringComparison.OrdinalIgnoreCase) ? seriesImdbId : "tt" + seriesImdbId;
var path = GetSeasonFilePath(imdbParam, seasonId);
var fileInfo = _fileSystem.GetFileSystemInfo(path);
if (fileInfo.Exists)
{
// If it's recent or automatic updates are enabled, don't re-download
if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
{
return path;
}
}
var url = string.Format("https://www.omdbapi.com/?i={0}&season={1}&detail=full", imdbParam, seasonId);
using (var stream = await _httpClient.Get(new HttpRequestOptions
{
Url = url,
ResourcePool = ResourcePool,
CancellationToken = cancellationToken
}).ConfigureAwait(false))
{
var rootObject = _jsonSerializer.DeserializeFromStream<SeasonRootObject>(stream);
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
_jsonSerializer.SerializeToFile(rootObject, path);
}
return path;
}
internal string GetDataFilePath(string imdbId) internal string GetDataFilePath(string imdbId)
{ {
if (string.IsNullOrEmpty(imdbId)) if (string.IsNullOrEmpty(imdbId))
@ -187,6 +362,20 @@ namespace MediaBrowser.Providers.Omdb
return Path.Combine(dataPath, filename); return Path.Combine(dataPath, filename);
} }
internal string GetSeasonFilePath(string imdbId, int seasonId)
{
if (string.IsNullOrEmpty(imdbId))
{
throw new ArgumentNullException("imdbId");
}
var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb");
var filename = string.Format("{0}_season_{1}.json", imdbId, seasonId);
return Path.Combine(dataPath, filename);
}
private void ParseAdditionalMetadata(BaseItem item, RootObject result) private void ParseAdditionalMetadata(BaseItem item, RootObject result)
{ {
// Grab series genres because imdb data is better than tvdb. Leave movies alone // Grab series genres because imdb data is better than tvdb. Leave movies alone
@ -238,12 +427,23 @@ namespace MediaBrowser.Providers.Omdb
return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase); return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase);
} }
internal class SeasonRootObject
{
public string Title { get; set; }
public string seriesID { get; set; }
public int Season { get; set; }
public int? totalSeasons { get; set; }
public RootObject[] Episodes { get; set; }
public string Response { get; set; }
}
internal class RootObject internal class RootObject
{ {
public string Title { get; set; } public string Title { get; set; }
public string Year { get; set; } public string Year { get; set; }
public string Rated { get; set; } public string Rated { get; set; }
public string Released { get; set; } public string Released { get; set; }
public int Episode { get; set; }
public string Runtime { get; set; } public string Runtime { get; set; }
public string Genre { get; set; } public string Genre { get; set; }
public string Director { get; set; } public string Director { get; set; }

View File

@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.TV
public async Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken) public async Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken)
{ {
var result = new MetadataResult<Episode> var result = new MetadataResult<Episode>()
{ {
Item = new Episode() Item = new Episode()
}; };
@ -53,30 +53,16 @@ namespace MediaBrowser.Providers.TV
return result; return result;
} }
var imdbId = info.GetProviderId(MetadataProviders.Imdb); if (OmdbProvider.IsValidSeries(info.SeriesProviderIds) && info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue)
if (string.IsNullOrWhiteSpace(imdbId))
{ {
imdbId = await GetEpisodeImdbId(info, cancellationToken).ConfigureAwait(false); var seriesImdbId = info.SeriesProviderIds[MetadataProviders.Imdb.ToString()];
}
if (!string.IsNullOrEmpty(imdbId)) result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).FetchEpisodeData(result.Item, info.IndexNumber.Value, info.ParentIndexNumber.Value, seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
{
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
result.HasMetadata = true;
await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
} }
return result; return result;
} }
private async Task<string> GetEpisodeImdbId(EpisodeInfo info, CancellationToken cancellationToken)
{
var results = await GetSearchResults(info, cancellationToken).ConfigureAwait(false);
var first = results.FirstOrDefault();
return first == null ? null : first.GetProviderId(MetadataProviders.Imdb);
}
public int Order public int Order
{ {
get get

View File

@ -104,6 +104,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
{ {
info.DeviceId = tokenInfo.DeviceId; info.DeviceId = tokenInfo.DeviceId;
} }
if (string.IsNullOrWhiteSpace(info.Version))
{
info.Version = tokenInfo.AppVersion;
}
} }
else else
{ {

View File

@ -1011,7 +1011,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
Action onStarted = () => Action onStarted = () =>
{ {
timer.Status = RecordingStatus.InProgress; timer.Status = RecordingStatus.InProgress;
_timerProvider.AddOrUpdate(timer); _timerProvider.AddOrUpdate(timer, false);
result.Item3.Release(); result.Item3.Release();
isResourceOpen = false; isResourceOpen = false;
@ -1060,7 +1060,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
if (recordingStatus == RecordingStatus.Completed) if (recordingStatus == RecordingStatus.Completed)
{ {
timer.Status = RecordingStatus.Completed; timer.Status = RecordingStatus.Completed;
_timerProvider.AddOrUpdate(timer); _timerProvider.AddOrUpdate(timer, false);
OnSuccessfulRecording(info.IsSeries, recordPath); OnSuccessfulRecording(info.IsSeries, recordPath);
_timerProvider.Delete(timer); _timerProvider.Delete(timer);

View File

@ -72,6 +72,26 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
} }
} }
public void AddOrUpdate(TimerInfo item, bool resetTimer)
{
if (resetTimer)
{
AddOrUpdate(item);
return;
}
var list = GetAll().ToList();
if (!list.Any(i => EqualityComparer(i, item)))
{
base.Add(item);
}
else
{
base.Update(item);
}
}
public override void Add(TimerInfo item) public override void Add(TimerInfo item)
{ {
if (string.IsNullOrWhiteSpace(item.Id)) if (string.IsNullOrWhiteSpace(item.Id))

View File

@ -127,7 +127,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
connection.RunQueries(new[] connection.RunQueries(new[]
{ {
"pragma temp_store = memory" "pragma temp_store = memory",
"PRAGMA main.locking_mode=EXCLUSIVE"
}, Logger); }, Logger);

View File

@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using MediaBrowser.Controller.Power; using MediaBrowser.Controller.Power;
using MediaBrowser.Model.System;
using MediaBrowser.Server.Implementations.Persistence; using MediaBrowser.Server.Implementations.Persistence;
using MediaBrowser.Server.Startup.Common.FFMpeg; using MediaBrowser.Server.Startup.Common.FFMpeg;
using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem; using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem;
@ -176,7 +177,7 @@ namespace MediaBrowser.Server.Mono.Native
} }
else if (string.Equals(uname.machine, "x86_64", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(uname.machine, "x86_64", StringComparison.OrdinalIgnoreCase))
{ {
info.SystemArchitecture = Architecture.X86_X64; info.SystemArchitecture = Architecture.X64;
} }
else if (uname.machine.StartsWith("arm", StringComparison.OrdinalIgnoreCase)) else if (uname.machine.StartsWith("arm", StringComparison.OrdinalIgnoreCase))
{ {
@ -260,7 +261,7 @@ namespace MediaBrowser.Server.Mono.Native
switch (environment.SystemArchitecture) switch (environment.SystemArchitecture)
{ {
case Architecture.X86_X64: case Architecture.X64:
info.Version = "20160124"; info.Version = "20160124";
break; break;
case Architecture.X86: case Architecture.X86:
@ -283,7 +284,7 @@ namespace MediaBrowser.Server.Mono.Native
switch (environment.SystemArchitecture) switch (environment.SystemArchitecture)
{ {
case Architecture.X86_X64: case Architecture.X64:
return new[] return new[]
{ {
"https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.8.5.7z" "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.8.5.7z"
@ -300,7 +301,7 @@ namespace MediaBrowser.Server.Mono.Native
switch (environment.SystemArchitecture) switch (environment.SystemArchitecture)
{ {
case Architecture.X86_X64: case Architecture.X64:
return new[] return new[]
{ {
"https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z" "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z"

View File

@ -647,15 +647,20 @@ namespace MediaBrowser.Server.Startup.Common
/// <returns>Task.</returns> /// <returns>Task.</returns>
private async Task RegisterMediaEncoder(IProgress<double> progress) private async Task RegisterMediaEncoder(IProgress<double> progress)
{ {
string encoderPath = null;
string probePath = null;
var info = await new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, NativeApp.Environment, NativeApp.GetFfmpegInstallInfo()) var info = await new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, NativeApp.Environment, NativeApp.GetFfmpegInstallInfo())
.GetFFMpegInfo(NativeApp.Environment, _startupOptions, progress).ConfigureAwait(false); .GetFFMpegInfo(NativeApp.Environment, _startupOptions, progress).ConfigureAwait(false);
_hasExternalEncoder = string.Equals(info.Version, "custom", StringComparison.OrdinalIgnoreCase); encoderPath = info.EncoderPath;
probePath = info.ProbePath;
_hasExternalEncoder = string.Equals(info.Version, "external", StringComparison.OrdinalIgnoreCase);
var mediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), var mediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"),
JsonSerializer, JsonSerializer,
info.EncoderPath, encoderPath,
info.ProbePath, probePath,
_hasExternalEncoder, _hasExternalEncoder,
ServerConfigurationManager, ServerConfigurationManager,
FileSystemManager, FileSystemManager,
@ -1145,7 +1150,8 @@ namespace MediaBrowser.Server.Startup.Common
ServerName = FriendlyName, ServerName = FriendlyName,
LocalAddress = localAddress, LocalAddress = localAddress,
SupportsLibraryMonitor = SupportsLibraryMonitor, SupportsLibraryMonitor = SupportsLibraryMonitor,
HasExternalEncoder = _hasExternalEncoder HasExternalEncoder = _hasExternalEncoder,
SystemArchitecture = NativeApp.Environment.SystemArchitecture
}; };
} }

View File

@ -53,13 +53,17 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
{ {
ProbePath = customffProbePath, ProbePath = customffProbePath,
EncoderPath = customffMpegPath, EncoderPath = customffMpegPath,
Version = "custom" Version = "external"
}; };
} }
var downloadInfo = _ffmpegInstallInfo; var downloadInfo = _ffmpegInstallInfo;
var version = downloadInfo.Version; var version = downloadInfo.Version;
if (string.Equals(version, "0", StringComparison.OrdinalIgnoreCase))
{
return new FFMpegInfo();
}
if (string.Equals(version, "path", StringComparison.OrdinalIgnoreCase)) if (string.Equals(version, "path", StringComparison.OrdinalIgnoreCase))
{ {
@ -175,18 +179,6 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
return null; return null;
} }
private async void DownloadFFMpegInBackground(FFMpegInstallInfo downloadinfo, string directory)
{
try
{
await DownloadFFMpeg(downloadinfo, directory, new Progress<double>()).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error downloading ffmpeg", ex);
}
}
private async Task DownloadFFMpeg(FFMpegInstallInfo downloadinfo, string directory, IProgress<double> progress) private async Task DownloadFFMpeg(FFMpegInstallInfo downloadinfo, string directory, IProgress<double> progress)
{ {
foreach (var url in downloadinfo.DownloadUrls) foreach (var url in downloadinfo.DownloadUrls)

View File

@ -1,4 +1,5 @@
 using MediaBrowser.Model.System;
namespace MediaBrowser.Server.Startup.Common namespace MediaBrowser.Server.Startup.Common
{ {
public class NativeEnvironment public class NativeEnvironment
@ -15,11 +16,4 @@ namespace MediaBrowser.Server.Startup.Common
Bsd = 2, Bsd = 2,
Linux = 3 Linux = 3
} }
public enum Architecture
{
X86 = 0,
X86_X64 = 1,
Arm = 2
}
} }

View File

@ -10,6 +10,7 @@ using System.Reflection;
using System.Windows.Forms; using System.Windows.Forms;
using CommonIO; using CommonIO;
using MediaBrowser.Controller.Power; using MediaBrowser.Controller.Power;
using MediaBrowser.Model.System;
using MediaBrowser.Server.Implementations.Persistence; using MediaBrowser.Server.Implementations.Persistence;
using MediaBrowser.Server.Startup.Common.FFMpeg; using MediaBrowser.Server.Startup.Common.FFMpeg;
using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem; using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem;
@ -53,7 +54,7 @@ namespace MediaBrowser.ServerApplication.Native
return new NativeEnvironment return new NativeEnvironment
{ {
OperatingSystem = OperatingSystem.Windows, OperatingSystem = OperatingSystem.Windows,
SystemArchitecture = System.Environment.Is64BitOperatingSystem ? Architecture.X86_X64 : Architecture.X86, SystemArchitecture = System.Environment.Is64BitOperatingSystem ? Architecture.X64 : Architecture.X86,
OperatingSystemVersionString = System.Environment.OSVersion.VersionString OperatingSystemVersionString = System.Environment.OSVersion.VersionString
}; };
} }
@ -158,9 +159,7 @@ namespace MediaBrowser.ServerApplication.Native
info.FFMpegFilename = "ffmpeg.exe"; info.FFMpegFilename = "ffmpeg.exe";
info.FFProbeFilename = "ffprobe.exe"; info.FFProbeFilename = "ffprobe.exe";
info.Version = "20160410"; info.Version = "0";
info.ArchiveType = "7z";
info.DownloadUrls = GetDownloadUrls();
return info; return info;
} }
@ -205,25 +204,5 @@ namespace MediaBrowser.ServerApplication.Native
{ {
((Process)sender).Dispose(); ((Process)sender).Dispose();
} }
private string[] GetDownloadUrls()
{
switch (Environment.SystemArchitecture)
{
case Architecture.X86_X64:
return new[]
{
"https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160410-win64.7z",
"https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-20160409-git-0c90b2e-win64-static.7z"
};
case Architecture.X86:
return new[]
{
"https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160410-win32.7z",
"https://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20160409-git-0c90b2e-win32-static.7z"
};
}
return new string[] { };
}
} }
} }

View File

@ -374,6 +374,12 @@
<Content Include="dashboard-ui\scripts\tvlatest.js"> <Content Include="dashboard-ui\scripts\tvlatest.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\wizardcomponents.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\wizardcontroller.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\wizardlivetvguide.js"> <Content Include="dashboard-ui\scripts\wizardlivetvguide.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@ -1094,6 +1100,9 @@
<Content Include="dashboard-ui\wizardagreement.html"> <Content Include="dashboard-ui\wizardagreement.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\wizardcomponents.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\wizardlivetvguide.html"> <Content Include="dashboard-ui\wizardlivetvguide.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>