From 192e36275b3caeb16a579d443a1a9e6e7bb515ec Mon Sep 17 00:00:00 2001 From: softworkz Date: Thu, 23 Jun 2016 03:27:25 +0200 Subject: [PATCH 1/6] OMDB Episode Provider: Use new full season API for retrieval and caching --- MediaBrowser.Providers/Omdb/OmdbProvider.cs | 200 ++++++++++++++++++ .../TV/Omdb/OmdbEpisodeProvider.cs | 22 +- 2 files changed, 204 insertions(+), 18 deletions(-) diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index 4056073f2c..397107c744 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -6,6 +6,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Serialization; using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; @@ -114,6 +115,106 @@ namespace MediaBrowser.Providers.Omdb ParseAdditionalMetadata(item, result); } + public async Task 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 GetRootObject(string imdbId, CancellationToken cancellationToken) { var path = await EnsureItemInfo(imdbId, cancellationToken); @@ -133,6 +234,40 @@ namespace MediaBrowser.Providers.Omdb return result; } + internal async Task 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(resultString); + return result; + } + + internal static bool IsValidSeries(Dictionary 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 EnsureItemInfo(string imdbId, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(imdbId)) @@ -173,6 +308,46 @@ namespace MediaBrowser.Providers.Omdb return path; } + private async Task 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(stream); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _jsonSerializer.SerializeToFile(rootObject, path); + } + + return path; + } + internal string GetDataFilePath(string imdbId) { if (string.IsNullOrEmpty(imdbId)) @@ -187,6 +362,20 @@ namespace MediaBrowser.Providers.Omdb 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) { // 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); } + 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 { public string Title { get; set; } public string Year { get; set; } public string Rated { get; set; } public string Released { get; set; } + public int Episode { get; set; } public string Runtime { get; set; } public string Genre { get; set; } public string Director { get; set; } diff --git a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs index ebbefbeb1a..21668f0148 100644 --- a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs @@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.TV public async Task> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken) { - var result = new MetadataResult + var result = new MetadataResult() { Item = new Episode() }; @@ -53,30 +53,16 @@ namespace MediaBrowser.Providers.TV return result; } - var imdbId = info.GetProviderId(MetadataProviders.Imdb); - if (string.IsNullOrWhiteSpace(imdbId)) + if (OmdbProvider.IsValidSeries(info.SeriesProviderIds) && info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue) { - imdbId = await GetEpisodeImdbId(info, cancellationToken).ConfigureAwait(false); - } + var seriesImdbId = info.SeriesProviderIds[MetadataProviders.Imdb.ToString()]; - if (!string.IsNullOrEmpty(imdbId)) - { - 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); + 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); } return result; } - private async Task 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 { get From e8e5a314c5d6dbccc2dcb9be3ea3c26b17e9bbb0 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 23 Jun 2016 01:25:30 -0400 Subject: [PATCH 2/6] update musicbrainz --- .../Music/MusicBrainzAlbumProvider.cs | 10 +++++++++- .../Music/MusicBrainzArtistProvider.cs | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs index 36676c6b20..1710ec2b0f 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs @@ -339,7 +339,14 @@ namespace MediaBrowser.Providers.Music { 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; @@ -361,6 +368,7 @@ namespace MediaBrowser.Providers.Music { list = _json.DeserializeFromStream>(stream); } + _lastMbzUrlQueryTicks = DateTime.UtcNow.Ticks; } catch (Exception ex) { diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs index f36062ebe3..88635bf06c 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs @@ -93,7 +93,7 @@ namespace MediaBrowser.Providers.Music { if (string.Equals(child.Name, "name", StringComparison.OrdinalIgnoreCase)) { - name = node.InnerText; + name = child.InnerText; break; } } From d569ef4fd7ea2abbc46d29fb69aa4566bc6139e9 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 23 Jun 2016 01:25:43 -0400 Subject: [PATCH 3/6] fix appVersion errors --- .../HttpServer/Security/AuthorizationContext.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs index 357f5c9769..bc3e7b163b 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs @@ -104,6 +104,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security { info.DeviceId = tokenInfo.DeviceId; } + if (string.IsNullOrWhiteSpace(info.Version)) + { + info.Version = tokenInfo.AppVersion; + } } else { From 70f75f8adbdec92c43d0fbbfe713d162b38dcdf4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 23 Jun 2016 01:26:34 -0400 Subject: [PATCH 4/6] fix recording timers --- .../LiveTv/EmbyTV/EmbyTV.cs | 6 +++--- .../LiveTv/EmbyTV/TimerManager.cs | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 29c1d1c9a1..2d2d18524a 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -461,7 +461,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV return CreateTimer(info, cancellationToken); } - public Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken) + public Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken) { return CreateSeriesTimer(info, cancellationToken); } @@ -1011,7 +1011,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV Action onStarted = () => { timer.Status = RecordingStatus.InProgress; - _timerProvider.AddOrUpdate(timer); + _timerProvider.AddOrUpdate(timer, false); result.Item3.Release(); isResourceOpen = false; @@ -1060,7 +1060,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV if (recordingStatus == RecordingStatus.Completed) { timer.Status = RecordingStatus.Completed; - _timerProvider.AddOrUpdate(timer); + _timerProvider.AddOrUpdate(timer, false); OnSuccessfulRecording(info.IsSeries, recordPath); _timerProvider.Delete(timer); diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs index bcad80447c..4233589068 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs @@ -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) { if (string.IsNullOrWhiteSpace(item.Id)) From 82e80857472b94ea73798276ef76829a86f5c2e5 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 23 Jun 2016 01:26:49 -0400 Subject: [PATCH 5/6] fix identify images --- MediaBrowser.Api/ItemLookupService.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs index 30cde48835..ad0ca68af3 100644 --- a/MediaBrowser.Api/ItemLookupService.cs +++ b/MediaBrowser.Api/ItemLookupService.cs @@ -181,11 +181,9 @@ namespace MediaBrowser.Api return ToOptimizedResult(result); } - public async Task Get(GetRemoteSearchImage request) + public Task Get(GetRemoteSearchImage request) { - var result = GetRemoteImage(request).ConfigureAwait(false); - - return result; + return GetRemoteImage(request); } public void Post(ApplySearchCriteria request) From cffc9417c7a25263a194615096685bd8ef1e37b2 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 23 Jun 2016 13:04:18 -0400 Subject: [PATCH 6/6] update startup wizard --- MediaBrowser.Api/ConfigurationService.cs | 20 ++++- MediaBrowser.Api/ItemLookupService.cs | 13 +++ MediaBrowser.Api/StartupWizardService.cs | 9 +- MediaBrowser.Api/System/SystemService.cs | 6 +- MediaBrowser.Api/UserLibrary/ItemsService.cs | 16 ++-- .../MediaEncoding/IMediaEncoder.cs | 2 + .../Encoder/MediaEncoder.cs | 84 ++++++++++++++----- .../MediaBrowser.Model.Portable.csproj | 3 + .../MediaBrowser.Model.net35.csproj | 3 + MediaBrowser.Model/MediaBrowser.Model.csproj | 1 + MediaBrowser.Model/System/Architecture.cs | 9 ++ MediaBrowser.Model/System/SystemInfo.cs | 2 + .../Movies/MovieDbSearch.cs | 24 ++++-- .../Persistence/SqliteItemRepository.cs | 3 +- .../Native/BaseMonoApp.cs | 9 +- .../ApplicationHost.cs | 14 +++- .../FFMpeg/FFMpegLoader.cs | 18 ++-- .../NativeEnvironment.cs | 10 +-- .../Native/WindowsApp.cs | 27 +----- .../MediaBrowser.WebDashboard.csproj | 9 ++ 20 files changed, 192 insertions(+), 90 deletions(-) create mode 100644 MediaBrowser.Model/System/Architecture.cs diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs index 446415fbb5..9c8120de7d 100644 --- a/MediaBrowser.Api/ConfigurationService.cs +++ b/MediaBrowser.Api/ConfigurationService.cs @@ -9,7 +9,9 @@ using ServiceStack.Web; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Controller.MediaEncoding; 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 { /// @@ -86,14 +96,22 @@ namespace MediaBrowser.Api private readonly IFileSystem _fileSystem; private readonly IProviderManager _providerManager; 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; _configurationManager = configurationManager; _fileSystem = fileSystem; _providerManager = providerManager; _libraryManager = libraryManager; + _mediaEncoder = mediaEncoder; + } + + public void Post(UpdateMediaEncoderPath request) + { + var task = _mediaEncoder.UpdateEncoderPath(request.Path); + Task.WaitAll(task); } /// diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs index ad0ca68af3..357ff43949 100644 --- a/MediaBrowser.Api/ItemLookupService.cs +++ b/MediaBrowser.Api/ItemLookupService.cs @@ -38,6 +38,12 @@ namespace MediaBrowser.Api { } + [Route("/Items/RemoteSearch/Trailer", "POST")] + [Authenticated] + public class GetTrailerRemoteSearchResults : RemoteSearchQuery, IReturn> + { + } + [Route("/Items/RemoteSearch/AdultVideo", "POST")] [Authenticated] public class GetAdultVideoRemoteSearchResults : RemoteSearchQuery, IReturn> @@ -132,6 +138,13 @@ namespace MediaBrowser.Api return ToOptimizedResult(infos); } + public async Task Post(GetTrailerRemoteSearchResults request) + { + var result = await _providerManager.GetRemoteSearchResults(request, CancellationToken.None).ConfigureAwait(false); + + return ToOptimizedResult(result); + } + public async Task Post(GetMovieRemoteSearchResults request) { var result = await _providerManager.GetRemoteSearchResults(request, CancellationToken.None).ConfigureAwait(false); diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index 93c9f8b9f5..6c58228ea7 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -11,6 +11,7 @@ using ServiceStack; using System; using System.Linq; using System.Threading.Tasks; +using MediaBrowser.Controller.MediaEncoding; namespace MediaBrowser.Api { @@ -52,14 +53,16 @@ namespace MediaBrowser.Api private readonly IUserManager _userManager; private readonly IConnectManager _connectManager; 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; _appHost = appHost; _userManager = userManager; _connectManager = connectManager; _liveTvManager = liveTvManager; + _mediaEncoder = mediaEncoder; } public void Post(ReportStartupWizardComplete request) @@ -75,7 +78,8 @@ namespace MediaBrowser.Api 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 bool SupportsRunningAsService { get; set; } + public bool HasMediaEncoder { get; set; } } public class StartupUser diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs index 346f6b32ab..c2318dccbc 100644 --- a/MediaBrowser.Api/System/SystemService.cs +++ b/MediaBrowser.Api/System/SystemService.cs @@ -19,7 +19,7 @@ namespace MediaBrowser.Api.System /// Class GetSystemInfo /// [Route("/System/Info", "GET", Summary = "Gets information about the server")] - [Authenticated(EscapeParentalControl = true)] + [Authenticated(EscapeParentalControl = true, AllowBeforeStartupWizard = true)] public class GetSystemInfo : IReturn { @@ -120,7 +120,7 @@ namespace MediaBrowser.Api.System try { - files = _fileSystem.GetFiles(_appPaths.LogDirectoryPath) + files = _fileSystem.GetFiles(_appPaths.LogDirectoryPath) .Where(i => string.Equals(i.Extension, ".txt", StringComparison.OrdinalIgnoreCase)) .ToList(); } @@ -146,7 +146,7 @@ namespace MediaBrowser.Api.System public Task Get(GetLogFile request) { - var file = _fileSystem.GetFiles(_appPaths.LogDirectoryPath) + var file = _fileSystem.GetFiles(_appPaths.LogDirectoryPath) .First(i => string.Equals(i.Name, request.Name, StringComparison.OrdinalIgnoreCase)); return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShare.ReadWrite); diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index f89cd0ef6e..51ca2d5cac 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -119,11 +119,17 @@ namespace MediaBrowser.Api.UserLibrary // Default list type = children + var folder = item as Folder; + if (folder == null) + { + folder = user == null ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder(); + } + if (!string.IsNullOrEmpty(request.Ids)) { request.Recursive = true; 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)) { @@ -138,22 +144,22 @@ namespace MediaBrowser.Api.UserLibrary 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) { - return await ((Folder)item).GetItems(GetItemsQuery(request, null)).ConfigureAwait(false); + return await folder.GetItems(GetItemsQuery(request, null)).ConfigureAwait(false); } var userRoot = item as UserRootFolder; if (userRoot == null) { - return await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); + return await folder.GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); } - IEnumerable items = ((Folder)item).GetChildren(user, true); + IEnumerable items = folder.GetChildren(user, true); var itemsArray = items.ToArray(); diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index c00f76f22c..77ba1685f5 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -130,5 +130,7 @@ namespace MediaBrowser.Controller.MediaEncoding string EscapeSubtitleFilterPath(string path); void Init(); + + Task UpdateEncoderPath(string path); } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 39a2338568..f8321f6cd1 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -23,6 +23,7 @@ using System.Threading.Tasks; using CommonIO; using MediaBrowser.Model.Configuration; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; 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() { if (_hasExternalEncoder) @@ -131,46 +161,60 @@ namespace MediaBrowser.MediaEncoding.Encoder { 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)) - { - SetPathsFromDirectory(appPath); - } - - else if (File.Exists(appPath)) - { - FFMpegPath = appPath; - - SetProbePathFromEncoderPath(appPath); - } + FFMpegPath = newPaths.Item1; + FFProbePath = newPaths.Item2; } LogPaths(); } - private void SetPathsFromDirectory(string path) + private Tuple GetEncoderPaths(string configuredPath) + { + var appPath = configuredPath; + + if (!string.IsNullOrWhiteSpace(appPath)) + { + if (Directory.Exists(appPath)) + { + return GetPathsFromDirectory(appPath); + } + + if (File.Exists(appPath)) + { + return new Tuple(appPath, GetProbePathFromEncoderPath(appPath)); + } + } + + return new Tuple(null, null); + } + + private Tuple GetPathsFromDirectory(string path) { // 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 var files = Directory.GetFiles(path); - 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 ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", 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); - FFMpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase)); - SetProbePathFromEncoderPath(FFMpegPath); + ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase)); + ffprobePath = GetProbePathFromEncoderPath(ffmpegPath); } + + return new Tuple(ffmpegPath, ffprobePath); } - private void SetProbePathFromEncoderPath(string appPath) + 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)); } diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index f9d28605ef..0de9fb519c 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -1136,6 +1136,9 @@ Sync\SyncTarget.cs + + System\Architecture.cs + System\LogFile.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index edaa0e0270..fe0b3bcae9 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -1101,6 +1101,9 @@ Sync\SyncTarget.cs + + System\Architecture.cs + System\LogFile.cs diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 7c9f132dbd..e54273b84c 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -395,6 +395,7 @@ + diff --git a/MediaBrowser.Model/System/Architecture.cs b/MediaBrowser.Model/System/Architecture.cs new file mode 100644 index 0000000000..09eedddc13 --- /dev/null +++ b/MediaBrowser.Model/System/Architecture.cs @@ -0,0 +1,9 @@ +namespace MediaBrowser.Model.System +{ + public enum Architecture + { + X86 = 0, + X64 = 1, + Arm = 2 + } +} diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index 73d5961f67..868d9dc282 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -154,6 +154,8 @@ namespace MediaBrowser.Model.System public bool HasExternalEncoder { get; set; } + public Architecture SystemArchitecture { get; set; } + /// /// Initializes a new instance of the class. /// diff --git a/MediaBrowser.Providers/Movies/MovieDbSearch.cs b/MediaBrowser.Providers/Movies/MovieDbSearch.cs index ceb41178e1..ab2cd3bedd 100644 --- a/MediaBrowser.Providers/Movies/MovieDbSearch.cs +++ b/MediaBrowser.Providers/Movies/MovieDbSearch.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Movies internal static string ApiKey = "f6bd687ffa63cd282b6ff2c6877f2669"; internal static string AcceptHeader = "application/json,image/*"; - + private readonly ILogger _logger; private readonly IJsonSerializer _json; private readonly ILibraryManager _libraryManager; @@ -54,6 +54,11 @@ namespace MediaBrowser.Providers.Movies var name = idInfo.Name; var year = idInfo.Year; + if (string.IsNullOrWhiteSpace(name)) + { + return new List(); + } + var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original"; @@ -73,7 +78,7 @@ namespace MediaBrowser.Providers.Movies //var searchType = item is BoxSet ? "collection" : "movie"; var results = await GetSearchResults(name, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false); - + if (results.Count == 0) { //try in english if wasn't before @@ -123,19 +128,23 @@ namespace MediaBrowser.Providers.Movies }); } - private async Task> GetSearchResults(string name, string type, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) + private Task> GetSearchResults(string name, string type, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) { switch (type) { case "tv": - return await GetSearchResultsTv(name, year, language, baseImageUrl, cancellationToken); + return GetSearchResultsTv(name, year, language, baseImageUrl, cancellationToken); default: - return await GetSearchResultsGeneric(name, type, year, language, baseImageUrl, cancellationToken); + return GetSearchResultsGeneric(name, type, year, language, baseImageUrl, cancellationToken); } } private async Task> 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); @@ -189,6 +198,11 @@ namespace MediaBrowser.Providers.Movies private async Task> 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"); using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 992a0a2cfa..784766071a 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -127,7 +127,8 @@ namespace MediaBrowser.Server.Implementations.Persistence connection.RunQueries(new[] { - "pragma temp_store = memory" + "pragma temp_store = memory", + "PRAGMA main.locking_mode=EXCLUSIVE" }, Logger); diff --git a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs index a012a19a3d..5d7274356e 100644 --- a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs +++ b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Reflection; using System.Text.RegularExpressions; using MediaBrowser.Controller.Power; +using MediaBrowser.Model.System; using MediaBrowser.Server.Implementations.Persistence; using MediaBrowser.Server.Startup.Common.FFMpeg; 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)) { - info.SystemArchitecture = Architecture.X86_X64; + info.SystemArchitecture = Architecture.X64; } else if (uname.machine.StartsWith("arm", StringComparison.OrdinalIgnoreCase)) { @@ -260,7 +261,7 @@ namespace MediaBrowser.Server.Mono.Native switch (environment.SystemArchitecture) { - case Architecture.X86_X64: + case Architecture.X64: info.Version = "20160124"; break; case Architecture.X86: @@ -283,7 +284,7 @@ namespace MediaBrowser.Server.Mono.Native switch (environment.SystemArchitecture) { - case Architecture.X86_X64: + case Architecture.X64: return new[] { "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) { - case Architecture.X86_X64: + case Architecture.X64: return new[] { "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z" diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index e7e6a9dc3a..7390fbbbc4 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -647,15 +647,20 @@ namespace MediaBrowser.Server.Startup.Common /// Task. private async Task RegisterMediaEncoder(IProgress progress) { + string encoderPath = null; + string probePath = null; + var info = await new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, NativeApp.Environment, NativeApp.GetFfmpegInstallInfo()) .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"), JsonSerializer, - info.EncoderPath, - info.ProbePath, + encoderPath, + probePath, _hasExternalEncoder, ServerConfigurationManager, FileSystemManager, @@ -1145,7 +1150,8 @@ namespace MediaBrowser.Server.Startup.Common ServerName = FriendlyName, LocalAddress = localAddress, SupportsLibraryMonitor = SupportsLibraryMonitor, - HasExternalEncoder = _hasExternalEncoder + HasExternalEncoder = _hasExternalEncoder, + SystemArchitecture = NativeApp.Environment.SystemArchitecture }; } diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs index 2c393ff29b..a4c50d0d8f 100644 --- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs +++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs @@ -53,13 +53,17 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg { ProbePath = customffProbePath, EncoderPath = customffMpegPath, - Version = "custom" + Version = "external" }; } var downloadInfo = _ffmpegInstallInfo; var version = downloadInfo.Version; + if (string.Equals(version, "0", StringComparison.OrdinalIgnoreCase)) + { + return new FFMpegInfo(); + } if (string.Equals(version, "path", StringComparison.OrdinalIgnoreCase)) { @@ -175,18 +179,6 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg return null; } - private async void DownloadFFMpegInBackground(FFMpegInstallInfo downloadinfo, string directory) - { - try - { - await DownloadFFMpeg(downloadinfo, directory, new Progress()).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error downloading ffmpeg", ex); - } - } - private async Task DownloadFFMpeg(FFMpegInstallInfo downloadinfo, string directory, IProgress progress) { foreach (var url in downloadinfo.DownloadUrls) diff --git a/MediaBrowser.Server.Startup.Common/NativeEnvironment.cs b/MediaBrowser.Server.Startup.Common/NativeEnvironment.cs index 5b45afe731..b30509982e 100644 --- a/MediaBrowser.Server.Startup.Common/NativeEnvironment.cs +++ b/MediaBrowser.Server.Startup.Common/NativeEnvironment.cs @@ -1,4 +1,5 @@ - +using MediaBrowser.Model.System; + namespace MediaBrowser.Server.Startup.Common { public class NativeEnvironment @@ -15,11 +16,4 @@ namespace MediaBrowser.Server.Startup.Common Bsd = 2, Linux = 3 } - - public enum Architecture - { - X86 = 0, - X86_X64 = 1, - Arm = 2 - } } diff --git a/MediaBrowser.ServerApplication/Native/WindowsApp.cs b/MediaBrowser.ServerApplication/Native/WindowsApp.cs index 2ea5064a4e..d8b2720c2c 100644 --- a/MediaBrowser.ServerApplication/Native/WindowsApp.cs +++ b/MediaBrowser.ServerApplication/Native/WindowsApp.cs @@ -10,6 +10,7 @@ using System.Reflection; using System.Windows.Forms; using CommonIO; using MediaBrowser.Controller.Power; +using MediaBrowser.Model.System; using MediaBrowser.Server.Implementations.Persistence; using MediaBrowser.Server.Startup.Common.FFMpeg; using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem; @@ -53,7 +54,7 @@ namespace MediaBrowser.ServerApplication.Native return new NativeEnvironment { 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 }; } @@ -158,9 +159,7 @@ namespace MediaBrowser.ServerApplication.Native info.FFMpegFilename = "ffmpeg.exe"; info.FFProbeFilename = "ffprobe.exe"; - info.Version = "20160410"; - info.ArchiveType = "7z"; - info.DownloadUrls = GetDownloadUrls(); + info.Version = "0"; return info; } @@ -205,25 +204,5 @@ namespace MediaBrowser.ServerApplication.Native { ((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[] { }; - } } } \ No newline at end of file diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index bc5db02241..4eae9975a1 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -374,6 +374,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -1094,6 +1100,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest