diff options
| author | Eric Reed <ebr@mediabrowser3.com> | 2013-05-18 18:18:41 -0400 |
|---|---|---|
| committer | Eric Reed <ebr@mediabrowser3.com> | 2013-05-18 18:18:41 -0400 |
| commit | 00449ff66dbbaba9901c47c65070868ca111ac45 (patch) | |
| tree | 14980d9711cbcc5d5d92a02031715d8f79b0eaf8 | |
| parent | 584641a6f43fee18f65dc9c8abd24b88742922ce (diff) | |
| parent | 407016a30702694e39505afbb599791ed7c2dcfe (diff) | |
Merge branch 'master' of https://github.com/MediaBrowser/MediaBrowser
35 files changed, 477 insertions, 341 deletions
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 3f5b9da2a..51608a899 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -361,7 +361,7 @@ namespace MediaBrowser.Api.Images index++; } - + index = 0; foreach (var image in item.ScreenshotImagePaths) @@ -422,7 +422,7 @@ namespace MediaBrowser.Api.Images return list; } - + /// <summary> /// Gets the specified request. /// </summary> @@ -765,7 +765,7 @@ namespace MediaBrowser.Api.Images } // Don't save locally if there's no parent (special feature, trailer, etc) - var saveLocally = (!(entity is Audio) && entity.Parent != null && !string.IsNullOrEmpty(entity.MetaLocation)) || entity is User; + var saveLocally = !(entity is Audio) && entity.Parent != null && !string.IsNullOrEmpty(entity.MetaLocation) || entity is User; if (imageType != ImageType.Primary) { @@ -775,6 +775,11 @@ namespace MediaBrowser.Api.Images } } + if (entity.LocationType != LocationType.FileSystem) + { + saveLocally = false; + } + var imagePath = _providerManager.GetSavePath(entity, filename + "." + extension, saveLocally); // Save to file system diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 2819a649a..fb06e35ea 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -56,6 +56,8 @@ <Reference Include="Microsoft.CSharp" /> <Reference Include="System.Data" /> <Reference Include="System.Drawing" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Net.Http.WebRequest" /> <Reference Include="System.XML" /> </ItemGroup> <ItemGroup> @@ -82,6 +84,7 @@ <Compile Include="Playback\Progressive\BaseProgressiveStreamingService.cs" /> <Compile Include="Playback\BaseStreamingService.cs" /> <Compile Include="Playback\Progressive\ProgressiveStreamWriter.cs" /> + <Compile Include="Playback\StaticRemoteStreamWriter.cs" /> <Compile Include="Playback\StreamRequest.cs" /> <Compile Include="Playback\StreamState.cs" /> <Compile Include="Playback\Progressive\VideoService.cs" /> diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 04b6a656d..19b339cd7 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -622,9 +622,26 @@ namespace MediaBrowser.Api.Playback /// <returns>System.String.</returns> protected string GetUserAgentParam(BaseItem item) { + var useragent = GetUserAgent(item); + + if (!string.IsNullOrEmpty(useragent)) + { + return "-user-agent \"" + useragent + "\""; + } + + return string.Empty; + } + + /// <summary> + /// Gets the user agent. + /// </summary> + /// <param name="item">The item.</param> + /// <returns>System.String.</returns> + protected string GetUserAgent(BaseItem item) + { if (item.Path.IndexOf("apple.com", StringComparison.OrdinalIgnoreCase) != -1) { - return "-user-agent \"QuickTime/7.6.2\""; + return "QuickTime/7.6.2"; } return string.Empty; diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 9bcce87c8..ab1fb4f90 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -1,4 +1,7 @@ -using MediaBrowser.Api.Images; +using System.Net; +using System.Net.Cache; +using System.Net.Http; +using MediaBrowser.Api.Images; using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; using MediaBrowser.Common.Net; @@ -188,6 +191,11 @@ namespace MediaBrowser.Api.Playback.Progressive var responseHeaders = new Dictionary<string, string>(); + if (request.Static && state.Item.LocationType == LocationType.Remote) + { + return GetStaticRemoteStreamResult(state.Item, responseHeaders, isHeadRequest).Result; + } + var outputPath = GetOutputFilePath(state); var outputPathExists = File.Exists(outputPath); @@ -210,6 +218,61 @@ namespace MediaBrowser.Api.Playback.Progressive } /// <summary> + /// Gets the static remote stream result. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="responseHeaders">The response headers.</param> + /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param> + /// <returns>Task{System.Object}.</returns> + private async Task<object> GetStaticRemoteStreamResult(BaseItem item, Dictionary<string, string> responseHeaders, bool isHeadRequest) + { + responseHeaders["Accept-Ranges"] = "none"; + + var httpClient = new HttpClient(new WebRequestHandler + { + CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache), + AutomaticDecompression = DecompressionMethods.None + }); + + using (var message = new HttpRequestMessage(HttpMethod.Get, item.Path)) + { + var useragent = GetUserAgent(item); + + if (!string.IsNullOrEmpty(useragent)) + { + message.Headers.Add("User-Agent", useragent); + } + + var response = await httpClient.SendAsync(message, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); + + response.EnsureSuccessStatusCode(); + + var contentType = response.Content.Headers.ContentType.MediaType; + + // Headers only + if (isHeadRequest) + { + response.Dispose(); + httpClient.Dispose(); + + return ResultFactory.GetResult(null, contentType, responseHeaders); + } + + var result = new StaticRemoteStreamWriter(response, httpClient); + + result.Options["Content-Type"] = contentType; + + // Add the response headers to the result object + foreach (var header in responseHeaders) + { + result.Options[header.Key] = header.Value; + } + + return result; + } + } + + /// <summary> /// Gets the album art response. /// </summary> /// <param name="state">The state.</param> diff --git a/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs b/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs new file mode 100644 index 000000000..89e13acb3 --- /dev/null +++ b/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs @@ -0,0 +1,75 @@ +using ServiceStack.Service; +using ServiceStack.ServiceHost; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; + +namespace MediaBrowser.Api.Playback +{ + /// <summary> + /// Class StaticRemoteStreamWriter + /// </summary> + public class StaticRemoteStreamWriter : IStreamWriter, IHasOptions + { + /// <summary> + /// The _input stream + /// </summary> + private readonly HttpResponseMessage _msg; + + private readonly HttpClient _client; + + /// <summary> + /// The _options + /// </summary> + private readonly IDictionary<string, string> _options = new Dictionary<string, string>(); + + /// <summary> + /// Initializes a new instance of the <see cref="StaticRemoteStreamWriter"/> class. + /// </summary> + public StaticRemoteStreamWriter(HttpResponseMessage msg, HttpClient client) + { + _msg = msg; + _client = client; + } + + /// <summary> + /// Gets the options. + /// </summary> + /// <value>The options.</value> + public IDictionary<string, string> Options + { + get { return _options; } + } + + /// <summary> + /// Writes to. + /// </summary> + /// <param name="responseStream">The response stream.</param> + public void WriteTo(Stream responseStream) + { + var task = WriteToAsync(responseStream); + + Task.WaitAll(task); + } + + /// <summary> + /// Writes to async. + /// </summary> + /// <param name="responseStream">The response stream.</param> + /// <returns>Task.</returns> + public async Task WriteToAsync(Stream responseStream) + { + using (_client) + { + using (_msg) + { + using (var input = await _msg.Content.ReadAsStreamAsync().ConfigureAwait(false)) + { + await input.CopyToAsync(responseStream).ConfigureAwait(false); + } + } + } + } + } +} diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index 22098a368..26b0aa192 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -135,7 +135,7 @@ namespace MediaBrowser.Api.UserLibrary { if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater)) { - items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.Name, StringComparison.OrdinalIgnoreCase) < 1); + items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.Name, StringComparison.CurrentCultureIgnoreCase) < 1); } var filters = request.GetFilters().ToList(); diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index c57778fd6..fbf41eb38 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -456,7 +456,7 @@ namespace MediaBrowser.Api.UserLibrary if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater)) { - items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.OrdinalIgnoreCase) < 1); + items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1); } // Filter by Series Status diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs index 34bdda94f..7451b6b97 100644 --- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs +++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs @@ -180,8 +180,6 @@ namespace MediaBrowser.Common.Implementations await RegisterResources().ConfigureAwait(false); FindParts(); - - await RunStartupTasks().ConfigureAwait(false); } protected virtual void OnLoggerLoaded() @@ -193,7 +191,7 @@ namespace MediaBrowser.Common.Implementations /// Runs the startup tasks. /// </summary> /// <returns>Task.</returns> - protected virtual Task RunStartupTasks() + public virtual Task RunStartupTasks() { return Task.Run(() => { diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs index 4c51d1299..7ea2d0adf 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -97,7 +97,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager }; client = new HttpClient(handler); - client.Timeout = TimeSpan.FromSeconds(30); + client.Timeout = TimeSpan.FromSeconds(15); _httpClients.TryAdd(host, client); } @@ -136,8 +136,8 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager { var now = DateTime.UtcNow; - var isCacheValid = (!cachedInfo.MustRevalidate && !string.IsNullOrEmpty(cachedInfo.Etag) && (now - cachedInfo.RequestDate).TotalDays < 7) - || (cachedInfo.Expires.HasValue && cachedInfo.Expires.Value > now); + var isCacheValid = cachedInfo.Expires.HasValue ? cachedInfo.Expires.Value > now : + !cachedInfo.MustRevalidate && !string.IsNullOrEmpty(cachedInfo.Etag) && (now - cachedInfo.RequestDate).TotalDays < 5; if (isCacheValid) { @@ -157,89 +157,90 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager options.CancellationToken.ThrowIfCancellationRequested(); - var message = GetHttpRequestMessage(options); - - if (options.EnableResponseCache && cachedInfo != null) + using (var message = GetHttpRequestMessage(options)) { - if (!string.IsNullOrEmpty(cachedInfo.Etag)) + if (options.EnableResponseCache && cachedInfo != null) { - message.Headers.Add("If-None-Match", cachedInfo.Etag); + if (!string.IsNullOrEmpty(cachedInfo.Etag)) + { + message.Headers.Add("If-None-Match", cachedInfo.Etag); + } + else if (cachedInfo.LastModified.HasValue) + { + message.Headers.IfModifiedSince = new DateTimeOffset(cachedInfo.LastModified.Value); + } } - else if (cachedInfo.LastModified.HasValue) + + if (options.ResourcePool != null) { - message.Headers.IfModifiedSince = new DateTimeOffset(cachedInfo.LastModified.Value); + await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); } - } - if (options.ResourcePool != null) - { - await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); - } - - _logger.Info("HttpClientManager.Get url: {0}", options.Url); + _logger.Info("HttpClientManager.Get url: {0}", options.Url); - try - { - options.CancellationToken.ThrowIfCancellationRequested(); + try + { + options.CancellationToken.ThrowIfCancellationRequested(); - var response = await GetHttpClient(GetHostFromUrl(options.Url)).SendAsync(message, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false); + var response = await GetHttpClient(GetHostFromUrl(options.Url)).SendAsync(message, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false); - if (options.EnableResponseCache) - { - if (response.StatusCode != HttpStatusCode.NotModified) + if (options.EnableResponseCache) { - EnsureSuccessStatusCode(response); - } + if (response.StatusCode != HttpStatusCode.NotModified) + { + EnsureSuccessStatusCode(response); + } - options.CancellationToken.ThrowIfCancellationRequested(); + options.CancellationToken.ThrowIfCancellationRequested(); - cachedInfo = UpdateInfoCache(cachedInfo, options.Url, cachedInfoPath, response); + cachedInfo = UpdateInfoCache(cachedInfo, options.Url, cachedInfoPath, response); - if (response.StatusCode == HttpStatusCode.NotModified) - { - _logger.Debug("Server indicates not modified for {0}. Returning cached result.", options.Url); - - return GetCachedResponse(cachedReponsePath); - } + if (response.StatusCode == HttpStatusCode.NotModified) + { + _logger.Debug("Server indicates not modified for {0}. Returning cached result.", options.Url); - if (!string.IsNullOrEmpty(cachedInfo.Etag) || cachedInfo.LastModified.HasValue || - (cachedInfo.Expires.HasValue && cachedInfo.Expires.Value > DateTime.UtcNow)) + return GetCachedResponse(cachedReponsePath); + } + + if (!string.IsNullOrEmpty(cachedInfo.Etag) || cachedInfo.LastModified.HasValue || + (cachedInfo.Expires.HasValue && cachedInfo.Expires.Value > DateTime.UtcNow)) + { + await UpdateResponseCache(response, cachedReponsePath).ConfigureAwait(false); + + return GetCachedResponse(cachedReponsePath); + } + } + else { - await UpdateResponseCache(response, cachedReponsePath).ConfigureAwait(false); + EnsureSuccessStatusCode(response); - return GetCachedResponse(cachedReponsePath); + options.CancellationToken.ThrowIfCancellationRequested(); } + + return await response.Content.ReadAsStreamAsync().ConfigureAwait(false); } - else + catch (OperationCanceledException ex) { - EnsureSuccessStatusCode(response); - - options.CancellationToken.ThrowIfCancellationRequested(); + throw GetCancellationException(options.Url, options.CancellationToken, ex); } + catch (HttpRequestException ex) + { + _logger.ErrorException("Error getting response from " + options.Url, ex); - return await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - } - catch (OperationCanceledException ex) - { - throw GetCancellationException(options.Url, options.CancellationToken, ex); - } - catch (HttpRequestException ex) - { - _logger.ErrorException("Error getting response from " + options.Url, ex); - - throw new HttpException(ex.Message, ex); - } - catch (Exception ex) - { - _logger.ErrorException("Error getting response from " + options.Url, ex); + throw new HttpException(ex.Message, ex); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting response from " + options.Url, ex); - throw; - } - finally - { - if (options.ResourcePool != null) + throw; + } + finally { - options.ResourcePool.Release(); + if (options.ResourcePool != null) + { + options.ResourcePool.Release(); + } } } } @@ -469,39 +470,42 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager { options.CancellationToken.ThrowIfCancellationRequested(); - using (var response = await GetHttpClient(GetHostFromUrl(options.Url)).SendAsync(GetHttpRequestMessage(options), HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false)) + using (var message = GetHttpRequestMessage(options)) { - EnsureSuccessStatusCode(response); + using (var response = await GetHttpClient(GetHostFromUrl(options.Url)).SendAsync(message, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false)) + { + EnsureSuccessStatusCode(response); - options.CancellationToken.ThrowIfCancellationRequested(); + options.CancellationToken.ThrowIfCancellationRequested(); - var contentLength = GetContentLength(response); + var contentLength = GetContentLength(response); - if (!contentLength.HasValue) - { - // We're not able to track progress - using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + if (!contentLength.HasValue) { - using (var fs = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + // We're not able to track progress + using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { - await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); + using (var fs = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + { + await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); + } } } - } - else - { - using (var stream = ProgressStream.CreateReadProgressStream(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), options.Progress.Report, contentLength.Value)) + else { - using (var fs = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + using (var stream = ProgressStream.CreateReadProgressStream(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), options.Progress.Report, contentLength.Value)) { - await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); + using (var fs = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + { + await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); + } } } - } - options.Progress.Report(100); + options.Progress.Report(100); - options.CancellationToken.ThrowIfCancellationRequested(); + options.CancellationToken.ThrowIfCancellationRequested(); + } } } catch (Exception ex) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index a0dba1aab..1bc6b523e 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -647,6 +647,11 @@ namespace MediaBrowser.Controller.Entities public long? RunTimeTicks { get; set; } /// <summary> + /// Gets or sets the original run time ticks. + /// </summary> + /// <value>The original run time ticks.</value> + public long? OriginalRunTimeTicks { get; set; } + /// <summary> /// Gets or sets the aspect ratio. /// </summary> /// <value>The aspect ratio.</value> @@ -900,7 +905,6 @@ namespace MediaBrowser.Controller.Entities if (changed || forceSave || themeSongsChanged || themeVideosChanged || localTrailersChanged) { cancellationToken.ThrowIfCancellationRequested(); - await LibraryManager.UpdateItem(this, cancellationToken).ConfigureAwait(false); } @@ -1501,7 +1505,7 @@ namespace MediaBrowser.Controller.Entities } // Refresh metadata - return RefreshMetadata(CancellationToken.None); + return RefreshMetadata(CancellationToken.None, forceSave: true); } } } diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 67692273d..70f3548f3 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Controller.Entities /// Specialized Folder class that points to a subset of the physical folders in the system. /// It is created from the user-specific folders within the system root /// </summary> - public class CollectionFolder : Folder, ICollectionFolder, IByReferenceItem + public class CollectionFolder : Folder, ICollectionFolder { /// <summary> /// Gets a value indicating whether this instance is virtual folder. diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 472cc115e..66a4ca215 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -760,7 +760,7 @@ namespace MediaBrowser.Controller.Entities var child = currentTuple.Item1; //refresh it - await child.RefreshMetadata(cancellationToken, resetResolveArgs: child.IsFolder).ConfigureAwait(false); + await child.RefreshMetadata(cancellationToken, resetResolveArgs: child.IsFolder, forceSave: currentTuple.Item2).ConfigureAwait(false); // Refresh children if a folder and the item changed or recursive is set to true var refreshChildren = child.IsFolder && (currentTuple.Item2 || (recursive.HasValue && recursive.Value)); diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index 65ec6899f..60a2c19a7 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -325,7 +325,7 @@ namespace MediaBrowser.Controller.Providers if (!string.IsNullOrWhiteSpace(val)) { - item.AddTrailerUrl(val); + //item.AddTrailerUrl(val); } break; } @@ -336,10 +336,10 @@ namespace MediaBrowser.Controller.Providers if (!string.IsNullOrWhiteSpace(val)) { - int ProductionYear; - if (int.TryParse(val, out ProductionYear) && ProductionYear > 1850) + int productionYear; + if (int.TryParse(val, out productionYear) && productionYear > 1850) { - item.ProductionYear = ProductionYear; + item.ProductionYear = productionYear; } } diff --git a/MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs b/MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs index 3e8501dfa..6e14682dd 100644 --- a/MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs +++ b/MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs @@ -91,12 +91,13 @@ namespace MediaBrowser.Controller.Providers } // Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below - var deletedKeys = item.Images.Keys.Where(image => + var deletedKeys = item.Images.ToList().Where(image => { - var path = item.Images[image]; + var path = image.Value; return IsInMetaLocation(item, path) && item.ResolveArgs.GetMetaFileByPath(path) == null; - }).ToList(); + + }).Select(i => i.Key).ToList(); // Now remove them from the dictionary foreach (var key in deletedKeys) diff --git a/MediaBrowser.Controller/Providers/ImagesByNameProvider.cs b/MediaBrowser.Controller/Providers/ImagesByNameProvider.cs index 64a988378..3611607c9 100644 --- a/MediaBrowser.Controller/Providers/ImagesByNameProvider.cs +++ b/MediaBrowser.Controller/Providers/ImagesByNameProvider.cs @@ -126,7 +126,8 @@ namespace MediaBrowser.Controller.Providers } data.Data = ConfigurationManager.ApplicationPaths.ItemsByNamePath.GetMD5(); - + SetLastRefreshed(item, DateTime.UtcNow); + return result; } diff --git a/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs index 307db023f..38d80883a 100644 --- a/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs @@ -973,8 +973,8 @@ namespace MediaBrowser.Controller.Providers.Movies boxset.OfficialRating = firstChild != null ? firstChild.OfficialRating : null; } - //if (movie.RunTimeTicks == null && movieData.runtime > 0) - // movie.RunTimeTicks = TimeSpan.FromMinutes(movieData.runtime).Ticks; + if (movieData.runtime > 0) + movie.OriginalRunTimeTicks = TimeSpan.FromMinutes(movieData.runtime).Ticks; //studios if (movieData.production_companies != null) diff --git a/MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs b/MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs index 2b4173ed9..b6852e9ba 100644 --- a/MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs +++ b/MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs @@ -46,11 +46,11 @@ namespace MediaBrowser.Controller.Providers.TV /// <summary> /// The episode query /// </summary> - private const string episodeQuery = "http://www.thetvdb.com/api/{0}/series/{1}/default/{2}/{3}/{4}.xml"; + private const string EpisodeQuery = "http://www.thetvdb.com/api/{0}/series/{1}/default/{2}/{3}/{4}.xml"; /// <summary> /// The abs episode query /// </summary> - private const string absEpisodeQuery = "http://www.thetvdb.com/api/{0}/series/{1}/absolute/{2}/{3}.xml"; + private const string AbsEpisodeQuery = "http://www.thetvdb.com/api/{0}/series/{1}/absolute/{2}/{3}.xml"; /// <summary> /// Supportses the specified item. @@ -179,25 +179,19 @@ namespace MediaBrowser.Controller.Providers.TV seasonNumber = "0"; // Specials } - var url = string.Format(episodeQuery, TVUtils.TvdbApiKey, seriesId, seasonNumber, episodeNumber, ConfigurationManager.Configuration.PreferredMetadataLanguage); + var url = string.Format(EpisodeQuery, TVUtils.TvdbApiKey, seriesId, seasonNumber, episodeNumber, ConfigurationManager.Configuration.PreferredMetadataLanguage); var doc = new XmlDocument(); - try + using (var result = await HttpClient.Get(new HttpRequestOptions { - using (var result = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = RemoteSeriesProvider.Current.TvDbResourcePool, - CancellationToken = cancellationToken, - EnableResponseCache = true + Url = url, + ResourcePool = RemoteSeriesProvider.Current.TvDbResourcePool, + CancellationToken = cancellationToken, + EnableResponseCache = true - }).ConfigureAwait(false)) - { - doc.Load(result); - } - } - catch (HttpException) + }).ConfigureAwait(false)) { + doc.Load(result); } //episode does not exist under this season, try absolute numbering. @@ -205,25 +199,19 @@ namespace MediaBrowser.Controller.Providers.TV //this is basicly just for anime. if (!doc.HasChildNodes && Int32.Parse(seasonNumber) == 1) { - url = string.Format(absEpisodeQuery, TVUtils.TvdbApiKey, seriesId, episodeNumber, ConfigurationManager.Configuration.PreferredMetadataLanguage); + url = string.Format(AbsEpisodeQuery, TVUtils.TvdbApiKey, seriesId, episodeNumber, ConfigurationManager.Configuration.PreferredMetadataLanguage); - try + using (var result = await HttpClient.Get(new HttpRequestOptions { - using (var result = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = RemoteSeriesProvider.Current.TvDbResourcePool, - CancellationToken = cancellationToken, - EnableResponseCache = true + Url = url, + ResourcePool = RemoteSeriesProvider.Current.TvDbResourcePool, + CancellationToken = cancellationToken, + EnableResponseCache = true - }).ConfigureAwait(false)) - { - if (result != null) doc.Load(result); - usingAbsoluteData = true; - } - } - catch (HttpException) + }).ConfigureAwait(false)) { + if (result != null) doc.Load(result); + usingAbsoluteData = true; } } diff --git a/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs b/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs index eb689ed2f..39fe4f2c6 100644 --- a/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs +++ b/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs @@ -1,5 +1,4 @@ -using System.Globalization; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -11,6 +10,7 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Net; using System.Text; @@ -142,8 +142,7 @@ namespace MediaBrowser.Controller.Providers.TV if (item.DontFetchMeta) return false; - return !HasLocalMeta(item) && (ConfigurationManager.Configuration.MetadataRefreshDays != -1 && - DateTime.UtcNow.Subtract(downloadDate).TotalDays > ConfigurationManager.Configuration.MetadataRefreshDays); + return !HasLocalMeta(item) && base.NeedsRefreshInternal(item, providerInfo); } /// <summary> @@ -164,16 +163,17 @@ namespace MediaBrowser.Controller.Providers.TV var seriesId = Path.GetFileName(path).GetAttributeValue("tvdbid") ?? await GetSeriesId(series, cancellationToken); cancellationToken.ThrowIfCancellationRequested(); - + + var status = ProviderRefreshStatus.Success; + if (!string.IsNullOrEmpty(seriesId)) { series.SetProviderId(MetadataProviders.Tvdb, seriesId); - if (!HasCompleteMetadata(series)) - { - await FetchSeriesData(series, seriesId, cancellationToken).ConfigureAwait(false); - } + + status = await FetchSeriesData(series, seriesId, cancellationToken).ConfigureAwait(false); } - SetLastRefreshed(item, DateTime.UtcNow); + + SetLastRefreshed(item, DateTime.UtcNow, status); return true; } Logger.Info("Series provider not fetching because local meta exists or requested to ignore: " + item.Name); @@ -188,11 +188,9 @@ namespace MediaBrowser.Controller.Providers.TV /// <param name="seriesId">The series id.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{System.Boolean}.</returns> - private async Task<bool> FetchSeriesData(Series series, string seriesId, CancellationToken cancellationToken) + private async Task<ProviderRefreshStatus> FetchSeriesData(Series series, string seriesId, CancellationToken cancellationToken) { - var success = false; - - var name = series.Name; + var status = ProviderRefreshStatus.Success; if (!string.IsNullOrEmpty(seriesId)) { @@ -200,22 +198,16 @@ namespace MediaBrowser.Controller.Providers.TV string url = string.Format(seriesGet, TVUtils.TvdbApiKey, seriesId, ConfigurationManager.Configuration.PreferredMetadataLanguage); var doc = new XmlDocument(); - try + using (var xml = await HttpClient.Get(new HttpRequestOptions { - using (var xml = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = TvDbResourcePool, - CancellationToken = cancellationToken, - EnableResponseCache = true + Url = url, + ResourcePool = TvDbResourcePool, + CancellationToken = cancellationToken, + EnableResponseCache = true - }).ConfigureAwait(false)) - { - doc.Load(xml); - } - } - catch (HttpException) + }).ConfigureAwait(false)) { + doc.Load(xml); } if (doc.HasChildNodes) @@ -224,8 +216,6 @@ namespace MediaBrowser.Controller.Providers.TV var actorTask = FetchActors(series, seriesId, doc, cancellationToken); var imageTask = FetchImages(series, seriesId, cancellationToken); - success = true; - series.Name = doc.SafeGetString("//SeriesName"); series.Overview = doc.SafeGetString("//Overview"); series.CommunityRating = doc.SafeGetSingle("//Rating", 0, 10); @@ -268,8 +258,15 @@ namespace MediaBrowser.Controller.Providers.TV } } - //wait for other tasks - await Task.WhenAll(actorTask, imageTask).ConfigureAwait(false); + try + { + //wait for other tasks + await Task.WhenAll(actorTask, imageTask).ConfigureAwait(false); + } + catch (HttpException) + { + status = ProviderRefreshStatus.CompletedWithErrors; + } if (ConfigurationManager.Configuration.SaveLocalMeta) { @@ -281,9 +278,7 @@ namespace MediaBrowser.Controller.Providers.TV } } - - - return success; + return status; } /// <summary> @@ -299,22 +294,16 @@ namespace MediaBrowser.Controller.Providers.TV string urlActors = string.Format(getActors, TVUtils.TvdbApiKey, seriesId); var docActors = new XmlDocument(); - try + using (var actors = await HttpClient.Get(new HttpRequestOptions { - using (var actors = await HttpClient.Get(new HttpRequestOptions - { - Url = urlActors, - ResourcePool = TvDbResourcePool, - CancellationToken = cancellationToken, - EnableResponseCache = true + Url = urlActors, + ResourcePool = TvDbResourcePool, + CancellationToken = cancellationToken, + EnableResponseCache = true - }).ConfigureAwait(false)) - { - docActors.Load(actors); - } - } - catch (HttpException) + }).ConfigureAwait(false)) { + docActors.Load(actors); } if (docActors.HasChildNodes) @@ -380,22 +369,16 @@ namespace MediaBrowser.Controller.Providers.TV string url = string.Format("http://www.thetvdb.com/api/" + TVUtils.TvdbApiKey + "/series/{0}/banners.xml", seriesId); var images = new XmlDocument(); - try + using (var imgs = await HttpClient.Get(new HttpRequestOptions { - using (var imgs = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = TvDbResourcePool, - CancellationToken = cancellationToken, - EnableResponseCache = true + Url = url, + ResourcePool = TvDbResourcePool, + CancellationToken = cancellationToken, + EnableResponseCache = true - }).ConfigureAwait(false)) - { - images.Load(imgs); - } - } - catch (HttpException) + }).ConfigureAwait(false)) { + images.Load(imgs); } if (images.HasChildNodes) @@ -408,17 +391,7 @@ namespace MediaBrowser.Controller.Providers.TV n = n.SelectSingleNode("./BannerPath"); if (n != null) { - try - { - series.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "folder" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, TvDbResourcePool, cancellationToken).ConfigureAwait(false); - } - catch (HttpException) - { - } - catch (IOException) - { - - } + series.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "folder" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, TvDbResourcePool, cancellationToken).ConfigureAwait(false); } } } @@ -431,19 +404,9 @@ namespace MediaBrowser.Controller.Providers.TV n = n.SelectSingleNode("./BannerPath"); if (n != null) { - try - { - var bannerImagePath = await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "banner" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, TvDbResourcePool, cancellationToken); - - series.SetImage(ImageType.Banner, bannerImagePath); - } - catch (HttpException) - { - } - catch (IOException) - { + var bannerImagePath = await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "banner" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, TvDbResourcePool, cancellationToken); - } + series.SetImage(ImageType.Banner, bannerImagePath); } } } @@ -460,17 +423,7 @@ namespace MediaBrowser.Controller.Providers.TV var bdName = "backdrop" + (bdNo > 0 ? bdNo.ToString(UsCulture) : ""); if (ConfigurationManager.Configuration.RefreshItemImages || !series.HasLocalImage(bdName)) { - try - { - series.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + p.InnerText, bdName + Path.GetExtension(p.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, TvDbResourcePool, cancellationToken).ConfigureAwait(false)); - } - catch (HttpException) - { - } - catch (IOException) - { - - } + series.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + p.InnerText, bdName + Path.GetExtension(p.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, TvDbResourcePool, cancellationToken).ConfigureAwait(false)); } bdNo++; if (bdNo >= ConfigurationManager.Configuration.MaxBackdrops) break; @@ -481,27 +434,13 @@ namespace MediaBrowser.Controller.Providers.TV } /// <summary> - /// Determines whether [has complete metadata] [the specified series]. - /// </summary> - /// <param name="series">The series.</param> - /// <returns><c>true</c> if [has complete metadata] [the specified series]; otherwise, <c>false</c>.</returns> - private bool HasCompleteMetadata(Series series) - { - return (series.HasImage(ImageType.Banner)) && (series.CommunityRating != null) - && (series.Overview != null) && (series.Name != null) && (series.People != null) - && (series.Genres != null) && (series.OfficialRating != null); - } - - /// <summary> /// Determines whether [has local meta] [the specified item]. /// </summary> /// <param name="item">The item.</param> /// <returns><c>true</c> if [has local meta] [the specified item]; otherwise, <c>false</c>.</returns> private bool HasLocalMeta(BaseItem item) { - //need at least the xml and folder.jpg/png - return item.ResolveArgs.ContainsMetaFileByName(LOCAL_META_FILE_NAME) && (item.ResolveArgs.ContainsMetaFileByName("folder.jpg") || - item.ResolveArgs.ContainsMetaFileByName("folder.png")); + return item.ResolveArgs.ContainsMetaFileByName(LOCAL_META_FILE_NAME); } /// <summary> diff --git a/MediaBrowser.Controller/Providers/TV/SeriesXmlParser.cs b/MediaBrowser.Controller/Providers/TV/SeriesXmlParser.cs index f63a47627..c03e2a7f5 100644 --- a/MediaBrowser.Controller/Providers/TV/SeriesXmlParser.cs +++ b/MediaBrowser.Controller/Providers/TV/SeriesXmlParser.cs @@ -63,8 +63,15 @@ namespace MediaBrowser.Controller.Providers.TV } case "Airs_Time": - item.AirTime = reader.ReadElementContentAsString(); - break; + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + item.AirTime = val; + } + break; + } case "SeriesName": item.Name = reader.ReadElementContentAsString(); diff --git a/MediaBrowser.Controller/Resolvers/ResolverPriority.cs b/MediaBrowser.Controller/Resolvers/ResolverPriority.cs index 63a107fda..df5edeb05 100644 --- a/MediaBrowser.Controller/Resolvers/ResolverPriority.cs +++ b/MediaBrowser.Controller/Resolvers/ResolverPriority.cs @@ -18,9 +18,10 @@ namespace MediaBrowser.Controller.Resolvers /// The third /// </summary> Third = 3, + Fourth = 4, /// <summary> /// The last /// </summary> - Last = 4 + Last = 5 } } diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index 8227170d4..f28721f5f 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -52,7 +52,7 @@ namespace MediaBrowser.Controller.Session /// <param name="deviceId">The device id.</param> /// <param name="deviceName">Name of the device.</param> /// <exception cref="System.ArgumentNullException"></exception> - void OnPlaybackStart(User user, BaseItem item, string clientType, string deviceId, string deviceName); + Task OnPlaybackStart(User user, BaseItem item, string clientType, string deviceId, string deviceName); /// <summary> /// Used to report playback progress for an item diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 65905b082..b692e97f3 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -269,16 +269,16 @@ namespace MediaBrowser.Server.Implementations.Library // Need to use DistinctBy Id because there could be multiple instances with the same id // due to sharing the default library var userRootFolders = _userManager.Users.Select(i => i.RootFolder) - .DistinctBy(i => i.Id) + .Distinct() .ToList(); items.AddRange(userRootFolders); // Get all user collection folders + // Skip BasePluginFolders because we already got them from RootFolder.RecursiveChildren var userFolders = - _userManager.Users.SelectMany(i => i.RootFolder.Children) + userRootFolders.SelectMany(i => i.Children) .Where(i => !(i is BasePluginFolder)) - .DistinctBy(i => i.Id) .ToList(); items.AddRange(userFolders); diff --git a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs index b0a0f8760..cc586ccbf 100644 --- a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs +++ b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs @@ -408,7 +408,7 @@ namespace MediaBrowser.Server.Implementations.Providers { return (saveLocally && item.MetaLocation != null) ? Path.Combine(item.MetaLocation, targetFileName) : - _remoteImageCache.GetResourcePath(item.GetType().FullName + item.Path.ToLower(), targetFileName); + _remoteImageCache.GetResourcePath(item.GetType().FullName + item.Id.ToString(), targetFileName); } /// <summary> diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/AudioImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/AudioImagesTask.cs index d3ed270b8..c56a2b54a 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/AudioImagesTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/AudioImagesTask.cs @@ -46,14 +46,14 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks private readonly List<Audio> _newlyAddedItems = new List<Audio>(); - private const int NewItemDelay = 300000; + private const int NewItemDelay = 60000; /// <summary> /// The current new item timer /// </summary> /// <value>The new item timer.</value> private Timer NewItemTimer { get; set; } - + /// <summary> /// Initializes a new instance of the <see cref="AudioImagesTask" /> class. /// </summary> @@ -118,7 +118,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks foreach (var item in newSongs .Where(i => i.LocationType == LocationType.FileSystem && string.IsNullOrEmpty(i.PrimaryImagePath) && i.MediaStreams.Any(m => m.Type == MediaStreamType.Video)) - .Take(20)) + .Take(10)) { try { @@ -130,7 +130,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks } } } - + /// <summary> /// Gets the name of the task /// </summary> @@ -216,7 +216,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks var filename = item.Album ?? string.Empty; - filename += album == null ? item.Id.ToString("N") + item.DateModified.Ticks : album.Id.ToString() + album.DateModified.Ticks; + filename += album == null ? item.Id.ToString("N") + item.DateModified.Ticks : album.Id.ToString("N") + album.DateModified.Ticks; var path = ImageCache.GetResourcePath(filename + "_primary", ".jpg"); @@ -232,7 +232,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks { try { - await _mediaEncoder.ExtractImage(new[] {item.Path}, InputType.AudioFile, null, path, cancellationToken).ConfigureAwait(false); + await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.AudioFile, null, path, cancellationToken).ConfigureAwait(false); } finally { diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs index 87b99973d..4178d4f8b 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks private readonly List<Video> _newlyAddedItems = new List<Video>(); - private const int NewItemDelay = 300000; + private const int NewItemDelay = 60000; /// <summary> /// The current new item timer @@ -95,7 +95,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks foreach (var item in newItems .Where(i => i.LocationType == LocationType.FileSystem && string.IsNullOrEmpty(i.PrimaryImagePath) && i.MediaStreams.Any(m => m.Type == MediaStreamType.Video)) - .Take(5)) + .Take(1)) { try { diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs index 6082b629e..a343943f7 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs @@ -1,11 +1,11 @@ using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Tasks; +using MediaBrowser.Server.Implementations.Library; using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Server.Implementations.Library; namespace MediaBrowser.Server.Implementations.ScheduledTasks { diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs index dcceb382c..f0afe6358 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs @@ -55,7 +55,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks private readonly List<BaseItem> _newlyAddedItems = new List<BaseItem>(); - private const int NewItemDelay = 300000; + private const int NewItemDelay = 60000; /// <summary> /// The current new item timer @@ -124,7 +124,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks NewItemTimer = null; } - foreach (var item in GetItemsForExtraction(newItems.Take(5))) + foreach (var item in GetItemsForExtraction(newItems.Take(3))) { try { @@ -215,7 +215,8 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks var videos = allItems.OfType<Video>().ToList(); - var items = videos; + var items = videos.ToList(); + items.AddRange(localTrailers); items.AddRange(themeVideos); diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 2f9c7e389..d3dbbc62b 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -200,7 +200,7 @@ namespace MediaBrowser.Server.Implementations.Session /// <param name="deviceName">Name of the device.</param> /// <exception cref="System.ArgumentNullException"> /// </exception> - public void OnPlaybackStart(User user, BaseItem item, string clientType, string deviceId, string deviceName) + public async Task OnPlaybackStart(User user, BaseItem item, string clientType, string deviceId, string deviceName) { if (user == null) { @@ -213,6 +213,15 @@ namespace MediaBrowser.Server.Implementations.Session UpdateNowPlayingItemId(user, clientType, deviceId, deviceName, item, false); + var key = item.GetUserDataKey(); + + var data = await _userDataRepository.GetUserData(user.Id, key).ConfigureAwait(false); + + data.PlayCount++; + data.LastPlayedDate = DateTime.UtcNow; + + await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false); + // Nothing to save here // Fire events to inform plugins EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs @@ -254,7 +263,7 @@ namespace MediaBrowser.Server.Implementations.Session { var data = await _userDataRepository.GetUserData(user.Id, key).ConfigureAwait(false); - UpdatePlayState(item, data, positionTicks.Value, false); + UpdatePlayState(item, data, positionTicks.Value); await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false); } @@ -297,7 +306,7 @@ namespace MediaBrowser.Server.Implementations.Session if (positionTicks.HasValue) { - UpdatePlayState(item, data, positionTicks.Value, true); + UpdatePlayState(item, data, positionTicks.Value); } else { @@ -322,11 +331,12 @@ namespace MediaBrowser.Server.Implementations.Session /// <param name="item">The item</param> /// <param name="data">User data for the item</param> /// <param name="positionTicks">The current playback position</param> - /// <param name="incrementPlayCount">Whether or not to increment playcount</param> - private void UpdatePlayState(BaseItem item, UserItemData data, long positionTicks, bool incrementPlayCount) + private void UpdatePlayState(BaseItem item, UserItemData data, long positionTicks) { + var hasRuntime = item.RunTimeTicks.HasValue && item.RunTimeTicks > 0; + // If a position has been reported, and if we know the duration - if (positionTicks > 0 && item.RunTimeTicks.HasValue && item.RunTimeTicks > 0) + if (positionTicks > 0 && hasRuntime) { var pctIn = Decimal.Divide(positionTicks, item.RunTimeTicks.Value) * 100; @@ -334,7 +344,6 @@ namespace MediaBrowser.Server.Implementations.Session if (pctIn < _configurationManager.Configuration.MinResumePct) { positionTicks = 0; - incrementPlayCount = false; } // If we're at the end, assume completed @@ -356,19 +365,19 @@ namespace MediaBrowser.Server.Implementations.Session } } } + else if (!hasRuntime) + { + // If we don't know the runtime we'll just have to assume it was fully played + data.Played = true; + positionTicks = 0; + } if (item is Audio) { - data.PlaybackPositionTicks = 0; + positionTicks = 0; } data.PlaybackPositionTicks = positionTicks; - - if (incrementPlayCount) - { - data.PlayCount++; - data.LastPlayedDate = DateTime.UtcNow; - } } } } diff --git a/MediaBrowser.ServerApplication/App.xaml.cs b/MediaBrowser.ServerApplication/App.xaml.cs index 762eb4cd2..5a1d7505c 100644 --- a/MediaBrowser.ServerApplication/App.xaml.cs +++ b/MediaBrowser.ServerApplication/App.xaml.cs @@ -6,6 +6,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Server.Implementations; +using MediaBrowser.ServerApplication.Splash; using Microsoft.Win32; using System; using System.Diagnostics; @@ -164,11 +165,19 @@ namespace MediaBrowser.ServerApplication Logger = CompositionRoot.LogManager.GetLogger("App"); + var splash = new SplashWindow(CompositionRoot.ApplicationVersion); + + splash.Show(); + await CompositionRoot.Init(); - var win = new MainWindow(CompositionRoot.LogManager, CompositionRoot, CompositionRoot.ServerConfigurationManager, CompositionRoot.UserManager, CompositionRoot.LibraryManager, CompositionRoot.JsonSerializer, CompositionRoot.DisplayPreferencesManager); + splash.Hide(); + + var task = CompositionRoot.RunStartupTasks(); + + new MainWindow(CompositionRoot.LogManager, CompositionRoot, CompositionRoot.ServerConfigurationManager, CompositionRoot.UserManager, CompositionRoot.LibraryManager, CompositionRoot.JsonSerializer, CompositionRoot.DisplayPreferencesManager).Show(); - win.Show(); + await task.ConfigureAwait(false); } catch (Exception ex) { diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 4dd24d98f..35a9698c3 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -44,7 +44,6 @@ using MediaBrowser.Server.Implementations.Udp; using MediaBrowser.Server.Implementations.Updates; using MediaBrowser.Server.Implementations.WebSocket; using MediaBrowser.ServerApplication.Implementations; -using MediaBrowser.ServerApplication.Splash; using MediaBrowser.WebDashboard.Api; using System; using System.Collections.Generic; @@ -63,7 +62,7 @@ namespace MediaBrowser.ServerApplication /// </summary> public class ApplicationHost : BaseApplicationHost<ServerApplicationPaths>, IServerApplicationHost { - private const int UdpServerPort = 7359; + internal const int UdpServerPort = 7359; /// <summary> /// Gets the server kernel. @@ -140,11 +139,6 @@ namespace MediaBrowser.ServerApplication private IHttpServer HttpServer { get; set; } /// <summary> - /// Gets or sets the UDP server. - /// </summary> - /// <value>The UDP server.</value> - private UdpServer UdpServer { get; set; } - /// <summary> /// Gets or sets the display preferences manager. /// </summary> /// <value>The display preferences manager.</value> @@ -176,25 +170,10 @@ namespace MediaBrowser.ServerApplication private Task<IHttpServer> _httpServerCreationTask; /// <summary> - /// Inits this instance. - /// </summary> - /// <returns>Task.</returns> - public override async Task Init() - { - var win = new SplashWindow(ApplicationVersion); - - win.Show(); - - await base.Init(); - - win.Hide(); - } - - /// <summary> /// Runs the startup tasks. /// </summary> /// <returns>Task.</returns> - protected override async Task RunStartupTasks() + public override async Task RunStartupTasks() { await base.RunStartupTasks().ConfigureAwait(false); @@ -390,21 +369,8 @@ namespace MediaBrowser.ServerApplication () => LibraryManager.AddParts(GetExports<IResolverIgnoreRule>(), GetExports<IVirtualFolderCreator>(), GetExports<IItemResolver>(), GetExports<IIntroProvider>(), GetExports<IBaseItemComparer>()), - () => ProviderManager.AddMetadataProviders(GetExports<BaseMetadataProvider>().ToArray()), + () => ProviderManager.AddMetadataProviders(GetExports<BaseMetadataProvider>().ToArray()) - () => - { - UdpServer = new UdpServer(Logger, NetworkManager, ServerConfigurationManager); - - try - { - UdpServer.Start(UdpServerPort); - } - catch (SocketException ex) - { - Logger.ErrorException("Failed to start UDP Server", ex); - } - } ); } @@ -472,23 +438,6 @@ namespace MediaBrowser.ServerApplication } /// <summary> - /// Releases unmanaged and - optionally - managed resources. - /// </summary> - /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> - protected override void Dispose(bool dispose) - { - if (dispose) - { - if (UdpServer != null) - { - UdpServer.Dispose(); - } - } - - base.Dispose(dispose); - } - - /// <summary> /// Checks for update. /// </summary> /// <param name="cancellationToken">The cancellation token.</param> diff --git a/MediaBrowser.ServerApplication/EntryPoints/UdpServerEntryPoint.cs b/MediaBrowser.ServerApplication/EntryPoints/UdpServerEntryPoint.cs new file mode 100644 index 000000000..af4ce84e3 --- /dev/null +++ b/MediaBrowser.ServerApplication/EntryPoints/UdpServerEntryPoint.cs @@ -0,0 +1,61 @@ +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Plugins; +using MediaBrowser.Model.Logging; +using MediaBrowser.Server.Implementations.Udp; +using System.Net.Sockets; + +namespace MediaBrowser.ServerApplication.EntryPoints +{ + public class UdpServerEntryPoint : IServerEntryPoint + { + /// <summary> + /// Gets or sets the UDP server. + /// </summary> + /// <value>The UDP server.</value> + private UdpServer UdpServer { get; set; } + + private readonly ILogger _logger; + private readonly INetworkManager _networkManager; + private readonly IServerConfigurationManager _serverConfigurationManager; + + public UdpServerEntryPoint(ILogger logger, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager) + { + _logger = logger; + _networkManager = networkManager; + _serverConfigurationManager = serverConfigurationManager; + } + + public void Run() + { + var udpServer = new UdpServer(_logger, _networkManager, _serverConfigurationManager); + + try + { + udpServer.Start(ApplicationHost.UdpServerPort); + + UdpServer = udpServer; + } + catch (SocketException ex) + { + _logger.ErrorException("Failed to start UDP Server", ex); + } + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + if (UdpServer != null) + { + UdpServer.Dispose(); + } + } + } + } +} diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 19b2d91ca..7c4eb128d 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -196,6 +196,7 @@ <Compile Include="EntryPoints\LoadRegistrations.cs" /> <Compile Include="EntryPoints\RefreshUsersMetadata.cs" /> <Compile Include="EntryPoints\StartupWizard.cs" /> + <Compile Include="EntryPoints\UdpServerEntryPoint.cs" /> <Compile Include="EntryPoints\WebSocketEvents.cs" /> <Compile Include="Splash\SplashWindow.xaml.cs"> <DependentUpon>SplashWindow.xaml</DependentUpon> diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index cbe918ddf..13b1bae99 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common.Internal</id> - <version>3.0.103</version> + <version>3.0.104</version> <title>MediaBrowser.Common.Internal</title> <authors>Luke</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains common components shared by Media Browser Theatre and Media Browser Server. Not intended for plugin developer consumption.</description> <copyright>Copyright © Media Browser 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.103" /> + <dependency id="MediaBrowser.Common" version="3.0.104" /> <dependency id="NLog" version="2.0.1.2" /> <dependency id="ServiceStack.Text" version="3.9.45" /> <dependency id="SimpleInjector" version="2.2.1" /> diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 00ba0028f..a8530230c 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common</id> - <version>3.0.103</version> + <version>3.0.104</version> <title>MediaBrowser.Common</title> <authors>Media Browser Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index a2dee313d..8236cd633 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <metadata> <id>MediaBrowser.Server.Core</id> - <version>3.0.103</version> + <version>3.0.104</version> <title>Media Browser.Server.Core</title> <authors>Media Browser Team</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains core components required to build plugins for Media Browser Server.</description> <copyright>Copyright © Media Browser 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.103" /> + <dependency id="MediaBrowser.Common" version="3.0.104" /> </dependencies> </metadata> <files> |
