diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index 80ebbb719..e9f8f81f3 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -829,18 +829,7 @@ namespace Emby.Drawing
// Run the enhancers sequentially in order of priority
foreach (var enhancer in imageEnhancers)
{
- var typeName = enhancer.GetType().Name;
-
- try
- {
- await enhancer.EnhanceImageAsync(item, inputPath, outputPath, imageType, imageIndex).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("{0} failed enhancing {1}", ex, typeName, item.Name);
-
- throw;
- }
+ await enhancer.EnhanceImageAsync(item, inputPath, outputPath, imageType, imageIndex).ConfigureAwait(false);
// Feed the output into the next enhancer as input
inputPath = outputPath;
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 7c5f7cde4..c6e45c61a 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session;
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -15,6 +16,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
+using MediaBrowser.Model.Dto;
namespace MediaBrowser.Api
{
@@ -43,7 +45,13 @@ namespace MediaBrowser.Api
private readonly IFileSystem _fileSystem;
private readonly IMediaSourceManager _mediaSourceManager;
- public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1);
+ ///
+ /// The active transcoding jobs
+ ///
+ private readonly List _activeTranscodingJobs = new List();
+
+ private readonly Dictionary _transcodingLocks =
+ new Dictionary();
///
/// Initializes a new instance of the class.
@@ -66,6 +74,21 @@ namespace MediaBrowser.Api
_sessionManager.PlaybackStart += _sessionManager_PlaybackStart;
}
+ public SemaphoreSlim GetTranscodingLock(string outputPath)
+ {
+ lock (_transcodingLocks)
+ {
+ SemaphoreSlim result;
+ if (!_transcodingLocks.TryGetValue(outputPath, out result))
+ {
+ result = new SemaphoreSlim(1, 1);
+ _transcodingLocks[outputPath] = result;
+ }
+
+ return result;
+ }
+ }
+
private void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
{
if (!string.IsNullOrWhiteSpace(e.PlaySessionId))
@@ -147,11 +170,6 @@ namespace MediaBrowser.Api
}
}
- ///
- /// The active transcoding jobs
- ///
- private readonly List _activeTranscodingJobs = new List();
-
///
/// Called when [transcode beginning].
///
@@ -187,7 +205,8 @@ namespace MediaBrowser.Api
CancellationTokenSource = cancellationTokenSource,
Id = transcodingJobId,
PlaySessionId = playSessionId,
- LiveStreamId = liveStreamId
+ LiveStreamId = liveStreamId,
+ MediaSource = state.MediaSource
};
_activeTranscodingJobs.Add(job);
@@ -256,6 +275,11 @@ namespace MediaBrowser.Api
}
}
+ lock (_transcodingLocks)
+ {
+ _transcodingLocks.Remove(path);
+ }
+
if (!string.IsNullOrWhiteSpace(state.Request.DeviceId))
{
_sessionManager.ClearTranscodingInfo(state.Request.DeviceId);
@@ -281,6 +305,14 @@ namespace MediaBrowser.Api
}
}
+ public TranscodingJob GetTranscodingJob(string playSessionId)
+ {
+ lock (_activeTranscodingJobs)
+ {
+ return _activeTranscodingJobs.FirstOrDefault(j => string.Equals(j.PlaySessionId, playSessionId, StringComparison.OrdinalIgnoreCase));
+ }
+ }
+
///
/// Called when [transcode begin request].
///
@@ -487,6 +519,11 @@ namespace MediaBrowser.Api
}
}
+ lock (_transcodingLocks)
+ {
+ _transcodingLocks.Remove(job.Path);
+ }
+
lock (job.ProcessLock)
{
if (job.TranscodingThrottler != null)
@@ -530,7 +567,7 @@ namespace MediaBrowser.Api
{
try
{
- await _mediaSourceManager.CloseLiveStream(job.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
+ await _mediaSourceManager.CloseLiveStream(job.LiveStreamId).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -656,6 +693,7 @@ namespace MediaBrowser.Api
/// Gets or sets the path.
///
/// The path.
+ public MediaSourceInfo MediaSource { get; set; }
public string Path { get; set; }
///
/// Gets or sets the type.
@@ -751,12 +789,12 @@ namespace MediaBrowser.Api
{
if (KillTimer == null)
{
- Logger.Debug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
+ //Logger.Debug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
KillTimer = new Timer(callback, this, intervalMs, Timeout.Infinite);
}
else
{
- Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
+ //Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
KillTimer.Change(intervalMs, Timeout.Infinite);
}
}
@@ -775,7 +813,7 @@ namespace MediaBrowser.Api
{
var intervalMs = PingTimeout;
- Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
+ //Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
KillTimer.Change(intervalMs, Timeout.Infinite);
}
}
diff --git a/MediaBrowser.Api/Dlna/DlnaServerService.cs b/MediaBrowser.Api/Dlna/DlnaServerService.cs
index 4e7b1a7d5..b2c7e5532 100644
--- a/MediaBrowser.Api/Dlna/DlnaServerService.cs
+++ b/MediaBrowser.Api/Dlna/DlnaServerService.cs
@@ -7,6 +7,8 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
namespace MediaBrowser.Api.Dlna
{
@@ -109,13 +111,15 @@ namespace MediaBrowser.Api.Dlna
private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar;
private const string XMLContentType = "text/xml; charset=UTF-8";
+ private readonly IMemoryStreamProvider _memoryStreamProvider;
- public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar)
+ public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar, IMemoryStreamProvider memoryStreamProvider)
{
_dlnaManager = dlnaManager;
_contentDirectory = contentDirectory;
_connectionManager = connectionManager;
_mediaReceiverRegistrar = mediaReceiverRegistrar;
+ _memoryStreamProvider = memoryStreamProvider;
}
public object Get(GetDescriptionXml request)
@@ -201,7 +205,7 @@ namespace MediaBrowser.Api.Dlna
{
using (var response = _dlnaManager.GetIcon(request.Filename))
{
- using (var ms = new MemoryStream())
+ using (var ms = _memoryStreamProvider.CreateNew())
{
response.Stream.CopyTo(ms);
diff --git a/MediaBrowser.Api/FilterService.cs b/MediaBrowser.Api/FilterService.cs
index b3b75359a..57aa5575f 100644
--- a/MediaBrowser.Api/FilterService.cs
+++ b/MediaBrowser.Api/FilterService.cs
@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Querying;
using ServiceStack;
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -104,7 +105,13 @@ namespace MediaBrowser.Api
MediaTypes = request.GetMediaTypes(),
IncludeItemTypes = request.GetIncludeItemTypes(),
Recursive = true,
- EnableTotalRecordCount = false
+ EnableTotalRecordCount = false,
+ DtoOptions = new Controller.Dto.DtoOptions
+ {
+ Fields = new List { ItemFields.Genres, ItemFields.Tags },
+ EnableImages = false,
+ EnableUserData = false
+ }
};
return query;
diff --git a/MediaBrowser.Api/GamesService.cs b/MediaBrowser.Api/GamesService.cs
index 0953b95e6..efa69d333 100644
--- a/MediaBrowser.Api/GamesService.cs
+++ b/MediaBrowser.Api/GamesService.cs
@@ -200,6 +200,8 @@ namespace MediaBrowser.Api
(!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
+ var dtoOptions = GetDtoOptions(request);
+
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
Limit = request.Limit,
@@ -207,12 +209,11 @@ namespace MediaBrowser.Api
{
typeof(Game).Name
},
- SimilarTo = item
+ SimilarTo = item,
+ DtoOptions = dtoOptions
}).ToList();
- var dtoOptions = GetDtoOptions(request);
-
var result = new QueryResult
{
Items = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false)).ToArray(),
diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs
index b21e54495..25d7639c6 100644
--- a/MediaBrowser.Api/Images/RemoteImageService.cs
+++ b/MediaBrowser.Api/Images/RemoteImageService.cs
@@ -273,7 +273,8 @@ namespace MediaBrowser.Api.Images
{
var result = await _httpClient.GetResponse(new HttpRequestOptions
{
- Url = url
+ Url = url,
+ BufferContent = false
}).ConfigureAwait(false);
diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs
index cda7ce0c6..687a21a46 100644
--- a/MediaBrowser.Api/ItemUpdateService.cs
+++ b/MediaBrowser.Api/ItemUpdateService.cs
@@ -243,11 +243,7 @@ namespace MediaBrowser.Api
hasBudget.Revenue = request.Revenue;
}
- var hasOriginalTitle = item as IHasOriginalTitle;
- if (hasOriginalTitle != null)
- {
- hasOriginalTitle.OriginalTitle = hasOriginalTitle.OriginalTitle;
- }
+ item.OriginalTitle = string.IsNullOrWhiteSpace(request.OriginalTitle) ? null : request.OriginalTitle;
var hasCriticRating = item as IHasCriticRating;
if (hasCriticRating != null)
@@ -278,10 +274,9 @@ namespace MediaBrowser.Api
item.Tags = request.Tags;
- var hasTaglines = item as IHasTaglines;
- if (hasTaglines != null)
+ if (request.Taglines != null)
{
- hasTaglines.Taglines = request.Taglines;
+ item.Tagline = request.Taglines.FirstOrDefault();
}
var hasShortOverview = item as IHasShortOverview;
@@ -308,8 +303,6 @@ namespace MediaBrowser.Api
item.OfficialRating = string.IsNullOrWhiteSpace(request.OfficialRating) ? null : request.OfficialRating;
item.CustomRating = request.CustomRating;
- SetProductionLocations(item, request);
-
item.PreferredMetadataCountryCode = request.PreferredMetadataCountryCode;
item.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
@@ -417,23 +410,5 @@ namespace MediaBrowser.Api
series.AirTime = request.AirTime;
}
}
-
- private void SetProductionLocations(BaseItem item, BaseItemDto request)
- {
- var hasProductionLocations = item as IHasProductionLocations;
-
- if (hasProductionLocations != null)
- {
- hasProductionLocations.ProductionLocations = request.ProductionLocations;
- }
-
- var person = item as Person;
- if (person != null)
- {
- person.PlaceOfBirth = request.ProductionLocations == null
- ? null
- : request.ProductionLocations.FirstOrDefault();
- }
- }
}
}
diff --git a/MediaBrowser.Api/Library/ChapterService.cs b/MediaBrowser.Api/Library/ChapterService.cs
deleted file mode 100644
index 6b8dd18f1..000000000
--- a/MediaBrowser.Api/Library/ChapterService.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using MediaBrowser.Controller.Chapters;
-using MediaBrowser.Controller.Net;
-using ServiceStack;
-using System.Linq;
-
-namespace MediaBrowser.Api.Library
-{
- [Route("/Providers/Chapters", "GET")]
- public class GetChapterProviders : IReturnVoid
- {
- }
-
- [Authenticated]
- public class ChapterService : BaseApiService
- {
- private readonly IChapterManager _chapterManager;
-
- public ChapterService(IChapterManager chapterManager)
- {
- _chapterManager = chapterManager;
- }
-
- public object Get(GetChapterProviders request)
- {
- var result = _chapterManager.GetProviders().ToList();
-
- return ToOptimizedResult(result);
- }
- }
-}
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index c6b637f01..e0dfcb9ad 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -835,14 +835,14 @@ namespace MediaBrowser.Api.Library
: (Folder)_libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id);
- while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
+ while (item.ThemeSongIds.Count == 0 && request.InheritFromParent && item.GetParent() != null)
{
item = item.GetParent();
}
var dtoOptions = GetDtoOptions(request);
- var dtos = GetThemeSongIds(item).Select(_libraryManager.GetItemById)
+ var dtos = item.ThemeSongIds.Select(_libraryManager.GetItemById)
.Where(i => i != null)
.OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
@@ -879,14 +879,14 @@ namespace MediaBrowser.Api.Library
: (Folder)_libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id);
- while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
+ while (item.ThemeVideoIds.Count == 0 && request.InheritFromParent && item.GetParent() != null)
{
item = item.GetParent();
}
var dtoOptions = GetDtoOptions(request);
- var dtos = GetThemeVideoIds(item).Select(_libraryManager.GetItemById)
+ var dtos = item.ThemeVideoIds.Select(_libraryManager.GetItemById)
.Where(i => i != null)
.OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
@@ -901,30 +901,6 @@ namespace MediaBrowser.Api.Library
};
}
- private List GetThemeVideoIds(BaseItem item)
- {
- var i = item as IHasThemeMedia;
-
- if (i != null)
- {
- return i.ThemeVideoIds;
- }
-
- return new List();
- }
-
- private List GetThemeSongIds(BaseItem item)
- {
- var i = item as IHasThemeMedia;
-
- if (i != null)
- {
- return i.ThemeSongIds;
- }
-
- return new List();
- }
-
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public object Get(GetYearIndex request)
diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs
index 72966a7cd..e872061a7 100644
--- a/MediaBrowser.Api/Library/LibraryStructureService.cs
+++ b/MediaBrowser.Api/Library/LibraryStructureService.cs
@@ -112,6 +112,8 @@ namespace MediaBrowser.Api.Library
/// The name.
public string Path { get; set; }
+ public MediaPathInfo PathInfo { get; set; }
+
///
/// Gets or sets a value indicating whether [refresh library].
///
@@ -119,6 +121,18 @@ namespace MediaBrowser.Api.Library
public bool RefreshLibrary { get; set; }
}
+ [Route("/Library/VirtualFolders/Paths/Update", "POST")]
+ public class UpdateMediaPath : IReturnVoid
+ {
+ ///
+ /// Gets or sets the name.
+ ///
+ /// The name.
+ public string Name { get; set; }
+
+ public MediaPathInfo PathInfo { get; set; }
+ }
+
[Route("/Library/VirtualFolders/Paths", "DELETE")]
public class RemoveMediaPath : IReturnVoid
{
@@ -212,7 +226,12 @@ namespace MediaBrowser.Api.Library
{
var libraryOptions = request.LibraryOptions ?? new LibraryOptions();
- _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, request.Paths, libraryOptions, request.RefreshLibrary);
+ if (request.Paths != null && request.Paths.Length > 0)
+ {
+ libraryOptions.PathInfos = request.Paths.Select(i => new MediaPathInfo { Path = i }).ToArray();
+ }
+
+ _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, libraryOptions, request.RefreshLibrary);
}
///
@@ -308,7 +327,16 @@ namespace MediaBrowser.Api.Library
try
{
- _libraryManager.AddMediaPath(request.Name, request.Path);
+ var mediaPath = request.PathInfo;
+
+ if (mediaPath == null)
+ {
+ mediaPath = new MediaPathInfo
+ {
+ Path = request.Path
+ };
+ }
+ _libraryManager.AddMediaPath(request.Name, mediaPath);
}
finally
{
@@ -332,6 +360,20 @@ namespace MediaBrowser.Api.Library
}
}
+ ///
+ /// Posts the specified request.
+ ///
+ /// The request.
+ public void Post(UpdateMediaPath request)
+ {
+ if (string.IsNullOrWhiteSpace(request.Name))
+ {
+ throw new ArgumentNullException("request");
+ }
+
+ _libraryManager.UpdateMediaPath(request.Name, request.PathInfo);
+ }
+
///
/// Deletes the specified request.
///
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index 3ad0ec1ba..4217cd6ab 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -12,9 +12,14 @@ using ServiceStack;
using System;
using System.Collections.Generic;
using System.Globalization;
+using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using CommonIO;
+using MediaBrowser.Api.Playback.Progressive;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Server.Implementations.LiveTv.EmbyTV;
namespace MediaBrowser.Api.LiveTv
{
@@ -44,6 +49,21 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? StartIndex { get; set; }
+ [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+ public bool? IsMovie { get; set; }
+
+ [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+ public bool? IsSeries { get; set; }
+
+ [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+ public bool? IsNews { get; set; }
+
+ [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+ public bool? IsKids { get; set; }
+
+ [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+ public bool? IsSports { get; set; }
+
///
/// The maximum number of items to return
///
@@ -85,6 +105,26 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableUserData { get; set; }
+ public string SortBy { get; set; }
+
+ public SortOrder? SortOrder { get; set; }
+
+ ///
+ /// Gets the order by.
+ ///
+ /// IEnumerable{ItemSortBy}.
+ public string[] GetOrderBy()
+ {
+ var val = SortBy;
+
+ if (string.IsNullOrEmpty(val))
+ {
+ return new string[] { };
+ }
+
+ return val.Split(',');
+ }
+
public GetChannels()
{
AddCurrentProgram = true;
@@ -159,6 +199,7 @@ namespace MediaBrowser.Api.LiveTv
public bool? IsSeries { get; set; }
public bool? IsKids { get; set; }
public bool? IsSports { get; set; }
+ public bool? IsNews { get; set; }
public GetRecordings()
{
@@ -275,6 +316,8 @@ namespace MediaBrowser.Api.LiveTv
public string SeriesTimerId { get; set; }
public bool? IsActive { get; set; }
+
+ public bool? IsScheduled { get; set; }
}
[Route("/LiveTv/Programs", "GET,POST", Summary = "Gets available live tv epgs..")]
@@ -305,6 +348,12 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsMovie { get; set; }
+ [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+ public bool? IsSeries { get; set; }
+
+ [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+ public bool? IsNews { get; set; }
+
[ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsKids { get; set; }
@@ -340,6 +389,8 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableUserData { get; set; }
+ public string SeriesTimerId { get; set; }
+
///
/// Fields to return within the items, in addition to basic information
///
@@ -376,15 +427,21 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "HasAired", Description = "Optional. Filter by programs that have completed airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? HasAired { get; set; }
- [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
- public bool? IsSports { get; set; }
+ [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+ public bool? IsSeries { get; set; }
- [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsMovie { get; set; }
- [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+ public bool? IsNews { get; set; }
+
+ [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsKids { get; set; }
+ [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+ public bool? IsSports { get; set; }
+
[ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableImages { get; set; }
@@ -613,16 +670,30 @@ namespace MediaBrowser.Api.LiveTv
}
+ [Route("/LiveTv/LiveStreamFiles/{Id}/stream.{Container}", "GET", Summary = "Gets a live tv channel")]
+ public class GetLiveStreamFile
+ {
+ public string Id { get; set; }
+ public string Container { get; set; }
+ }
+
+ [Route("/LiveTv/LiveRecordings/{Id}/stream", "GET", Summary = "Gets a live tv channel")]
+ public class GetLiveRecordingFile
+ {
+ public string Id { get; set; }
+ }
+
public class LiveTvService : BaseApiService
{
private readonly ILiveTvManager _liveTvManager;
private readonly IUserManager _userManager;
- private readonly IConfigurationManager _config;
+ private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
+ private readonly IFileSystem _fileSystem;
- public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService)
+ public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem)
{
_liveTvManager = liveTvManager;
_userManager = userManager;
@@ -630,6 +701,41 @@ namespace MediaBrowser.Api.LiveTv
_httpClient = httpClient;
_libraryManager = libraryManager;
_dtoService = dtoService;
+ _fileSystem = fileSystem;
+ }
+
+ public async Task
/// The name.
[ApiMember(Name = "PackageType", Description = "Optional package type filter (System/UserInstalled)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public PackageType? PackageType { get; set; }
+ public string PackageType { get; set; }
[ApiMember(Name = "TargetSystems", Description = "Optional. Filter by target system type. Allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET", AllowMultiple = true)]
public string TargetSystems { get; set; }
@@ -72,7 +72,7 @@ namespace MediaBrowser.Api
///
/// The name.
[ApiMember(Name = "PackageType", Description = "Package type filter (System/UserInstalled)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
- public PackageType PackageType { get; set; }
+ public string PackageType { get; set; }
}
///
@@ -149,12 +149,12 @@ namespace MediaBrowser.Api
{
var result = new List();
- if (request.PackageType == PackageType.UserInstalled || request.PackageType == PackageType.All)
+ if (string.Equals(request.PackageType, "UserInstalled", StringComparison.OrdinalIgnoreCase) || string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase))
{
result.AddRange(_installationManager.GetAvailablePluginUpdates(_appHost.ApplicationVersion, false, CancellationToken.None).Result.ToList());
}
- else if (request.PackageType == PackageType.System || request.PackageType == PackageType.All)
+ else if (string.Equals(request.PackageType, "System", StringComparison.OrdinalIgnoreCase) || string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase))
{
var updateCheckResult = _appHost.CheckForApplicationUpdate(CancellationToken.None, new Progress()).Result;
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 0b0b23e12..0e91c0b9e 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -367,6 +367,8 @@ namespace MediaBrowser.Api.Playback
{
param += " -crf 23";
}
+
+ param += " -tune zerolatency";
}
else if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase))
@@ -461,13 +463,15 @@ namespace MediaBrowser.Api.Playback
var level = NormalizeTranscodingLevel(state.OutputVideoCodec, state.VideoRequest.Level);
// h264_qsv and h264_nvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format
+ // also needed for libx264 due to https://trac.ffmpeg.org/ticket/3307
if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
+ string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
{
switch (level)
{
case "30":
- param += " -level 3";
+ param += " -level 3.0";
break;
case "31":
param += " -level 3.1";
@@ -476,7 +480,7 @@ namespace MediaBrowser.Api.Playback
param += " -level 3.2";
break;
case "40":
- param += " -level 4";
+ param += " -level 4.0";
break;
case "41":
param += " -level 4.1";
@@ -485,7 +489,7 @@ namespace MediaBrowser.Api.Playback
param += " -level 4.2";
break;
case "50":
- param += " -level 5";
+ param += " -level 5.0";
break;
case "51":
param += " -level 5.1";
@@ -767,7 +771,20 @@ namespace MediaBrowser.Api.Playback
if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
{
outputSizeParam = GetOutputSizeParam(state, outputVideoCodec).TrimEnd('"');
- outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase));
+
+ if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
+ {
+ outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase));
+ }
+ else
+ {
+ outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase));
+ }
+ }
+
+ if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) && outputSizeParam.Length == 0)
+ {
+ outputSizeParam = ",format=nv12|vaapi,hwupload";
}
var videoSizeParam = string.Empty;
@@ -802,10 +819,10 @@ namespace MediaBrowser.Api.Playback
{
if (state.PlayableStreamFileNames.Count > 0)
{
- return MediaEncoder.GetProbeSizeArgument(state.PlayableStreamFileNames.ToArray(), state.InputProtocol);
+ return MediaEncoder.GetProbeSizeAndAnalyzeDurationArgument(state.PlayableStreamFileNames.ToArray(), state.InputProtocol);
}
- return MediaEncoder.GetProbeSizeArgument(new[] { state.MediaPath }, state.InputProtocol);
+ return MediaEncoder.GetProbeSizeAndAnalyzeDurationArgument(new[] { state.MediaPath }, state.InputProtocol);
}
///
@@ -1022,7 +1039,15 @@ namespace MediaBrowser.Api.Playback
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
if (GetVideoEncoder(state).IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1)
{
- arg = "-hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device " + encodingOptions.VaapiDevice + " " + arg;
+ var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
+ var hwOutputFormat = "vaapi";
+
+ if (hasGraphicalSubs)
+ {
+ hwOutputFormat = "yuv420p";
+ }
+
+ arg = "-hwaccel vaapi -hwaccel_output_format " + hwOutputFormat + " -vaapi_device " + encodingOptions.VaapiDevice + " " + arg;
}
}
@@ -1219,7 +1244,7 @@ namespace MediaBrowser.Api.Playback
private void StartThrottler(StreamState state, TranscodingJob transcodingJob)
{
- if (EnableThrottling(state) && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EnableThrottling(state))
{
transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager);
state.TranscodingThrottler.Start();
@@ -1832,26 +1857,37 @@ namespace MediaBrowser.Api.Playback
state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
- var archivable = item as IArchivable;
- state.IsInputArchive = archivable != null && archivable.IsArchive;
-
- MediaSourceInfo mediaSource;
+ MediaSourceInfo mediaSource = null;
if (string.IsNullOrWhiteSpace(request.LiveStreamId))
{
- var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList();
+ TranscodingJob currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ?
+ ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId)
+ : null;
- mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
- ? mediaSources.First()
- : mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId));
-
- if (mediaSource == null && string.Equals(request.Id, request.MediaSourceId, StringComparison.OrdinalIgnoreCase))
+ if (currentJob != null)
{
- mediaSource = mediaSources.First();
+ mediaSource = currentJob.MediaSource;
+ }
+
+ if (mediaSource == null)
+ {
+ var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList();
+
+ mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
+ ? mediaSources.First()
+ : mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId));
+
+ if (mediaSource == null && string.Equals(request.Id, request.MediaSourceId, StringComparison.OrdinalIgnoreCase))
+ {
+ mediaSource = mediaSources.First();
+ }
}
}
else
{
- mediaSource = await MediaSourceManager.GetLiveStream(request.LiveStreamId, cancellationToken).ConfigureAwait(false);
+ var liveStreamInfo = await MediaSourceManager.GetLiveStreamWithDirectStreamProvider(request.LiveStreamId, cancellationToken).ConfigureAwait(false);
+ mediaSource = liveStreamInfo.Item1;
+ state.DirectStreamProvider = liveStreamInfo.Item2;
}
var videoRequest = request as VideoStreamRequest;
@@ -2294,7 +2330,8 @@ namespace MediaBrowser.Api.Playback
state.TargetRefFrames,
state.TargetVideoStreamCount,
state.TargetAudioStreamCount,
- state.TargetVideoCodecTag);
+ state.TargetVideoCodecTag,
+ state.IsTargetAVC);
if (mediaProfile != null)
{
@@ -2429,7 +2466,8 @@ namespace MediaBrowser.Api.Playback
Url = "https://mb3admin.com/admin/service/transcoding/report",
CancellationToken = CancellationToken.None,
LogRequest = false,
- LogErrors = false
+ LogErrors = false,
+ BufferContent = false
};
options.RequestContent = JsonSerializer.SerializeToString(dict);
options.RequestContentType = "application/json";
@@ -2512,7 +2550,8 @@ namespace MediaBrowser.Api.Playback
state.TargetRefFrames,
state.TargetVideoStreamCount,
state.TargetAudioStreamCount,
- state.TargetVideoCodecTag
+ state.TargetVideoCodecTag,
+ state.IsTargetAVC
).FirstOrDefault() ?? string.Empty;
}
@@ -2567,6 +2606,7 @@ namespace MediaBrowser.Api.Playback
inputModifier += " " + GetFastSeekCommandLineParameter(state.Request);
inputModifier = inputModifier.Trim();
+ //inputModifier += " -fflags +genpts+ignidx+igndts";
if (state.VideoRequest != null && genPts)
{
inputModifier += " -fflags +genpts";
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index a0ac96b9d..319e4bbb6 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -87,7 +87,8 @@ namespace MediaBrowser.Api.Playback.Hls
if (!FileSystem.FileExists(playlist))
{
- await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
+ var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(playlist);
+ await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
try
{
if (!FileSystem.FileExists(playlist))
@@ -104,13 +105,13 @@ namespace MediaBrowser.Api.Playback.Hls
throw;
}
- var waitForSegments = state.SegmentLength >= 10 ? 2 : 3;
+ var waitForSegments = state.SegmentLength >= 10 ? 2 : (state.SegmentLength > 3 || !isLive ? 3 : 3);
await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false);
}
}
finally
{
- ApiEntryPoint.Instance.TranscodingStartLock.Release();
+ transcodingLock.Release();
}
}
@@ -128,10 +129,9 @@ namespace MediaBrowser.Api.Playback.Hls
var audioBitrate = state.OutputAudioBitrate ?? 0;
var videoBitrate = state.OutputVideoBitrate ?? 0;
- var appendBaselineStream = false;
var baselineStreamBitrate = 64000;
- var playlistText = GetMasterPlaylistFileText(playlist, videoBitrate + audioBitrate, appendBaselineStream, baselineStreamBitrate);
+ var playlistText = GetMasterPlaylistFileText(playlist, videoBitrate + audioBitrate, baselineStreamBitrate);
job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType);
@@ -151,9 +151,10 @@ namespace MediaBrowser.Api.Playback.Hls
{
var text = reader.ReadToEnd();
+ text = text.Replace("#EXTM3U", "#EXTM3U\n#EXT-X-PLAYLIST-TYPE:EVENT");
+
var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(UsCulture);
- // ffmpeg pads the reported length by a full second
text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase);
return text;
@@ -161,7 +162,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
}
- private string GetMasterPlaylistFileText(string firstPlaylist, int bitrate, bool includeBaselineStream, int baselineStreamBitrate)
+ private string GetMasterPlaylistFileText(string firstPlaylist, int bitrate, int baselineStreamBitrate)
{
var builder = new StringBuilder();
@@ -175,14 +176,6 @@ namespace MediaBrowser.Api.Playback.Hls
var playlistUrl = "hls/" + Path.GetFileName(firstPlaylist).Replace(".m3u8", "/stream.m3u8");
builder.AppendLine(playlistUrl);
- // Low bitrate stream
- if (includeBaselineStream)
- {
- builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + baselineStreamBitrate.ToString(UsCulture));
- playlistUrl = "hls/" + Path.GetFileName(firstPlaylist).Replace(".m3u8", "-low/stream.m3u8");
- builder.AppendLine(playlistUrl);
- }
-
return builder.ToString();
}
@@ -190,32 +183,41 @@ namespace MediaBrowser.Api.Playback.Hls
{
Logger.Debug("Waiting for {0} segments in {1}", segmentCount, playlist);
- while (true)
+ while (!cancellationToken.IsCancellationRequested)
{
- // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
- using (var fileStream = GetPlaylistFileStream(playlist))
+ try
{
- using (var reader = new StreamReader(fileStream))
+ // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
+ using (var fileStream = GetPlaylistFileStream(playlist))
{
- var count = 0;
-
- while (!reader.EndOfStream)
+ using (var reader = new StreamReader(fileStream))
{
- var line = await reader.ReadLineAsync().ConfigureAwait(false);
+ var count = 0;
- if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
+ while (!reader.EndOfStream)
{
- count++;
- if (count >= segmentCount)
+ var line = await reader.ReadLineAsync().ConfigureAwait(false);
+
+ if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
{
- Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
- return;
+ count++;
+ if (count >= segmentCount)
+ {
+ Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
+ return;
+ }
}
}
+ await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
- await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
}
+ catch (IOException)
+ {
+ // May get an error if the file is locked
+ }
+
+ await Task.Delay(50, cancellationToken).ConfigureAwait(false);
}
}
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 9cd55528d..51455c141 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -171,14 +171,15 @@ namespace MediaBrowser.Api.Playback.Hls
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
- await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
+ var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(playlistPath);
+ await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
var released = false;
try
{
if (FileSystem.FileExists(segmentPath))
{
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
- ApiEntryPoint.Instance.TranscodingStartLock.Release();
+ transcodingLock.Release();
released = true;
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
@@ -242,7 +243,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
if (!released)
{
- ApiEntryPoint.Instance.TranscodingStartLock.Release();
+ transcodingLock.Release();
}
}
@@ -906,6 +907,7 @@ namespace MediaBrowser.Api.Playback.Hls
).Trim();
}
+ // TODO: check libavformat version for 57 50.100 and use -hls_flags split_by_time
return string.Format("{0}{11} {1} -map_metadata -1 -threads {2} {3} {4}{5} {6} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"",
inputModifier,
GetInputArgument(state),
diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
index 27deaf25e..976fed3f0 100644
--- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
+++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
@@ -68,8 +68,6 @@ namespace MediaBrowser.Api.Playback.Hls
[Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
public class GetHlsVideoSegmentLegacy : VideoStreamRequest
{
- // TODO: Deprecate with new iOS app
-
public string PlaylistId { get; set; }
///
@@ -113,7 +111,7 @@ namespace MediaBrowser.Api.Playback.Hls
var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
file = Path.Combine(_config.ApplicationPaths.TranscodingTempPath, file);
- var normalizedPlaylistId = request.PlaylistId.Replace("-low", string.Empty);
+ var normalizedPlaylistId = request.PlaylistId;
var playlistPath = Directory.EnumerateFiles(_config.ApplicationPaths.TranscodingTempPath, "*")
.FirstOrDefault(i => string.Equals(Path.GetExtension(i), ".m3u8", StringComparison.OrdinalIgnoreCase) && i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1);
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index 8a14948d2..c7258d72f 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -113,6 +113,8 @@ namespace MediaBrowser.Api.Playback.Hls
args += GetGraphicalSubtitleParam(state, codec);
}
+ args += " -flags -global_header";
+
return args;
}
diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs
index 656b2ee08..7fe7d5a21 100644
--- a/MediaBrowser.Api/Playback/MediaInfoService.cs
+++ b/MediaBrowser.Api/Playback/MediaInfoService.cs
@@ -107,7 +107,7 @@ namespace MediaBrowser.Api.Playback
{
var authInfo = AuthorizationContext.GetAuthorizationInfo(Request);
- var result = await _mediaSourceManager.OpenLiveStream(request, false, CancellationToken.None).ConfigureAwait(false);
+ var result = await _mediaSourceManager.OpenLiveStream(request, true, CancellationToken.None).ConfigureAwait(false);
var profile = request.DeviceProfile;
if (profile == null)
@@ -140,7 +140,7 @@ namespace MediaBrowser.Api.Playback
public void Post(CloseMediaSource request)
{
- var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId, CancellationToken.None);
+ var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId);
Task.WaitAll(task);
}
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index b8cb6b14f..809eabef8 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
@@ -17,6 +17,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
+using ServiceStack;
namespace MediaBrowser.Api.Playback.Progressive
{
@@ -26,12 +27,10 @@ namespace MediaBrowser.Api.Playback.Progressive
public abstract class BaseProgressiveStreamingService : BaseStreamingService
{
protected readonly IImageProcessor ImageProcessor;
- protected readonly IHttpClient HttpClient;
protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer)
{
ImageProcessor = imageProcessor;
- HttpClient = httpClient;
}
///
@@ -122,6 +121,25 @@ namespace MediaBrowser.Api.Playback.Progressive
var responseHeaders = new Dictionary();
+ if (request.Static && state.DirectStreamProvider != null)
+ {
+ AddDlnaHeaders(state, responseHeaders, true);
+
+ using (state)
+ {
+ var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ // TODO: Don't hardcode this
+ outputHeaders["Content-Type"] = MediaBrowser.Model.Net.MimeTypes.GetMimeType("file.ts");
+
+ var streamSource = new ProgressiveFileCopier(state.DirectStreamProvider, outputHeaders, null, Logger, CancellationToken.None)
+ {
+ AllowEndOfFile = false
+ };
+ return ResultFactory.GetAsyncStreamWriter(streamSource);
+ }
+ }
+
// Static remote stream
if (request.Static && state.InputProtocol == MediaProtocol.Http)
{
@@ -129,8 +147,7 @@ namespace MediaBrowser.Api.Playback.Progressive
using (state)
{
- return await GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource)
- .ConfigureAwait(false);
+ return await GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).ConfigureAwait(false);
}
}
@@ -154,6 +171,19 @@ namespace MediaBrowser.Api.Playback.Progressive
using (state)
{
+ if (state.MediaSource.IsInfiniteStream)
+ {
+ var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ outputHeaders["Content-Type"] = contentType;
+
+ var streamSource = new ProgressiveFileCopier(FileSystem, state.MediaPath, outputHeaders, null, Logger, CancellationToken.None)
+ {
+ AllowEndOfFile = false
+ };
+ return ResultFactory.GetAsyncStreamWriter(streamSource);
+ }
+
TimeSpan? cacheDuration = null;
if (!string.IsNullOrEmpty(request.Tag))
@@ -345,7 +375,8 @@ namespace MediaBrowser.Api.Playback.Progressive
return streamResult;
}
- await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
+ var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(outputPath);
+ await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
try
{
TranscodingJob job;
@@ -376,7 +407,7 @@ namespace MediaBrowser.Api.Playback.Progressive
}
finally
{
- ApiEntryPoint.Instance.TranscodingStartLock.Release();
+ transcodingLock.Release();
}
}
diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
index 0a9a44641..3477ad57b 100644
--- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
+++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
@@ -7,6 +7,7 @@ using CommonIO;
using MediaBrowser.Controller.Net;
using System.Collections.Generic;
using ServiceStack.Web;
+using MediaBrowser.Controller.Library;
namespace MediaBrowser.Api.Playback.Progressive
{
@@ -23,6 +24,10 @@ namespace MediaBrowser.Api.Playback.Progressive
private const int BufferSize = 81920;
private long _bytesWritten = 0;
+ public long StartPosition { get; set; }
+ public bool AllowEndOfFile = true;
+
+ private IDirectStreamProvider _directStreamProvider;
public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken)
{
@@ -34,6 +39,15 @@ namespace MediaBrowser.Api.Playback.Progressive
_cancellationToken = cancellationToken;
}
+ public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken)
+ {
+ _directStreamProvider = directStreamProvider;
+ _outputHeaders = outputHeaders;
+ _job = job;
+ _logger = logger;
+ _cancellationToken = cancellationToken;
+ }
+
public IDictionary Options
{
get
@@ -42,17 +56,33 @@ namespace MediaBrowser.Api.Playback.Progressive
}
}
+ private Stream GetInputStream()
+ {
+ return _fileSystem.GetFileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true);
+ }
+
public async Task WriteToAsync(Stream outputStream)
{
try
{
+ if (_directStreamProvider != null)
+ {
+ await _directStreamProvider.CopyToAsync(outputStream, _cancellationToken).ConfigureAwait(false);
+ return;
+ }
+
var eofCount = 0;
- using (var fs = _fileSystem.GetFileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
+ using (var inputStream = GetInputStream())
{
- while (eofCount < 15)
+ if (StartPosition > 0)
{
- var bytesRead = await CopyToAsyncInternal(fs, outputStream, BufferSize, _cancellationToken).ConfigureAwait(false);
+ inputStream.Position = StartPosition;
+ }
+
+ while (eofCount < 15 || !AllowEndOfFile)
+ {
+ var bytesRead = await CopyToAsyncInternal(inputStream, outputStream, BufferSize, _cancellationToken).ConfigureAwait(false);
//var position = fs.Position;
//_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index 2e92c4a49..003599390 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -37,6 +37,7 @@ namespace MediaBrowser.Api.Playback
///
/// The log file stream.
public Stream LogFileStream { get; set; }
+ public IDirectStreamProvider DirectStreamProvider { get; set; }
public string InputContainer { get; set; }
@@ -62,7 +63,6 @@ namespace MediaBrowser.Api.Playback
get { return Request is VideoStreamRequest; }
}
public bool IsInputVideo { get; set; }
- public bool IsInputArchive { get; set; }
public VideoType VideoType { get; set; }
public IsoType? IsoType { get; set; }
@@ -88,9 +88,17 @@ namespace MediaBrowser.Api.Playback
return 10;
}
+ if (!RunTimeTicks.HasValue)
+ {
+ return 3;
+ }
return 6;
}
+ if (!RunTimeTicks.HasValue)
+ {
+ return 3;
+ }
return 3;
}
}
@@ -217,7 +225,7 @@ namespace MediaBrowser.Api.Playback
{
try
{
- await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
+ await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -508,5 +516,18 @@ namespace MediaBrowser.Api.Playback
return false;
}
}
+
+ public bool? IsTargetAVC
+ {
+ get
+ {
+ if (Request.Static)
+ {
+ return VideoStream == null ? null : VideoStream.IsAVC;
+ }
+
+ return true;
+ }
+ }
}
}
diff --git a/MediaBrowser.Api/Reports/Data/ReportBuilder.cs b/MediaBrowser.Api/Reports/Data/ReportBuilder.cs
index e4a560383..c9c63847c 100644
--- a/MediaBrowser.Api/Reports/Data/ReportBuilder.cs
+++ b/MediaBrowser.Api/Reports/Data/ReportBuilder.cs
@@ -517,10 +517,6 @@ namespace MediaBrowser.Api.Reports
internalHeader = HeaderMetadata.Album;
break;
- case HeaderMetadata.Countries:
- option.Column = (i, r) => this.GetListAsString(this.GetObject>(i, (x) => x.ProductionLocations));
- break;
-
case HeaderMetadata.Disc:
option.Column = (i, r) => i.ParentIndexNumber;
break;
diff --git a/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs b/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs
index 0da4857ac..52b095dee 100644
--- a/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs
+++ b/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs
@@ -36,7 +36,6 @@ namespace MediaBrowser.Api.Reports
result = this.GetResultStudios(result, items, topItem);
result = this.GetResultPersons(result, items, topItem);
result = this.GetResultProductionYears(result, items, topItem);
- result = this.GetResulProductionLocations(result, items, topItem);
result = this.GetResultCommunityRatings(result, items, topItem);
result = this.GetResultParentalRatings(result, items, topItem);
@@ -100,30 +99,6 @@ namespace MediaBrowser.Api.Reports
}
}
- /// Gets resul production locations.
- /// The result.
- /// The items.
- /// The top item.
- /// The resul production locations.
- private ReportStatResult GetResulProductionLocations(ReportStatResult result, BaseItem[] items, int topItem = 5)
- {
- this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.Countries), topItem,
- items.OfType()
- .Where(x => x.ProductionLocations != null)
- .SelectMany(x => x.ProductionLocations)
- .GroupBy(x => x)
- .OrderByDescending(x => x.Count())
- .Take(topItem)
- .Select(x => new ReportStatItem
- {
- Name = x.Key.ToString(),
- Value = x.Count().ToString()
- })
- );
-
- return result;
- }
-
/// Gets result community ratings.
/// The result.
/// The items.
diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs
index 1621c8056..ddcb6b7bf 100644
--- a/MediaBrowser.Api/SimilarItemsHelper.cs
+++ b/MediaBrowser.Api/SimilarItemsHelper.cs
@@ -81,7 +81,8 @@ namespace MediaBrowser.Api
var query = new InternalItemsQuery(user)
{
IncludeItemTypes = includeTypes.Select(i => i.Name).ToArray(),
- Recursive = true
+ Recursive = true,
+ DtoOptions = dtoOptions
};
// ExcludeArtistIds
diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs
index 176b497d7..d5158677c 100644
--- a/MediaBrowser.Api/StartupWizardService.cs
+++ b/MediaBrowser.Api/StartupWizardService.cs
@@ -88,8 +88,6 @@ namespace MediaBrowser.Api
var result = new StartupConfiguration
{
UICulture = _config.Configuration.UICulture,
- EnableInternetProviders = _config.Configuration.EnableInternetProviders,
- SaveLocalMeta = _config.Configuration.SaveLocalMeta,
MetadataCountryCode = _config.Configuration.MetadataCountryCode,
PreferredMetadataLanguage = _config.Configuration.PreferredMetadataLanguage
};
@@ -116,15 +114,16 @@ namespace MediaBrowser.Api
config.EnableLocalizedGuids = true;
config.EnableStandaloneMusicKeys = true;
config.EnableCaseSensitiveItemIds = true;
- //config.EnableFolderView = true;
+ config.EnableFolderView = true;
config.SchemaVersion = 109;
+ config.EnableSimpleArtistDetection = true;
+ config.SkipDeserializationForBasicTypes = true;
+ config.SkipDeserializationForPrograms = true;
}
public void Post(UpdateStartupConfiguration request)
{
_config.Configuration.UICulture = request.UICulture;
- _config.Configuration.EnableInternetProviders = request.EnableInternetProviders;
- _config.Configuration.SaveLocalMeta = request.SaveLocalMeta;
_config.Configuration.MetadataCountryCode = request.MetadataCountryCode;
_config.Configuration.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
_config.SaveConfiguration();
@@ -215,8 +214,6 @@ namespace MediaBrowser.Api
public class StartupConfiguration
{
public string UICulture { get; set; }
- public bool EnableInternetProviders { get; set; }
- public bool SaveLocalMeta { get; set; }
public string MetadataCountryCode { get; set; }
public string PreferredMetadataLanguage { get; set; }
public string LiveTvTunerType { get; set; }
diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs
index fe13e8b21..b07a31a87 100644
--- a/MediaBrowser.Api/Subtitles/SubtitleService.cs
+++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs
@@ -148,7 +148,7 @@ namespace MediaBrowser.Api.Subtitles
{
var item = (Video)_libraryManager.GetItemById(new Guid(request.Id));
- var mediaSource = await _mediaSourceManager.GetMediaSource(item, request.MediaSourceId, false).ConfigureAwait(false);
+ var mediaSource = await _mediaSourceManager.GetMediaSource(item, request.MediaSourceId, null, false, CancellationToken.None).ConfigureAwait(false);
var builder = new StringBuilder();
diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs
index a0d69317c..ef93d2fc9 100644
--- a/MediaBrowser.Api/TvShowsService.cs
+++ b/MediaBrowser.Api/TvShowsService.cs
@@ -302,6 +302,8 @@ namespace MediaBrowser.Api
(!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
+ var dtoOptions = GetDtoOptions(request);
+
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
Limit = request.Limit,
@@ -309,12 +311,11 @@ namespace MediaBrowser.Api
{
typeof(Series).Name
},
- SimilarTo = item
+ SimilarTo = item,
+ DtoOptions = dtoOptions
}).ToList();
- var dtoOptions = GetDtoOptions(request);
-
var result = new QueryResult
{
Items = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false)).ToArray(),
@@ -333,6 +334,8 @@ namespace MediaBrowser.Api
var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
+ var options = GetDtoOptions(request);
+
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(Episode).Name },
@@ -342,12 +345,11 @@ namespace MediaBrowser.Api
StartIndex = request.StartIndex,
Limit = request.Limit,
ParentId = parentIdGuid,
- Recursive = true
+ Recursive = true,
+ DtoOptions = options
}).ToList();
- var options = GetDtoOptions(request);
-
var returnItems = (await _dtoService.GetBaseItemDtos(itemsResult, options, user).ConfigureAwait(false)).ToArray();
var result = new ItemsResult
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
index 5381f9004..182a92fc8 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
@@ -205,6 +205,7 @@ namespace MediaBrowser.Api.UserLibrary
private void SetItemCounts(BaseItemDto dto, ItemCounts counts)
{
dto.ChildCount = counts.ItemCount;
+ dto.ProgramCount = counts.ProgramCount;
dto.SeriesCount = counts.SeriesCount;
dto.EpisodeCount = counts.EpisodeCount;
dto.MovieCount = counts.MovieCount;
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index 681624ba2..04395da3c 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -99,8 +99,10 @@ namespace MediaBrowser.Api.UserLibrary
private async Task GetItems(GetItems request)
{
var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
-
- var result = await GetQueryResult(request, user).ConfigureAwait(false);
+
+ var dtoOptions = GetDtoOptions(request);
+
+ var result = await GetQueryResult(request, dtoOptions, user).ConfigureAwait(false);
if (result == null)
{
@@ -112,8 +114,6 @@ namespace MediaBrowser.Api.UserLibrary
throw new InvalidOperationException("GetItemsToSerialize result.Items returned null");
}
- var dtoOptions = GetDtoOptions(request);
-
var dtoList = await _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user).ConfigureAwait(false);
if (dtoList == null)
@@ -131,24 +131,31 @@ namespace MediaBrowser.Api.UserLibrary
///
/// Gets the items to serialize.
///
- /// The request.
- /// The user.
- /// IEnumerable{BaseItem}.
- private async Task> GetQueryResult(GetItems request, User user)
+ private async Task> GetQueryResult(GetItems request, DtoOptions dtoOptions, User user)
{
var item = string.IsNullOrEmpty(request.ParentId) ?
- user == null ? _libraryManager.RootFolder : user.RootFolder :
+ null :
_libraryManager.GetItemById(request.ParentId);
if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase))
{
- //item = user == null ? _libraryManager.RootFolder : user.RootFolder;
+ if (item == null || user != null)
+ {
+ item = _libraryManager.RootFolder.Children.OfType().FirstOrDefault(i => string.Equals(i.GetType().Name, "PlaylistsFolder", StringComparison.OrdinalIgnoreCase));
+ }
}
else if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase))
{
item = user == null ? _libraryManager.RootFolder : user.RootFolder;
}
+ if (item == null)
+ {
+ item = string.IsNullOrEmpty(request.ParentId) ?
+ user == null ? _libraryManager.RootFolder : user.RootFolder :
+ _libraryManager.GetItemById(request.ParentId);
+ }
+
// Default list type = children
var folder = item as Folder;
@@ -159,14 +166,14 @@ namespace MediaBrowser.Api.UserLibrary
if (request.Recursive || !string.IsNullOrEmpty(request.Ids) || user == null)
{
- return await folder.GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
+ return await folder.GetItems(GetItemsQuery(request, dtoOptions, user)).ConfigureAwait(false);
}
var userRoot = item as UserRootFolder;
if (userRoot == null)
{
- return await folder.GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
+ return await folder.GetItems(GetItemsQuery(request, dtoOptions, user)).ConfigureAwait(false);
}
IEnumerable items = folder.GetChildren(user, true);
@@ -180,7 +187,7 @@ namespace MediaBrowser.Api.UserLibrary
};
}
- private InternalItemsQuery GetItemsQuery(GetItems request, User user)
+ private InternalItemsQuery GetItemsQuery(GetItems request, DtoOptions dtoOptions, User user)
{
var query = new InternalItemsQuery(user)
{
@@ -241,7 +248,8 @@ namespace MediaBrowser.Api.UserLibrary
AiredDuringSeason = request.AiredDuringSeason,
AlbumArtistStartsWithOrGreater = request.AlbumArtistStartsWithOrGreater,
EnableTotalRecordCount = request.EnableTotalRecordCount,
- ExcludeItemIds = request.GetExcludeItemIds()
+ ExcludeItemIds = request.GetExcludeItemIds(),
+ DtoOptions = dtoOptions
};
if (!string.IsNullOrWhiteSpace(request.Ids))
diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
index baf5afc1b..4099c9c56 100644
--- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
+++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
@@ -30,6 +30,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
+using MediaBrowser.Common.IO;
namespace MediaBrowser.Common.Implementations
{
@@ -192,6 +193,8 @@ namespace MediaBrowser.Common.Implementations
get { return Environment.OSVersion.VersionString; }
}
+ public IMemoryStreamProvider MemoryStreamProvider { get; set; }
+
///
/// Initializes a new instance of the class.
///
@@ -231,6 +234,8 @@ namespace MediaBrowser.Common.Implementations
JsonSerializer = CreateJsonSerializer();
+ MemoryStreamProvider = new MemoryStreamProvider();
+
OnLoggerLoaded(true);
LogManager.LoggerLoaded += (s, e) => OnLoggerLoaded(false);
@@ -456,6 +461,7 @@ namespace MediaBrowser.Common.Implementations
RegisterSingleInstance(JsonSerializer);
RegisterSingleInstance(XmlSerializer);
+ RegisterSingleInstance(MemoryStreamProvider);
RegisterSingleInstance(LogManager);
RegisterSingleInstance(Logger);
@@ -464,7 +470,7 @@ namespace MediaBrowser.Common.Implementations
RegisterSingleInstance(FileSystemManager);
- HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager);
+ HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamProvider);
RegisterSingleInstance(HttpClient);
NetworkManager = CreateNetworkManager(LogManager.GetLogger("NetworkManager"));
diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
index 371757f6c..eec18e985 100644
--- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
@@ -42,6 +42,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
+ private readonly IMemoryStreamProvider _memoryStreamProvider;
///
/// Initializes a new instance of the class.
@@ -52,7 +53,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
/// appPaths
/// or
/// logger
- public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem)
+ public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem, IMemoryStreamProvider memoryStreamProvider)
{
if (appPaths == null)
{
@@ -65,6 +66,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
_logger = logger;
_fileSystem = fileSystem;
+ _memoryStreamProvider = memoryStreamProvider;
_appPaths = appPaths;
// http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c
@@ -269,6 +271,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
Url = url,
ResourcePool = resourcePool,
CancellationToken = cancellationToken,
+ BufferContent = resourcePool != null
});
}
@@ -293,12 +296,9 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
///
public async Task SendAsync(HttpRequestOptions options, string httpMethod)
{
- HttpResponseInfo response;
-
if (options.CacheMode == CacheMode.None)
{
- response = await SendAsyncInternal(options, httpMethod).ConfigureAwait(false);
- return response;
+ return await SendAsyncInternal(options, httpMethod).ConfigureAwait(false);
}
var url = options.Url;
@@ -306,7 +306,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);
- response = await GetCachedResponse(responseCachePath, options.CacheLength, url).ConfigureAwait(false);
+ var response = await GetCachedResponse(responseCachePath, options.CacheLength, url).ConfigureAwait(false);
if (response != null)
{
return response;
@@ -332,7 +332,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
{
using (var stream = _fileSystem.GetFileStream(responseCachePath, FileMode.Open, FileAccess.Read, FileShare.Read, true))
{
- var memoryStream = new MemoryStream();
+ var memoryStream = _memoryStreamProvider.CreateNew();
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0;
@@ -366,7 +366,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
using (var responseStream = response.Content)
{
- var memoryStream = new MemoryStream();
+ var memoryStream = _memoryStreamProvider.CreateNew();
await responseStream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0;
@@ -458,7 +458,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
using (var stream = httpResponse.GetResponseStream())
{
- var memoryStream = new MemoryStream();
+ var memoryStream = _memoryStreamProvider.CreateNew();
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
@@ -553,7 +553,8 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
{
Url = url,
ResourcePool = resourcePool,
- CancellationToken = cancellationToken
+ CancellationToken = cancellationToken,
+ BufferContent = resourcePool != null
}, postData);
}
@@ -563,7 +564,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
///
/// The options.
/// Task{System.String}.
- /// progress
public async Task GetTempFile(HttpRequestOptions options)
{
var response = await GetTempFileResponse(options).ConfigureAwait(false);
diff --git a/MediaBrowser.Common.Implementations/IO/MemoryStreamProvider.cs b/MediaBrowser.Common.Implementations/IO/MemoryStreamProvider.cs
new file mode 100644
index 000000000..364055283
--- /dev/null
+++ b/MediaBrowser.Common.Implementations/IO/MemoryStreamProvider.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using Microsoft.IO;
+
+namespace MediaBrowser.Common.Implementations.IO
+{
+ public class MemoryStreamProvider : IMemoryStreamProvider
+ {
+ readonly RecyclableMemoryStreamManager _manager = new RecyclableMemoryStreamManager();
+
+ public MemoryStream CreateNew()
+ {
+ return _manager.GetStream();
+ }
+
+ public MemoryStream CreateNew(int capacity)
+ {
+ return _manager.GetStream("RecyclableMemoryStream", capacity);
+ }
+
+ public MemoryStream CreateNew(byte[] buffer)
+ {
+ return _manager.GetStream("RecyclableMemoryStream", buffer, 0, buffer.Length);
+ }
+ }
+}
diff --git a/MediaBrowser.Common.Implementations/Logging/NLogger.cs b/MediaBrowser.Common.Implementations/Logging/NLogger.cs
index 29b618890..97bc437a0 100644
--- a/MediaBrowser.Common.Implementations/Logging/NLogger.cs
+++ b/MediaBrowser.Common.Implementations/Logging/NLogger.cs
@@ -72,6 +72,11 @@ namespace MediaBrowser.Common.Implementations.Logging
/// The param list.
public void Debug(string message, params object[] paramList)
{
+ if (_logManager.LogSeverity == LogSeverity.Info)
+ {
+ return;
+ }
+
_logger.Debug(message, paramList);
}
@@ -137,6 +142,11 @@ namespace MediaBrowser.Common.Implementations.Logging
/// Content of the additional.
public void LogMultiline(string message, LogSeverity severity, StringBuilder additionalContent)
{
+ if (severity == LogSeverity.Debug && _logManager.LogSeverity == LogSeverity.Info)
+ {
+ return;
+ }
+
additionalContent.Insert(0, message + Environment.NewLine);
const char tabChar = '\t';
diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
index ced2dd5a3..f3444f01b 100644
--- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
+++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
@@ -1,5 +1,5 @@
-
+
Debug
@@ -14,6 +14,7 @@
10.0.0
2.0
v4.5
+
true
@@ -23,7 +24,7 @@
DEBUG;TRACE
prompt
4
- v4.5
+ v4.5.1
none
@@ -51,11 +52,15 @@
False
..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll
+
+ ..\packages\Microsoft.IO.RecyclableMemoryStream.1.1.0.0\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll
+ True
+
..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll
- ..\packages\NLog.4.3.6\lib\net45\NLog.dll
+ ..\packages\NLog.4.3.8\lib\net45\NLog.dll
True
@@ -65,8 +70,8 @@
False
..\ThirdParty\SharpCompress\SharpCompress.dll
-
- ..\packages\SimpleInjector.3.2.0\lib\net45\SimpleInjector.dll
+
+ ..\packages\SimpleInjector.3.2.2\lib\net45\SimpleInjector.dll
True
@@ -75,6 +80,9 @@
+
+ ..\ThirdParty\fastjsonparser\System.Text.Json.dll
+
..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll
@@ -93,6 +101,7 @@
+
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index e6b6bc2e3..ced85780f 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -390,13 +390,13 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
try
{
- var localTask = ScheduledTask.Execute(CurrentCancellationTokenSource.Token, progress);
-
if (options != null && options.MaxRuntimeMs.HasValue)
{
CurrentCancellationTokenSource.CancelAfter(options.MaxRuntimeMs.Value);
}
+ var localTask = ScheduledTask.Execute(CurrentCancellationTokenSource.Token, progress);
+
await localTask.ConfigureAwait(false);
status = TaskCompletionStatus.Completed;
diff --git a/MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs b/MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs
index 10c0f8fc9..5d440609e 100644
--- a/MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs
+++ b/MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs
@@ -169,7 +169,8 @@ namespace MediaBrowser.Common.Implementations.Security
var options = new HttpRequestOptions()
{
Url = AppstoreRegUrl,
- CancellationToken = CancellationToken.None
+ CancellationToken = CancellationToken.None,
+ BufferContent = false
};
options.RequestHeaders.Add("X-Emby-Token", _appHost.SystemId);
options.RequestContent = parameters;
@@ -269,7 +270,8 @@ namespace MediaBrowser.Common.Implementations.Security
Url = MBValidateUrl,
// Seeing block length errors
- EnableHttpCompression = false
+ EnableHttpCompression = false,
+ BufferContent = false
};
options.SetPostData(data);
diff --git a/MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs b/MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs
index 84c08439e..371c2ea11 100644
--- a/MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs
+++ b/MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs
@@ -30,7 +30,8 @@ namespace MediaBrowser.Common.Implementations.Updates
Url = url,
EnableKeepAlive = false,
CancellationToken = cancellationToken,
- UserAgent = "Emby/3.0"
+ UserAgent = "Emby/3.0",
+ BufferContent = false
};
if (cacheLength.Ticks > 0)
@@ -105,7 +106,8 @@ namespace MediaBrowser.Common.Implementations.Updates
Url = url,
EnableKeepAlive = false,
CancellationToken = cancellationToken,
- UserAgent = "Emby/3.0"
+ UserAgent = "Emby/3.0",
+ BufferContent = false
};
using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
diff --git a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
index 8c7646209..9674199fe 100644
--- a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
+++ b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
@@ -148,14 +148,10 @@ namespace MediaBrowser.Common.Implementations.Updates
///
/// Gets all available packages.
///
- /// The cancellation token.
- /// if set to true [with registration].
- /// Type of the package.
- /// The application version.
/// Task{List{PackageInfo}}.
public async Task> GetAvailablePackages(CancellationToken cancellationToken,
bool withRegistration = true,
- PackageType? packageType = null,
+ string packageType = null,
Version applicationVersion = null)
{
var data = new Dictionary
@@ -293,7 +289,7 @@ namespace MediaBrowser.Common.Implementations.Updates
return packages;
}
- protected IEnumerable FilterPackages(List packages, PackageType? packageType, Version applicationVersion)
+ protected IEnumerable FilterPackages(List packages, string packageType, Version applicationVersion)
{
foreach (var package in packages)
{
@@ -301,9 +297,9 @@ namespace MediaBrowser.Common.Implementations.Updates
.OrderByDescending(GetPackageVersion).ToList();
}
- if (packageType.HasValue)
+ if (!string.IsNullOrWhiteSpace(packageType))
{
- packages = packages.Where(p => p.type == packageType.Value).ToList();
+ packages = packages.Where(p => string.Equals(p.type, packageType, StringComparison.OrdinalIgnoreCase)).ToList();
}
// If an app version was supplied, filter the versions for each package to only include supported versions
diff --git a/MediaBrowser.Common.Implementations/packages.config b/MediaBrowser.Common.Implementations/packages.config
index 594b4c7c5..40c727a06 100644
--- a/MediaBrowser.Common.Implementations/packages.config
+++ b/MediaBrowser.Common.Implementations/packages.config
@@ -1,8 +1,9 @@
+
-
+
-
+
\ No newline at end of file
diff --git a/MediaBrowser.Common/IO/IMemoryStreamProvider.cs b/MediaBrowser.Common/IO/IMemoryStreamProvider.cs
new file mode 100644
index 000000000..1c0e98018
--- /dev/null
+++ b/MediaBrowser.Common/IO/IMemoryStreamProvider.cs
@@ -0,0 +1,11 @@
+using System.IO;
+
+namespace MediaBrowser.Common.IO
+{
+ public interface IMemoryStreamProvider
+ {
+ MemoryStream CreateNew();
+ MemoryStream CreateNew(int capacity);
+ MemoryStream CreateNew(byte[] buffer);
+ }
+}
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index 17f211d84..a46aaf9f7 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -60,6 +60,7 @@
+
diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs
index 68853f05e..f7a202f97 100644
--- a/MediaBrowser.Common/Updates/IInstallationManager.cs
+++ b/MediaBrowser.Common/Updates/IInstallationManager.cs
@@ -51,7 +51,7 @@ namespace MediaBrowser.Common.Updates
/// Task{List{PackageInfo}}.
Task> GetAvailablePackages(CancellationToken cancellationToken,
bool withRegistration = true,
- PackageType? packageType = null,
+ string packageType = null,
Version applicationVersion = null);
///
diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs
index 3c46247a7..9177e2d81 100644
--- a/MediaBrowser.Controller/Channels/IChannelManager.cs
+++ b/MediaBrowser.Controller/Channels/IChannelManager.cs
@@ -134,15 +134,5 @@ namespace MediaBrowser.Controller.Channels
/// The cancellation token.
/// BaseItemDto.
Task GetChannelFolder(string userId, CancellationToken cancellationToken);
-
- ///
- /// Downloads the channel item.
- ///
- /// The item.
- /// The destination path.
- /// The progress.
- /// The cancellation token.
- /// Task.
- Task DownloadChannelItem(BaseItem item, string destinationPath, IProgress progress, CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Controller/Chapters/ChapterResponse.cs b/MediaBrowser.Controller/Chapters/ChapterResponse.cs
deleted file mode 100644
index 3c1b8ed07..000000000
--- a/MediaBrowser.Controller/Chapters/ChapterResponse.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using MediaBrowser.Model.Chapters;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Chapters
-{
- public class ChapterResponse
- {
- ///
- /// Gets or sets the chapters.
- ///
- /// The chapters.
- public List Chapters { get; set; }
-
- public ChapterResponse()
- {
- Chapters = new List();
- }
- }
-}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Chapters/ChapterSearchRequest.cs b/MediaBrowser.Controller/Chapters/ChapterSearchRequest.cs
deleted file mode 100644
index 982dc35bb..000000000
--- a/MediaBrowser.Controller/Chapters/ChapterSearchRequest.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Chapters
-{
- public class ChapterSearchRequest : IHasProviderIds
- {
- public string Language { get; set; }
-
- public VideoContentType ContentType { get; set; }
-
- public string MediaPath { get; set; }
- public string SeriesName { get; set; }
- public string Name { get; set; }
- public int? IndexNumber { get; set; }
- public int? IndexNumberEnd { get; set; }
- public int? ParentIndexNumber { get; set; }
- public int? ProductionYear { get; set; }
- public long? RuntimeTicks { get; set; }
- public Dictionary ProviderIds { get; set; }
-
- public bool SearchAllProviders { get; set; }
-
- public ChapterSearchRequest()
- {
- ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase);
- }
- }
-}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Chapters/IChapterManager.cs b/MediaBrowser.Controller/Chapters/IChapterManager.cs
index 27e06fb8d..4b39e66cc 100644
--- a/MediaBrowser.Controller/Chapters/IChapterManager.cs
+++ b/MediaBrowser.Controller/Chapters/IChapterManager.cs
@@ -1,6 +1,4 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Chapters;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Configuration;
@@ -13,12 +11,6 @@ namespace MediaBrowser.Controller.Chapters
///
public interface IChapterManager
{
- ///
- /// Adds the parts.
- ///
- /// The chapter providers.
- void AddParts(IEnumerable chapterProviders);
-
///
/// Gets the chapters.
///
@@ -35,43 +27,6 @@ namespace MediaBrowser.Controller.Chapters
/// Task.
Task SaveChapters(string itemId, List chapters, CancellationToken cancellationToken);
- ///
- /// Searches the specified video.
- ///
- /// The video.
- /// The cancellation token.
- /// Task{IEnumerable{RemoteChapterResult}}.
- Task> Search(Video video, CancellationToken cancellationToken);
-
- ///
- /// Searches the specified request.
- ///
- /// The request.
- /// The cancellation token.
- /// Task{IEnumerable{RemoteChapterResult}}.
- Task> Search(ChapterSearchRequest request, CancellationToken cancellationToken);
-
- ///
- /// Gets the chapters.
- ///
- /// The identifier.
- /// The cancellation token.
- /// Task{ChapterResponse}.
- Task GetChapters(string id, CancellationToken cancellationToken);
-
- ///
- /// Gets the providers.
- ///
- /// The item identifier.
- /// IEnumerable{ChapterProviderInfo}.
- IEnumerable GetProviders(string itemId);
-
- ///
- /// Gets the providers.
- ///
- /// IEnumerable{ChapterProviderInfo}.
- IEnumerable GetProviders();
-
///
/// Gets the configuration.
///
diff --git a/MediaBrowser.Controller/Chapters/IChapterProvider.cs b/MediaBrowser.Controller/Chapters/IChapterProvider.cs
deleted file mode 100644
index a7505347b..000000000
--- a/MediaBrowser.Controller/Chapters/IChapterProvider.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Chapters;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Chapters
-{
- public interface IChapterProvider
- {
- ///
- /// Gets the name.
- ///
- /// The name.
- string Name { get; }
-
- ///
- /// Gets the supported media types.
- ///
- /// The supported media types.
- IEnumerable SupportedMediaTypes { get; }
-
- ///
- /// Searches the specified request.
- ///
- /// The request.
- /// The cancellation token.
- /// Task{IEnumerable{RemoteChapterResult}}.
- Task> Search(ChapterSearchRequest request, CancellationToken cancellationToken);
-
- ///
- /// Gets the chapters.
- ///
- /// The identifier.
- /// The cancellation token.
- /// Task{ChapterResponse}.
- Task GetChapters(string id, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Dlna/IDeviceDiscovery.cs b/MediaBrowser.Controller/Dlna/IDeviceDiscovery.cs
index e8083b363..d2c5b9e4e 100644
--- a/MediaBrowser.Controller/Dlna/IDeviceDiscovery.cs
+++ b/MediaBrowser.Controller/Dlna/IDeviceDiscovery.cs
@@ -1,10 +1,20 @@
using System;
+using System.Collections.Generic;
+using System.Net;
+using MediaBrowser.Model.Events;
namespace MediaBrowser.Controller.Dlna
{
public interface IDeviceDiscovery
{
- event EventHandler DeviceDiscovered;
- event EventHandler DeviceLeft;
+ event EventHandler> DeviceDiscovered;
+ event EventHandler> DeviceLeft;
+ }
+
+ public class UpnpDeviceInfo
+ {
+ public Uri Location { get; set; }
+ public Dictionary Headers { get; set; }
+ public IPEndPoint LocalEndPoint { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Dlna/ISsdpHandler.cs b/MediaBrowser.Controller/Dlna/ISsdpHandler.cs
index e4126ddcf..ec3a00aad 100644
--- a/MediaBrowser.Controller/Dlna/ISsdpHandler.cs
+++ b/MediaBrowser.Controller/Dlna/ISsdpHandler.cs
@@ -4,6 +4,5 @@ namespace MediaBrowser.Controller.Dlna
{
public interface ISsdpHandler
{
- event EventHandler MessageReceived;
}
}
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index efc450248..7b769deb3 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -34,11 +34,26 @@ namespace MediaBrowser.Controller.Entities
}
}
+ [IgnoreDataMember]
+ public override bool IsPhysicalRoot
+ {
+ get { return true; }
+ }
+
public override bool CanDelete()
{
return false;
}
+ [IgnoreDataMember]
+ public override bool SupportsPlayedStatus
+ {
+ get
+ {
+ return false;
+ }
+ }
+
///
/// The _virtual children
///
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index 1af55a389..56c3248f3 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -23,8 +23,7 @@ namespace MediaBrowser.Controller.Entities.Audio
IHasMusicGenres,
IHasLookupInfo,
IHasMediaSources,
- IThemeMedia,
- IArchivable
+ IThemeMedia
{
public List ChannelMediaSources { get; set; }
@@ -60,10 +59,19 @@ namespace MediaBrowser.Controller.Entities.Audio
AlbumArtists = new List();
}
+ [IgnoreDataMember]
+ public override bool SupportsPlayedStatus
+ {
+ get
+ {
+ return true;
+ }
+ }
+
[IgnoreDataMember]
public override bool SupportsAddingToPlaylist
{
- get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; }
+ get { return true; }
}
[IgnoreDataMember]
@@ -84,21 +92,6 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
- [IgnoreDataMember]
- public bool IsArchive
- {
- get
- {
- if (string.IsNullOrWhiteSpace(Path))
- {
- return false;
- }
- var ext = System.IO.Path.GetExtension(Path) ?? string.Empty;
-
- return new[] { ".zip", ".rar", ".7z" }.Contains(ext, StringComparer.OrdinalIgnoreCase);
- }
- }
-
public override bool CanDownload()
{
var locationType = LocationType;
@@ -262,7 +255,7 @@ namespace MediaBrowser.Controller.Entities.Audio
Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File,
MediaStreams = MediaSourceManager.GetMediaStreams(i.Id).ToList(),
Name = i.Name,
- Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path,
+ Path = enablePathSubstituion ? GetMappedPath(i, i.Path, locationType) : i.Path,
RunTimeTicks = i.RunTimeTicks,
Container = i.Container,
Size = i.Size
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index 1f3b0c92a..3f457d89a 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -17,6 +17,9 @@ namespace MediaBrowser.Controller.Entities.Audio
///
public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo, IMetadataContainer
{
+ public List AlbumArtists { get; set; }
+ public List Artists { get; set; }
+
public MusicAlbum()
{
Artists = new List();
@@ -48,6 +51,15 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
+ [IgnoreDataMember]
+ public override bool SupportsPlayedStatus
+ {
+ get
+ {
+ return false;
+ }
+ }
+
[IgnoreDataMember]
public override bool SupportsCumulativeRunTimeTicks
{
@@ -83,8 +95,6 @@ namespace MediaBrowser.Controller.Entities.Audio
get { return false; }
}
- public List AlbumArtists { get; set; }
-
///
/// Gets the tracks.
///
@@ -103,8 +113,6 @@ namespace MediaBrowser.Controller.Entities.Audio
return Tracks;
}
- public List Artists { get; set; }
-
public override List GetUserDataKeys()
{
var list = base.GetUserDataKeys();
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 81d1deaa2..a31f04fc4 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -16,7 +16,7 @@ namespace MediaBrowser.Controller.Entities.Audio
///
/// Class MusicArtist
///
- public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasProductionLocations, IHasLookupInfo
+ public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasLookupInfo
{
[IgnoreDataMember]
public bool IsAccessedByName
@@ -24,8 +24,6 @@ namespace MediaBrowser.Controller.Entities.Audio
get { return ParentId == Guid.Empty; }
}
- public List ProductionLocations { get; set; }
-
[IgnoreDataMember]
public override bool IsFolder
{
@@ -50,6 +48,15 @@ namespace MediaBrowser.Controller.Entities.Audio
get { return true; }
}
+ [IgnoreDataMember]
+ public override bool SupportsPlayedStatus
+ {
+ get
+ {
+ return false;
+ }
+ }
+
public override bool CanDelete()
{
return !IsAccessedByName;
@@ -111,11 +118,6 @@ namespace MediaBrowser.Controller.Entities.Audio
return base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService);
}
- public MusicArtist()
- {
- ProductionLocations = new List();
- }
-
public override List GetUserDataKeys()
{
var list = base.GetUserDataKeys();
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 55aaf04ff..7eb84fc80 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -37,6 +37,8 @@ namespace MediaBrowser.Controller.Entities
{
protected BaseItem()
{
+ ThemeSongIds = new List();
+ ThemeVideoIds = new List();
Keywords = new List();
Tags = new List();
Genres = new List();
@@ -44,6 +46,8 @@ namespace MediaBrowser.Controller.Entities
ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase);
LockedFields = new List();
ImageInfos = new List();
+ InheritedTags = new List();
+ ProductionLocations = new List();
}
public static readonly char[] SlugReplaceChars = { '?', '/', '&' };
@@ -64,6 +68,9 @@ namespace MediaBrowser.Controller.Entities
public static string ThemeSongFilename = "theme";
public static string ThemeVideosFolderName = "backdrops";
+ public List ThemeSongIds { get; set; }
+ public List ThemeVideoIds { get; set; }
+
[IgnoreDataMember]
public string PreferredMetadataCountryCode { get; set; }
[IgnoreDataMember]
@@ -72,6 +79,8 @@ namespace MediaBrowser.Controller.Entities
public long? Size { get; set; }
public string Container { get; set; }
public string ShortOverview { get; set; }
+ [IgnoreDataMember]
+ public string Tagline { get; set; }
public List ImageInfos { get; set; }
@@ -114,6 +123,31 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public bool IsInMixedFolder { get; set; }
+ [IgnoreDataMember]
+ protected virtual bool SupportsIsInMixedFolderDetection
+ {
+ get { return false; }
+ }
+
+ [IgnoreDataMember]
+ public virtual bool SupportsPlayedStatus
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public bool DetectIsInMixedFolder()
+ {
+ if (SupportsIsInMixedFolderDetection)
+ {
+
+ }
+
+ return IsInMixedFolder;
+ }
+
[IgnoreDataMember]
public virtual bool SupportsRemoteImageDownloading
{
@@ -254,6 +288,19 @@ namespace MediaBrowser.Controller.Entities
}
}
+ [IgnoreDataMember]
+ public string ExternalSeriesId { get; set; }
+
+ [IgnoreDataMember]
+ public string ExternalSeriesIdLegacy
+ {
+ get { return this.GetProviderId("ProviderExternalSeriesId"); }
+ set
+ {
+ this.SetProviderId("ProviderExternalSeriesId", value);
+ }
+ }
+
///
/// Gets or sets the etag.
///
@@ -408,7 +455,7 @@ namespace MediaBrowser.Controller.Entities
public virtual bool IsInternetMetadataEnabled()
{
- return ConfigurationManager.Configuration.EnableInternetProviders;
+ return LibraryManager.GetLibraryOptions(this).EnableInternetProviders;
}
public virtual bool CanDelete()
@@ -784,6 +831,9 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public int InheritedParentalRatingValue { get; set; }
+ [IgnoreDataMember]
+ public List InheritedTags { get; set; }
+
///
/// Gets or sets the critic rating.
///
@@ -841,6 +891,7 @@ namespace MediaBrowser.Controller.Entities
public List Tags { get; set; }
public List Keywords { get; set; }
+ public List ProductionLocations { get; set; }
///
/// Gets or sets the home page URL.
@@ -956,7 +1007,7 @@ namespace MediaBrowser.Controller.Entities
/// Loads the theme songs.
///
/// List{Audio.Audio}.
- private IEnumerable LoadThemeSongs(List fileSystemChildren, IDirectoryService directoryService)
+ private static IEnumerable LoadThemeSongs(List fileSystemChildren, IDirectoryService directoryService)
{
var files = fileSystemChildren.Where(i => i.IsDirectory)
.Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
@@ -992,7 +1043,7 @@ namespace MediaBrowser.Controller.Entities
/// Loads the video backdrops.
///
/// List{Video}.
- private IEnumerable