Merge pull request #28 from MediaBrowser/dev

Dev
pull/702/head
hatharry 8 years ago committed by GitHub
commit 9b0ac4bde5

@ -53,7 +53,11 @@ namespace Emby.Drawing.ImageMagick
"arw", "arw",
"webp", "webp",
"gif", "gif",
"bmp" "bmp",
"erf",
"raf",
"rw2",
"nrw"
}; };
} }
} }

@ -829,18 +829,7 @@ namespace Emby.Drawing
// Run the enhancers sequentially in order of priority // Run the enhancers sequentially in order of priority
foreach (var enhancer in imageEnhancers) foreach (var enhancer in imageEnhancers)
{ {
var typeName = enhancer.GetType().Name; await enhancer.EnhanceImageAsync(item, inputPath, outputPath, imageType, imageIndex).ConfigureAwait(false);
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;
}
// Feed the output into the next enhancer as input // Feed the output into the next enhancer as input
inputPath = outputPath; inputPath = outputPath;

@ -8,6 +8,7 @@ using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@ -15,6 +16,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommonIO; using CommonIO;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Api namespace MediaBrowser.Api
{ {
@ -43,7 +45,13 @@ namespace MediaBrowser.Api
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1); /// <summary>
/// The active transcoding jobs
/// </summary>
private readonly List<TranscodingJob> _activeTranscodingJobs = new List<TranscodingJob>();
private readonly Dictionary<string, SemaphoreSlim> _transcodingLocks =
new Dictionary<string, SemaphoreSlim>();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ApiEntryPoint" /> class. /// Initializes a new instance of the <see cref="ApiEntryPoint" /> class.
@ -66,6 +74,21 @@ namespace MediaBrowser.Api
_sessionManager.PlaybackStart += _sessionManager_PlaybackStart; _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) private void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
{ {
if (!string.IsNullOrWhiteSpace(e.PlaySessionId)) if (!string.IsNullOrWhiteSpace(e.PlaySessionId))
@ -147,11 +170,6 @@ namespace MediaBrowser.Api
} }
} }
/// <summary>
/// The active transcoding jobs
/// </summary>
private readonly List<TranscodingJob> _activeTranscodingJobs = new List<TranscodingJob>();
/// <summary> /// <summary>
/// Called when [transcode beginning]. /// Called when [transcode beginning].
/// </summary> /// </summary>
@ -187,7 +205,8 @@ namespace MediaBrowser.Api
CancellationTokenSource = cancellationTokenSource, CancellationTokenSource = cancellationTokenSource,
Id = transcodingJobId, Id = transcodingJobId,
PlaySessionId = playSessionId, PlaySessionId = playSessionId,
LiveStreamId = liveStreamId LiveStreamId = liveStreamId,
MediaSource = state.MediaSource
}; };
_activeTranscodingJobs.Add(job); _activeTranscodingJobs.Add(job);
@ -256,6 +275,11 @@ namespace MediaBrowser.Api
} }
} }
lock (_transcodingLocks)
{
_transcodingLocks.Remove(path);
}
if (!string.IsNullOrWhiteSpace(state.Request.DeviceId)) if (!string.IsNullOrWhiteSpace(state.Request.DeviceId))
{ {
_sessionManager.ClearTranscodingInfo(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));
}
}
/// <summary> /// <summary>
/// Called when [transcode begin request]. /// Called when [transcode begin request].
/// </summary> /// </summary>
@ -487,6 +519,11 @@ namespace MediaBrowser.Api
} }
} }
lock (_transcodingLocks)
{
_transcodingLocks.Remove(job.Path);
}
lock (job.ProcessLock) lock (job.ProcessLock)
{ {
if (job.TranscodingThrottler != null) if (job.TranscodingThrottler != null)
@ -530,7 +567,7 @@ namespace MediaBrowser.Api
{ {
try try
{ {
await _mediaSourceManager.CloseLiveStream(job.LiveStreamId, CancellationToken.None).ConfigureAwait(false); await _mediaSourceManager.CloseLiveStream(job.LiveStreamId).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -656,6 +693,7 @@ namespace MediaBrowser.Api
/// Gets or sets the path. /// Gets or sets the path.
/// </summary> /// </summary>
/// <value>The path.</value> /// <value>The path.</value>
public MediaSourceInfo MediaSource { get; set; }
public string Path { get; set; } public string Path { get; set; }
/// <summary> /// <summary>
/// Gets or sets the type. /// Gets or sets the type.
@ -751,12 +789,12 @@ namespace MediaBrowser.Api
{ {
if (KillTimer == null) 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); KillTimer = new Timer(callback, this, intervalMs, Timeout.Infinite);
} }
else 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); KillTimer.Change(intervalMs, Timeout.Infinite);
} }
} }
@ -775,7 +813,7 @@ namespace MediaBrowser.Api
{ {
var intervalMs = PingTimeout; 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); KillTimer.Change(intervalMs, Timeout.Infinite);
} }
} }

@ -7,6 +7,7 @@ using ServiceStack;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.Session;
namespace MediaBrowser.Api namespace MediaBrowser.Api
{ {
@ -76,12 +77,12 @@ namespace MediaBrowser.Api
public class ConnectService : BaseApiService public class ConnectService : BaseApiService
{ {
private readonly IConnectManager _connectManager; private readonly IConnectManager _connectManager;
private readonly IUserManager _userManager; private readonly ISessionManager _sessionManager;
public ConnectService(IConnectManager connectManager, IUserManager userManager) public ConnectService(IConnectManager connectManager, ISessionManager sessionManager)
{ {
_connectManager = connectManager; _connectManager = connectManager;
_userManager = userManager; _sessionManager = sessionManager;
} }
public object Post(CreateConnectLink request) public object Post(CreateConnectLink request)
@ -141,10 +142,33 @@ namespace MediaBrowser.Api
throw new ResourceNotFoundException(); throw new ResourceNotFoundException();
} }
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
if (string.IsNullOrWhiteSpace(auth.Client))
{
return ToOptimizedResult(new ConnectAuthenticationExchangeResult
{
AccessToken = user.ConnectAccessKey,
LocalUserId = user.Id.ToString("N")
});
}
var session = await _sessionManager.CreateNewSession(new AuthenticationRequest
{
App = auth.Client,
AppVersion = auth.Version,
DeviceId = auth.DeviceId,
DeviceName = auth.Device,
RemoteEndPoint = Request.RemoteIp,
Username = user.Name,
UserId = user.Id.ToString("N")
}).ConfigureAwait(false);
return ToOptimizedResult(new ConnectAuthenticationExchangeResult return ToOptimizedResult(new ConnectAuthenticationExchangeResult
{ {
AccessToken = user.ConnectAccessKey, AccessToken = session.AccessToken,
LocalUserId = user.Id.ToString("N") LocalUserId = session.User.Id
}); });
} }
} }

@ -7,6 +7,8 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
namespace MediaBrowser.Api.Dlna namespace MediaBrowser.Api.Dlna
{ {
@ -109,13 +111,15 @@ namespace MediaBrowser.Api.Dlna
private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar; private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar;
private const string XMLContentType = "text/xml; charset=UTF-8"; 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; _dlnaManager = dlnaManager;
_contentDirectory = contentDirectory; _contentDirectory = contentDirectory;
_connectionManager = connectionManager; _connectionManager = connectionManager;
_mediaReceiverRegistrar = mediaReceiverRegistrar; _mediaReceiverRegistrar = mediaReceiverRegistrar;
_memoryStreamProvider = memoryStreamProvider;
} }
public object Get(GetDescriptionXml request) public object Get(GetDescriptionXml request)
@ -201,7 +205,7 @@ namespace MediaBrowser.Api.Dlna
{ {
using (var response = _dlnaManager.GetIcon(request.Filename)) using (var response = _dlnaManager.GetIcon(request.Filename))
{ {
using (var ms = new MemoryStream()) using (var ms = _memoryStreamProvider.CreateNew())
{ {
response.Stream.CopyTo(ms); response.Stream.CopyTo(ms);

@ -4,6 +4,7 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using ServiceStack; using ServiceStack;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -104,7 +105,13 @@ namespace MediaBrowser.Api
MediaTypes = request.GetMediaTypes(), MediaTypes = request.GetMediaTypes(),
IncludeItemTypes = request.GetIncludeItemTypes(), IncludeItemTypes = request.GetIncludeItemTypes(),
Recursive = true, Recursive = true,
EnableTotalRecordCount = false EnableTotalRecordCount = false,
DtoOptions = new Controller.Dto.DtoOptions
{
Fields = new List<ItemFields> { ItemFields.Genres, ItemFields.Tags },
EnableImages = false,
EnableUserData = false
}
}; };
return query; return query;

@ -200,6 +200,8 @@ namespace MediaBrowser.Api
(!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
var dtoOptions = GetDtoOptions(request);
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{ {
Limit = request.Limit, Limit = request.Limit,
@ -207,12 +209,11 @@ namespace MediaBrowser.Api
{ {
typeof(Game).Name typeof(Game).Name
}, },
SimilarTo = item SimilarTo = item,
DtoOptions = dtoOptions
}).ToList(); }).ToList();
var dtoOptions = GetDtoOptions(request);
var result = new QueryResult<BaseItemDto> var result = new QueryResult<BaseItemDto>
{ {
Items = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false)).ToArray(), Items = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false)).ToArray(),

@ -273,7 +273,8 @@ namespace MediaBrowser.Api.Images
{ {
var result = await _httpClient.GetResponse(new HttpRequestOptions var result = await _httpClient.GetResponse(new HttpRequestOptions
{ {
Url = url Url = url,
BufferContent = false
}).ConfigureAwait(false); }).ConfigureAwait(false);

@ -70,12 +70,13 @@ namespace MediaBrowser.Api
Cultures = _localizationManager.GetCultures().ToList() Cultures = _localizationManager.GetCultures().ToList()
}; };
if (!item.IsVirtualItem && !(item is ICollectionFolder) && !(item is UserView) && !(item is AggregateFolder) && !(item is LiveTvChannel) && !(item is IItemByName)) if (!item.IsVirtualItem && !(item is ICollectionFolder) && !(item is UserView) && !(item is AggregateFolder) && !(item is LiveTvChannel) && !(item is IItemByName) &&
item.SourceType == SourceType.Library)
{ {
var inheritedContentType = _libraryManager.GetInheritedContentType(item); var inheritedContentType = _libraryManager.GetInheritedContentType(item);
var configuredContentType = _libraryManager.GetConfiguredContentType(item); var configuredContentType = _libraryManager.GetConfiguredContentType(item);
if (string.IsNullOrWhiteSpace(inheritedContentType) || string.Equals(inheritedContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) || !string.IsNullOrWhiteSpace(configuredContentType)) if (string.IsNullOrWhiteSpace(inheritedContentType) || !string.IsNullOrWhiteSpace(configuredContentType))
{ {
info.ContentTypeOptions = GetContentTypeOptions(true); info.ContentTypeOptions = GetContentTypeOptions(true);
info.ContentType = configuredContentType; info.ContentType = configuredContentType;
@ -242,11 +243,7 @@ namespace MediaBrowser.Api
hasBudget.Revenue = request.Revenue; hasBudget.Revenue = request.Revenue;
} }
var hasOriginalTitle = item as IHasOriginalTitle; item.OriginalTitle = string.IsNullOrWhiteSpace(request.OriginalTitle) ? null : request.OriginalTitle;
if (hasOriginalTitle != null)
{
hasOriginalTitle.OriginalTitle = hasOriginalTitle.OriginalTitle;
}
var hasCriticRating = item as IHasCriticRating; var hasCriticRating = item as IHasCriticRating;
if (hasCriticRating != null) if (hasCriticRating != null)
@ -277,10 +274,9 @@ namespace MediaBrowser.Api
item.Tags = request.Tags; item.Tags = request.Tags;
var hasTaglines = item as IHasTaglines; if (request.Taglines != null)
if (hasTaglines != null)
{ {
hasTaglines.Taglines = request.Taglines; item.Tagline = request.Taglines.FirstOrDefault();
} }
var hasShortOverview = item as IHasShortOverview; var hasShortOverview = item as IHasShortOverview;
@ -307,8 +303,6 @@ namespace MediaBrowser.Api
item.OfficialRating = string.IsNullOrWhiteSpace(request.OfficialRating) ? null : request.OfficialRating; item.OfficialRating = string.IsNullOrWhiteSpace(request.OfficialRating) ? null : request.OfficialRating;
item.CustomRating = request.CustomRating; item.CustomRating = request.CustomRating;
SetProductionLocations(item, request);
item.PreferredMetadataCountryCode = request.PreferredMetadataCountryCode; item.PreferredMetadataCountryCode = request.PreferredMetadataCountryCode;
item.PreferredMetadataLanguage = request.PreferredMetadataLanguage; item.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
@ -416,23 +410,5 @@ namespace MediaBrowser.Api
series.AirTime = request.AirTime; 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();
}
}
} }
} }

@ -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);
}
}
}

@ -25,6 +25,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommonIO; using CommonIO;
using MediaBrowser.Controller.Configuration;
namespace MediaBrowser.Api.Library namespace MediaBrowser.Api.Library
{ {
@ -288,12 +289,13 @@ namespace MediaBrowser.Api.Library
private readonly ITVSeriesManager _tvManager; private readonly ITVSeriesManager _tvManager;
private readonly ILibraryMonitor _libraryMonitor; private readonly ILibraryMonitor _libraryMonitor;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _config;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LibraryService" /> class. /// Initializes a new instance of the <see cref="LibraryService" /> class.
/// </summary> /// </summary>
public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager, public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager,
IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem) IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, IServerConfigurationManager config)
{ {
_itemRepo = itemRepo; _itemRepo = itemRepo;
_libraryManager = libraryManager; _libraryManager = libraryManager;
@ -307,6 +309,7 @@ namespace MediaBrowser.Api.Library
_tvManager = tvManager; _tvManager = tvManager;
_libraryMonitor = libraryMonitor; _libraryMonitor = libraryMonitor;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_config = config;
} }
public object Get(GetSimilarItems request) public object Get(GetSimilarItems request)
@ -377,7 +380,7 @@ namespace MediaBrowser.Api.Library
if (item is Movie || (program != null && program.IsMovie) || item is Trailer) if (item is Movie || (program != null && program.IsMovie) || item is Trailer)
{ {
return new MoviesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService) return new MoviesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _config)
{ {
AuthorizationContext = AuthorizationContext, AuthorizationContext = AuthorizationContext,
Logger = Logger, Logger = Logger,
@ -832,14 +835,14 @@ namespace MediaBrowser.Api.Library
: (Folder)_libraryManager.RootFolder) : (Folder)_libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id); : _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(); item = item.GetParent();
} }
var dtoOptions = GetDtoOptions(request); var dtoOptions = GetDtoOptions(request);
var dtos = GetThemeSongIds(item).Select(_libraryManager.GetItemById) var dtos = item.ThemeSongIds.Select(_libraryManager.GetItemById)
.Where(i => i != null) .Where(i => i != null)
.OrderBy(i => i.SortName) .OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
@ -876,14 +879,14 @@ namespace MediaBrowser.Api.Library
: (Folder)_libraryManager.RootFolder) : (Folder)_libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id); : _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(); item = item.GetParent();
} }
var dtoOptions = GetDtoOptions(request); var dtoOptions = GetDtoOptions(request);
var dtos = GetThemeVideoIds(item).Select(_libraryManager.GetItemById) var dtos = item.ThemeVideoIds.Select(_libraryManager.GetItemById)
.Where(i => i != null) .Where(i => i != null)
.OrderBy(i => i.SortName) .OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
@ -898,30 +901,6 @@ namespace MediaBrowser.Api.Library
}; };
} }
private List<Guid> GetThemeVideoIds(BaseItem item)
{
var i = item as IHasThemeMedia;
if (i != null)
{
return i.ThemeVideoIds;
}
return new List<Guid>();
}
private List<Guid> GetThemeSongIds(BaseItem item)
{
var i = item as IHasThemeMedia;
if (i != null)
{
return i.ThemeSongIds;
}
return new List<Guid>();
}
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public object Get(GetYearIndex request) public object Get(GetYearIndex request)

@ -112,6 +112,8 @@ namespace MediaBrowser.Api.Library
/// <value>The name.</value> /// <value>The name.</value>
public string Path { get; set; } public string Path { get; set; }
public MediaPathInfo PathInfo { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether [refresh library]. /// Gets or sets a value indicating whether [refresh library].
/// </summary> /// </summary>
@ -119,6 +121,18 @@ namespace MediaBrowser.Api.Library
public bool RefreshLibrary { get; set; } public bool RefreshLibrary { get; set; }
} }
[Route("/Library/VirtualFolders/Paths/Update", "POST")]
public class UpdateMediaPath : IReturnVoid
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
public MediaPathInfo PathInfo { get; set; }
}
[Route("/Library/VirtualFolders/Paths", "DELETE")] [Route("/Library/VirtualFolders/Paths", "DELETE")]
public class RemoveMediaPath : IReturnVoid public class RemoveMediaPath : IReturnVoid
{ {
@ -212,7 +226,12 @@ namespace MediaBrowser.Api.Library
{ {
var libraryOptions = request.LibraryOptions ?? new LibraryOptions(); 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);
} }
/// <summary> /// <summary>
@ -308,7 +327,16 @@ namespace MediaBrowser.Api.Library
try 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 finally
{ {
@ -332,6 +360,20 @@ namespace MediaBrowser.Api.Library
} }
} }
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Post(UpdateMediaPath request)
{
if (string.IsNullOrWhiteSpace(request.Name))
{
throw new ArgumentNullException("request");
}
_libraryManager.UpdateMediaPath(request.Name, request.PathInfo);
}
/// <summary> /// <summary>
/// Deletes the specified request. /// Deletes the specified request.
/// </summary> /// </summary>

@ -12,9 +12,14 @@ using ServiceStack;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; 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 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")] [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; } 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; }
/// <summary> /// <summary>
/// The maximum number of items to return /// The maximum number of items to return
/// </summary> /// </summary>
@ -85,6 +105,26 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableUserData { get; set; } public bool? EnableUserData { get; set; }
public string SortBy { get; set; }
public SortOrder? SortOrder { get; set; }
/// <summary>
/// Gets the order by.
/// </summary>
/// <returns>IEnumerable{ItemSortBy}.</returns>
public string[] GetOrderBy()
{
var val = SortBy;
if (string.IsNullOrEmpty(val))
{
return new string[] { };
}
return val.Split(',');
}
public GetChannels() public GetChannels()
{ {
AddCurrentProgram = true; AddCurrentProgram = true;
@ -155,12 +195,73 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableUserData { get; set; } public bool? EnableUserData { get; set; }
public bool? IsMovie { get; set; }
public bool? IsSeries { get; set; }
public bool? IsKids { get; set; }
public bool? IsSports { get; set; }
public bool? IsNews { get; set; }
public GetRecordings() public GetRecordings()
{ {
EnableTotalRecordCount = true; EnableTotalRecordCount = true;
} }
} }
[Route("/LiveTv/Recordings/Series", "GET", Summary = "Gets live tv recordings")]
[Authenticated]
public class GetRecordingSeries : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions
{
[ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ChannelId { get; set; }
[ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string UserId { get; set; }
[ApiMember(Name = "GroupId", Description = "Optional filter by recording group.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string GroupId { get; set; }
[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 = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? Limit { get; set; }
[ApiMember(Name = "Status", Description = "Optional filter by recording status.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public RecordingStatus? Status { get; set; }
[ApiMember(Name = "Status", Description = "Optional filter by recordings that are in progress, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsInProgress { get; set; }
[ApiMember(Name = "SeriesTimerId", Description = "Optional filter by recordings belonging to a series timer", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string SeriesTimerId { 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; }
[ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? ImageTypeLimit { get; set; }
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string EnableImageTypes { get; set; }
/// <summary>
/// Fields to return within the items, in addition to basic information
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Fields { get; set; }
public bool EnableTotalRecordCount { get; set; }
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableUserData { get; set; }
public GetRecordingSeries()
{
EnableTotalRecordCount = true;
}
}
[Route("/LiveTv/Recordings/Groups", "GET", Summary = "Gets live tv recording groups")] [Route("/LiveTv/Recordings/Groups", "GET", Summary = "Gets live tv recording groups")]
[Authenticated] [Authenticated]
public class GetRecordingGroups : IReturn<QueryResult<BaseItemDto>> public class GetRecordingGroups : IReturn<QueryResult<BaseItemDto>>
@ -215,6 +316,8 @@ namespace MediaBrowser.Api.LiveTv
public string SeriesTimerId { get; set; } public string SeriesTimerId { get; set; }
public bool? IsActive { get; set; } public bool? IsActive { get; set; }
public bool? IsScheduled { get; set; }
} }
[Route("/LiveTv/Programs", "GET,POST", Summary = "Gets available live tv epgs..")] [Route("/LiveTv/Programs", "GET,POST", Summary = "Gets available live tv epgs..")]
@ -245,6 +348,12 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsMovie { get; set; } 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")] [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsKids { get; set; } public bool? IsKids { get; set; }
@ -280,6 +389,8 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableUserData { get; set; } public bool? EnableUserData { get; set; }
public string SeriesTimerId { get; set; }
/// <summary> /// <summary>
/// Fields to return within the items, in addition to basic information /// Fields to return within the items, in addition to basic information
/// </summary> /// </summary>
@ -316,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")] [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; } public bool? HasAired { get; set; }
[ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsSports { get; set; } 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; } 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; } 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")] [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableImages { get; set; } public bool? EnableImages { get; set; }
@ -535,12 +652,6 @@ namespace MediaBrowser.Api.LiveTv
[Authenticated] [Authenticated]
public class GetLiveTvRegistrationInfo : IReturn<MBRegistrationRecord> public class GetLiveTvRegistrationInfo : IReturn<MBRegistrationRecord>
{ {
[ApiMember(Name = "ChannelId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ChannelId { get; set; }
[ApiMember(Name = "ProgramId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ProgramId { get; set; }
[ApiMember(Name = "Feature", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "Feature", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Feature { get; set; } public string Feature { get; set; }
} }
@ -559,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 public class LiveTvService : BaseApiService
{ {
private readonly ILiveTvManager _liveTvManager; private readonly ILiveTvManager _liveTvManager;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly IConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService; 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; _liveTvManager = liveTvManager;
_userManager = userManager; _userManager = userManager;
@ -576,6 +701,41 @@ namespace MediaBrowser.Api.LiveTv
_httpClient = httpClient; _httpClient = httpClient;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_dtoService = dtoService; _dtoService = dtoService;
_fileSystem = fileSystem;
}
public async Task<object> Get(GetLiveRecordingFile request)
{
var path = EmbyTV.Current.GetActiveRecordingPath(request.Id);
if (path == null)
{
throw new FileNotFoundException();
}
var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path);
var streamSource = new ProgressiveFileCopier(_fileSystem, path, outputHeaders, null, Logger, CancellationToken.None)
{
AllowEndOfFile = false
};
return ResultFactory.GetAsyncStreamWriter(streamSource);
}
public async Task<object> Get(GetLiveStreamFile request)
{
var directStreamProvider = (await EmbyTV.Current.GetLiveStream(request.Id).ConfigureAwait(false)) as IDirectStreamProvider;
var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container);
var streamSource = new ProgressiveFileCopier(directStreamProvider, outputHeaders, null, Logger, CancellationToken.None)
{
AllowEndOfFile = false
};
return ResultFactory.GetAsyncStreamWriter(streamSource);
} }
public object Get(GetDefaultListingProvider request) public object Get(GetDefaultListingProvider request)
@ -592,7 +752,7 @@ namespace MediaBrowser.Api.LiveTv
public async Task<object> Get(GetLiveTvRegistrationInfo request) public async Task<object> Get(GetLiveTvRegistrationInfo request)
{ {
var result = await _liveTvManager.GetRegistrationInfo(request.ChannelId, request.ProgramId, request.Feature).ConfigureAwait(false); var result = await _liveTvManager.GetRegistrationInfo(request.Feature).ConfigureAwait(false);
return ToOptimizedResult(result); return ToOptimizedResult(result);
} }
@ -648,7 +808,8 @@ namespace MediaBrowser.Api.LiveTv
var response = await _httpClient.Get(new HttpRequestOptions var response = await _httpClient.Get(new HttpRequestOptions
{ {
Url = "https://json.schedulesdirect.org/20141201/available/countries" Url = "https://json.schedulesdirect.org/20141201/available/countries",
BufferContent = false
}).ConfigureAwait(false); }).ConfigureAwait(false);
@ -732,6 +893,13 @@ namespace MediaBrowser.Api.LiveTv
IsLiked = request.IsLiked, IsLiked = request.IsLiked,
IsDisliked = request.IsDisliked, IsDisliked = request.IsDisliked,
EnableFavoriteSorting = request.EnableFavoriteSorting, EnableFavoriteSorting = request.EnableFavoriteSorting,
IsMovie = request.IsMovie,
IsSeries = request.IsSeries,
IsNews = request.IsNews,
IsKids = request.IsKids,
IsSports = request.IsSports,
SortBy = request.GetOrderBy(),
SortOrder = request.SortOrder ?? SortOrder.Ascending,
AddCurrentProgram = request.AddCurrentProgram AddCurrentProgram = request.AddCurrentProgram
}, CancellationToken.None).ConfigureAwait(false); }, CancellationToken.None).ConfigureAwait(false);
@ -814,9 +982,12 @@ namespace MediaBrowser.Api.LiveTv
query.Limit = request.Limit; query.Limit = request.Limit;
query.SortBy = (request.SortBy ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); query.SortBy = (request.SortBy ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
query.SortOrder = request.SortOrder; query.SortOrder = request.SortOrder;
query.IsNews = request.IsNews;
query.IsMovie = request.IsMovie; query.IsMovie = request.IsMovie;
query.IsSeries = request.IsSeries;
query.IsKids = request.IsKids; query.IsKids = request.IsKids;
query.IsSports = request.IsSports; query.IsSports = request.IsSports;
query.SeriesTimerId = request.SeriesTimerId;
query.Genres = (request.Genres ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); query.Genres = (request.Genres ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false); var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false);
@ -832,8 +1003,10 @@ namespace MediaBrowser.Api.LiveTv
IsAiring = request.IsAiring, IsAiring = request.IsAiring,
Limit = request.Limit, Limit = request.Limit,
HasAired = request.HasAired, HasAired = request.HasAired,
IsSeries = request.IsSeries,
IsMovie = request.IsMovie, IsMovie = request.IsMovie,
IsKids = request.IsKids, IsKids = request.IsKids,
IsNews = request.IsNews,
IsSports = request.IsSports, IsSports = request.IsSports,
EnableTotalRecordCount = request.EnableTotalRecordCount EnableTotalRecordCount = request.EnableTotalRecordCount
}; };
@ -854,6 +1027,33 @@ namespace MediaBrowser.Api.LiveTv
options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId; options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;
var result = await _liveTvManager.GetRecordings(new RecordingQuery var result = await _liveTvManager.GetRecordings(new RecordingQuery
{
ChannelId = request.ChannelId,
UserId = request.UserId,
GroupId = request.GroupId,
StartIndex = request.StartIndex,
Limit = request.Limit,
Status = request.Status,
SeriesTimerId = request.SeriesTimerId,
IsInProgress = request.IsInProgress,
EnableTotalRecordCount = request.EnableTotalRecordCount,
IsMovie = request.IsMovie,
IsNews = request.IsNews,
IsSeries = request.IsSeries,
IsKids = request.IsKids,
IsSports = request.IsSports
}, options, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
public async Task<object> Get(GetRecordingSeries request)
{
var options = GetDtoOptions(request);
options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;
var result = await _liveTvManager.GetRecordingSeries(new RecordingQuery
{ {
ChannelId = request.ChannelId, ChannelId = request.ChannelId,
UserId = request.UserId, UserId = request.UserId,
@ -895,7 +1095,8 @@ namespace MediaBrowser.Api.LiveTv
{ {
ChannelId = request.ChannelId, ChannelId = request.ChannelId,
SeriesTimerId = request.SeriesTimerId, SeriesTimerId = request.SeriesTimerId,
IsActive = request.IsActive IsActive = request.IsActive,
IsScheduled = request.IsScheduled
}, CancellationToken.None).ConfigureAwait(false); }, CancellationToken.None).ConfigureAwait(false);

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -11,9 +11,10 @@
<AssemblyName>MediaBrowser.Api</AssemblyName> <AssemblyName>MediaBrowser.Api</AssemblyName>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<ReleaseVersion> <ReleaseVersion>
</ReleaseVersion> </ReleaseVersion>
<TargetFrameworkProfile />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -79,7 +80,6 @@
<Compile Include="Dlna\DlnaService.cs" /> <Compile Include="Dlna\DlnaService.cs" />
<Compile Include="FilterService.cs" /> <Compile Include="FilterService.cs" />
<Compile Include="IHasDtoOptions.cs" /> <Compile Include="IHasDtoOptions.cs" />
<Compile Include="Library\ChapterService.cs" />
<Compile Include="Playback\MediaInfoService.cs" /> <Compile Include="Playback\MediaInfoService.cs" />
<Compile Include="Playback\TranscodingThrottler.cs" /> <Compile Include="Playback\TranscodingThrottler.cs" />
<Compile Include="PlaylistService.cs" /> <Compile Include="PlaylistService.cs" />
@ -198,6 +198,10 @@
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project> <Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
<Name>MediaBrowser.Model</Name> <Name>MediaBrowser.Model</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj">
<Project>{2e781478-814d-4a48-9d80-bff206441a65}</Project>
<Name>MediaBrowser.Server.Implementations</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />

@ -14,6 +14,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
namespace MediaBrowser.Api.Movies namespace MediaBrowser.Api.Movies
@ -88,6 +89,7 @@ namespace MediaBrowser.Api.Movies
private readonly IItemRepository _itemRepo; private readonly IItemRepository _itemRepo;
private readonly IDtoService _dtoService; private readonly IDtoService _dtoService;
private readonly IServerConfigurationManager _config;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="MoviesService" /> class. /// Initializes a new instance of the <see cref="MoviesService" /> class.
@ -97,13 +99,14 @@ namespace MediaBrowser.Api.Movies
/// <param name="libraryManager">The library manager.</param> /// <param name="libraryManager">The library manager.</param>
/// <param name="itemRepo">The item repo.</param> /// <param name="itemRepo">The item repo.</param>
/// <param name="dtoService">The dto service.</param> /// <param name="dtoService">The dto service.</param>
public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService) public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, IServerConfigurationManager config)
{ {
_userManager = userManager; _userManager = userManager;
_userDataRepository = userDataRepository; _userDataRepository = userDataRepository;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_itemRepo = itemRepo; _itemRepo = itemRepo;
_dtoService = dtoService; _dtoService = dtoService;
_config = config;
} }
/// <summary> /// <summary>
@ -146,23 +149,26 @@ namespace MediaBrowser.Api.Movies
(!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
var itemTypes = new List<string> { typeof(Movie).Name };
if (_config.Configuration.EnableExternalContentInSuggestions)
{
itemTypes.Add(typeof(Trailer).Name);
itemTypes.Add(typeof(LiveTvProgram).Name);
}
var dtoOptions = GetDtoOptions(request);
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{ {
Limit = request.Limit, Limit = request.Limit,
IncludeItemTypes = new[] IncludeItemTypes = itemTypes.ToArray(),
{
typeof(Movie).Name,
typeof(Trailer).Name,
typeof(LiveTvProgram).Name
},
IsMovie = true, IsMovie = true,
SimilarTo = item, SimilarTo = item,
EnableGroupByMetadataKey = true EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
}).ToList(); }).ToList();
var dtoOptions = GetDtoOptions(request);
var result = new QueryResult<BaseItemDto> var result = new QueryResult<BaseItemDto>
{ {
Items = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false)).ToArray(), Items = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false)).ToArray(),
@ -193,19 +199,22 @@ namespace MediaBrowser.Api.Movies
Limit = 7, Limit = 7,
ParentId = parentIdGuid, ParentId = parentIdGuid,
Recursive = true, Recursive = true,
IsPlayed = true IsPlayed = true,
DtoOptions = dtoOptions
}; };
var recentlyPlayedMovies = _libraryManager.GetItemList(query).ToList(); var recentlyPlayedMovies = _libraryManager.GetItemList(query).ToList();
var itemTypes = new List<string> { typeof(Movie).Name };
if (_config.Configuration.EnableExternalContentInSuggestions)
{
itemTypes.Add(typeof(Trailer).Name);
itemTypes.Add(typeof(LiveTvProgram).Name);
}
var likedMovies = _libraryManager.GetItemList(new InternalItemsQuery(user) var likedMovies = _libraryManager.GetItemList(new InternalItemsQuery(user)
{ {
IncludeItemTypes = new[] IncludeItemTypes = itemTypes.ToArray(),
{
typeof(Movie).Name,
typeof(Trailer).Name,
typeof(LiveTvProgram).Name
},
IsMovie = true, IsMovie = true,
SortBy = new[] { ItemSortBy.Random }, SortBy = new[] { ItemSortBy.Random },
SortOrder = SortOrder.Descending, SortOrder = SortOrder.Descending,
@ -214,7 +223,8 @@ namespace MediaBrowser.Api.Movies
ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id.ToString("N")).ToArray(), ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id.ToString("N")).ToArray(),
EnableGroupByMetadataKey = true, EnableGroupByMetadataKey = true,
ParentId = parentIdGuid, ParentId = parentIdGuid,
Recursive = true Recursive = true,
DtoOptions = dtoOptions
}).ToList(); }).ToList();
@ -278,6 +288,13 @@ namespace MediaBrowser.Api.Movies
private IEnumerable<RecommendationDto> GetWithDirector(User user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type) private IEnumerable<RecommendationDto> GetWithDirector(User user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type)
{ {
var itemTypes = new List<string> { typeof(Movie).Name };
if (_config.Configuration.EnableExternalContentInSuggestions)
{
itemTypes.Add(typeof(Trailer).Name);
itemTypes.Add(typeof(LiveTvProgram).Name);
}
foreach (var name in names) foreach (var name in names)
{ {
var items = _libraryManager.GetItemList(new InternalItemsQuery(user) var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
@ -286,14 +303,10 @@ namespace MediaBrowser.Api.Movies
// Account for duplicates by imdb id, since the database doesn't support this yet // Account for duplicates by imdb id, since the database doesn't support this yet
Limit = itemLimit + 2, Limit = itemLimit + 2,
PersonTypes = new[] { PersonType.Director }, PersonTypes = new[] { PersonType.Director },
IncludeItemTypes = new[] IncludeItemTypes = itemTypes.ToArray(),
{
typeof(Movie).Name,
typeof(Trailer).Name,
typeof(LiveTvProgram).Name
},
IsMovie = true, IsMovie = true,
EnableGroupByMetadataKey = true EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
}).DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N")) }).DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
.Take(itemLimit) .Take(itemLimit)
@ -314,6 +327,13 @@ namespace MediaBrowser.Api.Movies
private IEnumerable<RecommendationDto> GetWithActor(User user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type) private IEnumerable<RecommendationDto> GetWithActor(User user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type)
{ {
var itemTypes = new List<string> { typeof(Movie).Name };
if (_config.Configuration.EnableExternalContentInSuggestions)
{
itemTypes.Add(typeof(Trailer).Name);
itemTypes.Add(typeof(LiveTvProgram).Name);
}
foreach (var name in names) foreach (var name in names)
{ {
var items = _libraryManager.GetItemList(new InternalItemsQuery(user) var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
@ -321,14 +341,10 @@ namespace MediaBrowser.Api.Movies
Person = name, Person = name,
// Account for duplicates by imdb id, since the database doesn't support this yet // Account for duplicates by imdb id, since the database doesn't support this yet
Limit = itemLimit + 2, Limit = itemLimit + 2,
IncludeItemTypes = new[] IncludeItemTypes = itemTypes.ToArray(),
{
typeof(Movie).Name,
typeof(Trailer).Name,
typeof(LiveTvProgram).Name
},
IsMovie = true, IsMovie = true,
EnableGroupByMetadataKey = true EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
}).DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N")) }).DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
.Take(itemLimit) .Take(itemLimit)
@ -349,20 +365,23 @@ namespace MediaBrowser.Api.Movies
private IEnumerable<RecommendationDto> GetSimilarTo(User user, List<BaseItem> baselineItems, int itemLimit, DtoOptions dtoOptions, RecommendationType type) private IEnumerable<RecommendationDto> GetSimilarTo(User user, List<BaseItem> baselineItems, int itemLimit, DtoOptions dtoOptions, RecommendationType type)
{ {
var itemTypes = new List<string> { typeof(Movie).Name };
if (_config.Configuration.EnableExternalContentInSuggestions)
{
itemTypes.Add(typeof(Trailer).Name);
itemTypes.Add(typeof(LiveTvProgram).Name);
}
foreach (var item in baselineItems) foreach (var item in baselineItems)
{ {
var similar = _libraryManager.GetItemList(new InternalItemsQuery(user) var similar = _libraryManager.GetItemList(new InternalItemsQuery(user)
{ {
Limit = itemLimit, Limit = itemLimit,
IncludeItemTypes = new[] IncludeItemTypes = itemTypes.ToArray(),
{
typeof(Movie).Name,
typeof(Trailer).Name,
typeof(LiveTvProgram).Name
},
IsMovie = true, IsMovie = true,
SimilarTo = item, SimilarTo = item,
EnableGroupByMetadataKey = true EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
}).ToList(); }).ToList();

@ -46,7 +46,7 @@ namespace MediaBrowser.Api
/// </summary> /// </summary>
/// <value>The name.</value> /// <value>The name.</value>
[ApiMember(Name = "PackageType", Description = "Optional package type filter (System/UserInstalled)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] [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)] [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; } public string TargetSystems { get; set; }
@ -72,7 +72,7 @@ namespace MediaBrowser.Api
/// </summary> /// </summary>
/// <value>The name.</value> /// <value>The name.</value>
[ApiMember(Name = "PackageType", Description = "Package type filter (System/UserInstalled)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] [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; }
} }
/// <summary> /// <summary>
@ -149,12 +149,12 @@ namespace MediaBrowser.Api
{ {
var result = new List<PackageVersionInfo>(); var result = new List<PackageVersionInfo>();
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()); 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<double>()).Result; var updateCheckResult = _appHost.CheckForApplicationUpdate(CancellationToken.None, new Progress<double>()).Result;

@ -337,44 +337,62 @@ namespace MediaBrowser.Api.Playback
/// Gets the video bitrate to specify on the command line /// Gets the video bitrate to specify on the command line
/// </summary> /// </summary>
/// <param name="state">The state.</param> /// <param name="state">The state.</param>
/// <param name="videoCodec">The video codec.</param> /// <param name="videoEncoder">The video codec.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
protected string GetVideoQualityParam(StreamState state, string videoCodec) protected string GetVideoQualityParam(StreamState state, string videoEncoder)
{ {
var param = string.Empty; var param = string.Empty;
var isVc1 = state.VideoStream != null && var isVc1 = state.VideoStream != null &&
string.Equals(state.VideoStream.Codec, "vc1", StringComparison.OrdinalIgnoreCase); string.Equals(state.VideoStream.Codec, "vc1", StringComparison.OrdinalIgnoreCase);
if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
{ {
param = "-preset superfast"; if (!string.IsNullOrWhiteSpace(encodingOptions.H264Preset))
{
param += "-preset " + encodingOptions.H264Preset;
}
else
{
param += "-preset superfast";
}
if (encodingOptions.H264Crf >= 0 && encodingOptions.H264Crf <= 51)
{
param += " -crf " + encodingOptions.H264Crf.ToString(CultureInfo.InvariantCulture);
}
else
{
param += " -crf 23";
}
param += " -crf 23"; param += " -tune zerolatency";
} }
else if (string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase))
{ {
param = "-preset fast"; param += "-preset fast";
param += " -crf 28"; param += " -crf 28";
} }
// h264 (h264_qsv) // h264 (h264_qsv)
else if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase))
{ {
param = "-preset 7 -look_ahead 0"; param += "-preset 7 -look_ahead 0";
} }
// h264 (h264_nvenc) // h264 (h264_nvenc)
else if (string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
{ {
param = "-preset default"; param += "-preset default";
} }
// webm // webm
else if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase))
{ {
// Values 0-3, 0 being highest quality but slower // Values 0-3, 0 being highest quality but slower
var profileScore = 0; var profileScore = 0;
@ -394,30 +412,30 @@ namespace MediaBrowser.Api.Playback
profileScore = Math.Min(profileScore, 2); profileScore = Math.Min(profileScore, 2);
// http://www.webmproject.org/docs/encoder-parameters/ // http://www.webmproject.org/docs/encoder-parameters/
param = string.Format("-speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}", param += string.Format("-speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}",
profileScore.ToString(UsCulture), profileScore.ToString(UsCulture),
crf, crf,
qmin, qmin,
qmax); qmax);
} }
else if (string.Equals(videoCodec, "mpeg4", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(videoEncoder, "mpeg4", StringComparison.OrdinalIgnoreCase))
{ {
param = "-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2"; param += "-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2";
} }
// asf/wmv // asf/wmv
else if (string.Equals(videoCodec, "wmv2", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(videoEncoder, "wmv2", StringComparison.OrdinalIgnoreCase))
{ {
param = "-qmin 2"; param += "-qmin 2";
} }
else if (string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(videoEncoder, "msmpeg4", StringComparison.OrdinalIgnoreCase))
{ {
param = "-mbd 2"; param += "-mbd 2";
} }
param += GetVideoBitrateParam(state, videoCodec); param += GetVideoBitrateParam(state, videoEncoder);
var framerate = GetFramerateParam(state); var framerate = GetFramerateParam(state);
if (framerate.HasValue) if (framerate.HasValue)
@ -432,8 +450,8 @@ namespace MediaBrowser.Api.Playback
if (!string.IsNullOrEmpty(state.VideoRequest.Profile)) if (!string.IsNullOrEmpty(state.VideoRequest.Profile))
{ {
if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) && if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{ {
// not supported by h264_omx // not supported by h264_omx
param += " -profile:v " + state.VideoRequest.Profile; param += " -profile:v " + state.VideoRequest.Profile;
@ -442,14 +460,18 @@ namespace MediaBrowser.Api.Playback
if (!string.IsNullOrEmpty(state.VideoRequest.Level)) if (!string.IsNullOrEmpty(state.VideoRequest.Level))
{ {
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 // h264_qsv and h264_nvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format
if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) || // also needed for libx264 due to https://trac.ffmpeg.org/ticket/3307
string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) ||
string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) ||
string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
{ {
switch (state.VideoRequest.Level) switch (level)
{ {
case "30": case "30":
param += " -level 3"; param += " -level 3.0";
break; break;
case "31": case "31":
param += " -level 3.1"; param += " -level 3.1";
@ -458,7 +480,7 @@ namespace MediaBrowser.Api.Playback
param += " -level 3.2"; param += " -level 3.2";
break; break;
case "40": case "40":
param += " -level 4"; param += " -level 4.0";
break; break;
case "41": case "41":
param += " -level 4.1"; param += " -level 4.1";
@ -467,7 +489,7 @@ namespace MediaBrowser.Api.Playback
param += " -level 4.2"; param += " -level 4.2";
break; break;
case "50": case "50":
param += " -level 5"; param += " -level 5.0";
break; break;
case "51": case "51":
param += " -level 5.1"; param += " -level 5.1";
@ -476,20 +498,20 @@ namespace MediaBrowser.Api.Playback
param += " -level 5.2"; param += " -level 5.2";
break; break;
default: default:
param += " -level " + state.VideoRequest.Level; param += " -level " + level;
break; break;
} }
} }
else if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase)) else if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase))
{ {
param += " -level " + state.VideoRequest.Level; param += " -level " + level;
} }
} }
if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) && if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{ {
param = "-pix_fmt yuv420p " + param; param = "-pix_fmt yuv420p " + param;
} }
@ -497,6 +519,25 @@ namespace MediaBrowser.Api.Playback
return param; return param;
} }
private string NormalizeTranscodingLevel(string videoCodec, string level)
{
double requestLevel;
// Clients may direct play higher than level 41, but there's no reason to transcode higher
if (double.TryParse(level, NumberStyles.Any, UsCulture, out requestLevel))
{
if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
{
if (requestLevel > 41)
{
return "41";
}
}
}
return level;
}
protected string GetAudioFilterParam(StreamState state, bool isHls) protected string GetAudioFilterParam(StreamState state, bool isHls)
{ {
var volParam = string.Empty; var volParam = string.Empty;
@ -730,7 +771,20 @@ namespace MediaBrowser.Api.Playback
if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue) if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
{ {
outputSizeParam = GetOutputSizeParam(state, outputVideoCodec).TrimEnd('"'); 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; var videoSizeParam = string.Empty;
@ -985,7 +1039,15 @@ namespace MediaBrowser.Api.Playback
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions(); var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
if (GetVideoEncoder(state).IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1) 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;
} }
} }
@ -1160,17 +1222,21 @@ namespace MediaBrowser.Api.Playback
await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false); await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false);
} }
if (state.IsInputVideo && transcodingJob.Type == TranscodingJobType.Progressive) if (state.IsInputVideo && transcodingJob.Type == TranscodingJobType.Progressive && !transcodingJob.HasExited)
{ {
await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false); await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false);
if (state.ReadInputAtNativeFramerate) if (state.ReadInputAtNativeFramerate && !transcodingJob.HasExited)
{ {
await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false); await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
} }
} }
StartThrottler(state, transcodingJob); if (!transcodingJob.HasExited)
{
StartThrottler(state, transcodingJob);
}
ReportUsage(state); ReportUsage(state);
return transcodingJob; return transcodingJob;
@ -1178,14 +1244,14 @@ namespace MediaBrowser.Api.Playback
private void StartThrottler(StreamState state, TranscodingJob transcodingJob) 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); transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager);
state.TranscodingThrottler.Start(); state.TranscodingThrottler.Start();
} }
} }
protected virtual bool EnableThrottling(StreamState state) private bool EnableThrottling(StreamState state)
{ {
// do not use throttling with hardware encoders // do not use throttling with hardware encoders
return state.InputProtocol == MediaProtocol.File && return state.InputProtocol == MediaProtocol.File &&
@ -1399,7 +1465,8 @@ namespace MediaBrowser.Api.Playback
// Make sure we don't request a bitrate higher than the source // Make sure we don't request a bitrate higher than the source
var currentBitrate = audioStream == null ? request.AudioBitRate.Value : audioStream.BitRate ?? request.AudioBitRate.Value; var currentBitrate = audioStream == null ? request.AudioBitRate.Value : audioStream.BitRate ?? request.AudioBitRate.Value;
return request.AudioBitRate.Value; // Don't encode any higher than this
return Math.Min(384000, request.AudioBitRate.Value);
//return Math.Min(currentBitrate, request.AudioBitRate.Value); //return Math.Min(currentBitrate, request.AudioBitRate.Value);
} }
@ -1770,6 +1837,15 @@ namespace MediaBrowser.Api.Playback
// state.SegmentLength = 6; // state.SegmentLength = 6;
//} //}
if (state.VideoRequest != null)
{
if (!string.IsNullOrWhiteSpace(state.VideoRequest.VideoCodec))
{
state.SupportedVideoCodecs = state.VideoRequest.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
state.VideoRequest.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault();
}
}
if (!string.IsNullOrWhiteSpace(request.AudioCodec)) if (!string.IsNullOrWhiteSpace(request.AudioCodec))
{ {
state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
@ -1781,26 +1857,37 @@ namespace MediaBrowser.Api.Playback
state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
var archivable = item as IArchivable; MediaSourceInfo mediaSource = null;
state.IsInputArchive = archivable != null && archivable.IsArchive;
MediaSourceInfo mediaSource;
if (string.IsNullOrWhiteSpace(request.LiveStreamId)) 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) if (currentJob != null)
? mediaSources.First() {
: mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId)); mediaSource = currentJob.MediaSource;
}
if (mediaSource == null && string.Equals(request.Id, request.MediaSourceId, StringComparison.OrdinalIgnoreCase)) if (mediaSource == null)
{ {
mediaSource = mediaSources.First(); 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 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; var videoRequest = request as VideoStreamRequest;
@ -2012,7 +2099,7 @@ namespace MediaBrowser.Api.Playback
} }
// Source and target codecs must match // Source and target codecs must match
if (!string.Equals(request.VideoCodec, videoStream.Codec, StringComparison.OrdinalIgnoreCase)) if (string.IsNullOrEmpty(videoStream.Codec) || !state.SupportedVideoCodecs.Contains(videoStream.Codec, StringComparer.OrdinalIgnoreCase))
{ {
return false; return false;
} }
@ -2243,7 +2330,8 @@ namespace MediaBrowser.Api.Playback
state.TargetRefFrames, state.TargetRefFrames,
state.TargetVideoStreamCount, state.TargetVideoStreamCount,
state.TargetAudioStreamCount, state.TargetAudioStreamCount,
state.TargetVideoCodecTag); state.TargetVideoCodecTag,
state.IsTargetAVC);
if (mediaProfile != null) if (mediaProfile != null)
{ {
@ -2378,7 +2466,8 @@ namespace MediaBrowser.Api.Playback
Url = "https://mb3admin.com/admin/service/transcoding/report", Url = "https://mb3admin.com/admin/service/transcoding/report",
CancellationToken = CancellationToken.None, CancellationToken = CancellationToken.None,
LogRequest = false, LogRequest = false,
LogErrors = false LogErrors = false,
BufferContent = false
}; };
options.RequestContent = JsonSerializer.SerializeToString(dict); options.RequestContent = JsonSerializer.SerializeToString(dict);
options.RequestContentType = "application/json"; options.RequestContentType = "application/json";
@ -2461,7 +2550,8 @@ namespace MediaBrowser.Api.Playback
state.TargetRefFrames, state.TargetRefFrames,
state.TargetVideoStreamCount, state.TargetVideoStreamCount,
state.TargetAudioStreamCount, state.TargetAudioStreamCount,
state.TargetVideoCodecTag state.TargetVideoCodecTag,
state.IsTargetAVC
).FirstOrDefault() ?? string.Empty; ).FirstOrDefault() ?? string.Empty;
} }
@ -2516,6 +2606,7 @@ namespace MediaBrowser.Api.Playback
inputModifier += " " + GetFastSeekCommandLineParameter(state.Request); inputModifier += " " + GetFastSeekCommandLineParameter(state.Request);
inputModifier = inputModifier.Trim(); inputModifier = inputModifier.Trim();
//inputModifier += " -fflags +genpts+ignidx+igndts";
if (state.VideoRequest != null && genPts) if (state.VideoRequest != null && genPts)
{ {
inputModifier += " -fflags +genpts"; inputModifier += " -fflags +genpts";

@ -87,7 +87,8 @@ namespace MediaBrowser.Api.Playback.Hls
if (!FileSystem.FileExists(playlist)) 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 try
{ {
if (!FileSystem.FileExists(playlist)) if (!FileSystem.FileExists(playlist))
@ -104,13 +105,13 @@ namespace MediaBrowser.Api.Playback.Hls
throw; 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); await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false);
} }
} }
finally finally
{ {
ApiEntryPoint.Instance.TranscodingStartLock.Release(); transcodingLock.Release();
} }
} }
@ -128,10 +129,9 @@ namespace MediaBrowser.Api.Playback.Hls
var audioBitrate = state.OutputAudioBitrate ?? 0; var audioBitrate = state.OutputAudioBitrate ?? 0;
var videoBitrate = state.OutputVideoBitrate ?? 0; var videoBitrate = state.OutputVideoBitrate ?? 0;
var appendBaselineStream = false;
var baselineStreamBitrate = 64000; 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); job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType);
@ -151,9 +151,10 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
var text = reader.ReadToEnd(); var text = reader.ReadToEnd();
text = text.Replace("#EXTM3U", "#EXTM3U\n#EXT-X-PLAYLIST-TYPE:EVENT");
var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(UsCulture); 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); text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase);
return text; 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(); var builder = new StringBuilder();
@ -175,14 +176,6 @@ namespace MediaBrowser.Api.Playback.Hls
var playlistUrl = "hls/" + Path.GetFileName(firstPlaylist).Replace(".m3u8", "/stream.m3u8"); var playlistUrl = "hls/" + Path.GetFileName(firstPlaylist).Replace(".m3u8", "/stream.m3u8");
builder.AppendLine(playlistUrl); 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(); return builder.ToString();
} }
@ -190,32 +183,41 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
Logger.Debug("Waiting for {0} segments in {1}", segmentCount, playlist); 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 try
using (var fileStream = GetPlaylistFileStream(playlist))
{ {
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; using (var reader = new StreamReader(fileStream))
while (!reader.EndOfStream)
{ {
var line = await reader.ReadLineAsync().ConfigureAwait(false); var count = 0;
if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1) while (!reader.EndOfStream)
{ {
count++; var line = await reader.ReadLineAsync().ConfigureAwait(false);
if (count >= segmentCount)
if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
{ {
Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist); count++;
return; 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);
} }
} }

@ -171,14 +171,15 @@ namespace MediaBrowser.Api.Playback.Hls
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false); 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; var released = false;
try try
{ {
if (FileSystem.FileExists(segmentPath)) if (FileSystem.FileExists(segmentPath))
{ {
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
ApiEntryPoint.Instance.TranscodingStartLock.Release(); transcodingLock.Release();
released = true; released = true;
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false); return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
} }
@ -242,7 +243,7 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
if (!released) if (!released)
{ {
ApiEntryPoint.Instance.TranscodingStartLock.Release(); transcodingLock.Release();
} }
} }
@ -906,6 +907,7 @@ namespace MediaBrowser.Api.Playback.Hls
).Trim(); ).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}\"", 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, inputModifier,
GetInputArgument(state), GetInputArgument(state),
@ -922,11 +924,6 @@ namespace MediaBrowser.Api.Playback.Hls
).Trim(); ).Trim();
} }
protected override bool EnableThrottling(StreamState state)
{
return true;
}
/// <summary> /// <summary>
/// Gets the segment file extension. /// Gets the segment file extension.
/// </summary> /// </summary>

@ -68,8 +68,6 @@ namespace MediaBrowser.Api.Playback.Hls
[Api(Description = "Gets an Http live streaming segment file. Internal use only.")] [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
public class GetHlsVideoSegmentLegacy : VideoStreamRequest public class GetHlsVideoSegmentLegacy : VideoStreamRequest
{ {
// TODO: Deprecate with new iOS app
public string PlaylistId { get; set; } public string PlaylistId { get; set; }
/// <summary> /// <summary>
@ -113,7 +111,7 @@ namespace MediaBrowser.Api.Playback.Hls
var file = request.SegmentId + Path.GetExtension(Request.PathInfo); var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
file = Path.Combine(_config.ApplicationPaths.TranscodingTempPath, file); 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, "*") var playlistPath = Directory.EnumerateFiles(_config.ApplicationPaths.TranscodingTempPath, "*")
.FirstOrDefault(i => string.Equals(Path.GetExtension(i), ".m3u8", StringComparison.OrdinalIgnoreCase) && i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1); .FirstOrDefault(i => string.Equals(Path.GetExtension(i), ".m3u8", StringComparison.OrdinalIgnoreCase) && i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1);

@ -113,6 +113,8 @@ namespace MediaBrowser.Api.Playback.Hls
args += GetGraphicalSubtitleParam(state, codec); args += GetGraphicalSubtitleParam(state, codec);
} }
args += " -flags -global_header";
return args; return args;
} }

@ -17,6 +17,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Api.Playback namespace MediaBrowser.Api.Playback
{ {
@ -70,8 +71,9 @@ namespace MediaBrowser.Api.Playback
private readonly INetworkManager _networkManager; private readonly INetworkManager _networkManager;
private readonly IMediaEncoder _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly IJsonSerializer _json;
public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager, IMediaEncoder mediaEncoder, IUserManager userManager) public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager, IMediaEncoder mediaEncoder, IUserManager userManager, IJsonSerializer json)
{ {
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_deviceManager = deviceManager; _deviceManager = deviceManager;
@ -80,6 +82,7 @@ namespace MediaBrowser.Api.Playback
_networkManager = networkManager; _networkManager = networkManager;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
_userManager = userManager; _userManager = userManager;
_json = json;
} }
public object Get(GetBitrateTestBytes request) public object Get(GetBitrateTestBytes request)
@ -104,7 +107,7 @@ namespace MediaBrowser.Api.Playback
{ {
var authInfo = AuthorizationContext.GetAuthorizationInfo(Request); 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; var profile = request.DeviceProfile;
if (profile == null) if (profile == null)
@ -137,7 +140,7 @@ namespace MediaBrowser.Api.Playback
public void Post(CloseMediaSource request) public void Post(CloseMediaSource request)
{ {
var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId, CancellationToken.None); var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId);
Task.WaitAll(task); Task.WaitAll(task);
} }
@ -147,6 +150,8 @@ namespace MediaBrowser.Api.Playback
var profile = request.DeviceProfile; var profile = request.DeviceProfile;
//Logger.Info("GetPostedPlaybackInfo profile: {0}", _json.SerializeToString(profile));
if (profile == null) if (profile == null)
{ {
var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); var caps = _deviceManager.GetCapabilities(authInfo.DeviceId);

@ -17,6 +17,7 @@ using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommonIO; using CommonIO;
using ServiceStack;
namespace MediaBrowser.Api.Playback.Progressive namespace MediaBrowser.Api.Playback.Progressive
{ {
@ -26,12 +27,10 @@ namespace MediaBrowser.Api.Playback.Progressive
public abstract class BaseProgressiveStreamingService : BaseStreamingService public abstract class BaseProgressiveStreamingService : BaseStreamingService
{ {
protected readonly IImageProcessor ImageProcessor; 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) 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; ImageProcessor = imageProcessor;
HttpClient = httpClient;
} }
/// <summary> /// <summary>
@ -122,6 +121,25 @@ namespace MediaBrowser.Api.Playback.Progressive
var responseHeaders = new Dictionary<string, string>(); var responseHeaders = new Dictionary<string, string>();
if (request.Static && state.DirectStreamProvider != null)
{
AddDlnaHeaders(state, responseHeaders, true);
using (state)
{
var outputHeaders = new Dictionary<string, string>(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 // Static remote stream
if (request.Static && state.InputProtocol == MediaProtocol.Http) if (request.Static && state.InputProtocol == MediaProtocol.Http)
{ {
@ -129,8 +147,7 @@ namespace MediaBrowser.Api.Playback.Progressive
using (state) using (state)
{ {
return await GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource) return await GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).ConfigureAwait(false);
.ConfigureAwait(false);
} }
} }
@ -154,6 +171,19 @@ namespace MediaBrowser.Api.Playback.Progressive
using (state) using (state)
{ {
if (state.MediaSource.IsInfiniteStream)
{
var outputHeaders = new Dictionary<string, string>(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; TimeSpan? cacheDuration = null;
if (!string.IsNullOrEmpty(request.Tag)) if (!string.IsNullOrEmpty(request.Tag))
@ -345,7 +375,8 @@ namespace MediaBrowser.Api.Playback.Progressive
return streamResult; 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 try
{ {
TranscodingJob job; TranscodingJob job;
@ -376,7 +407,7 @@ namespace MediaBrowser.Api.Playback.Progressive
} }
finally finally
{ {
ApiEntryPoint.Instance.TranscodingStartLock.Release(); transcodingLock.Release();
} }
} }

@ -7,6 +7,7 @@ using CommonIO;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using System.Collections.Generic; using System.Collections.Generic;
using ServiceStack.Web; using ServiceStack.Web;
using MediaBrowser.Controller.Library;
namespace MediaBrowser.Api.Playback.Progressive namespace MediaBrowser.Api.Playback.Progressive
{ {
@ -23,6 +24,10 @@ namespace MediaBrowser.Api.Playback.Progressive
private const int BufferSize = 81920; private const int BufferSize = 81920;
private long _bytesWritten = 0; private long _bytesWritten = 0;
public long StartPosition { get; set; }
public bool AllowEndOfFile = true;
private IDirectStreamProvider _directStreamProvider;
public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken) public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken)
{ {
@ -34,6 +39,15 @@ namespace MediaBrowser.Api.Playback.Progressive
_cancellationToken = cancellationToken; _cancellationToken = cancellationToken;
} }
public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken)
{
_directStreamProvider = directStreamProvider;
_outputHeaders = outputHeaders;
_job = job;
_logger = logger;
_cancellationToken = cancellationToken;
}
public IDictionary<string, string> Options public IDictionary<string, string> Options
{ {
get 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) public async Task WriteToAsync(Stream outputStream)
{ {
try try
{ {
if (_directStreamProvider != null)
{
await _directStreamProvider.CopyToAsync(outputStream, _cancellationToken).ConfigureAwait(false);
return;
}
var eofCount = 0; 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)
{
inputStream.Position = StartPosition;
}
while (eofCount < 15 || !AllowEndOfFile)
{ {
var bytesRead = await CopyToAsyncInternal(fs, outputStream, BufferSize, _cancellationToken).ConfigureAwait(false); var bytesRead = await CopyToAsyncInternal(inputStream, outputStream, BufferSize, _cancellationToken).ConfigureAwait(false);
//var position = fs.Position; //var position = fs.Position;
//_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);

@ -37,6 +37,7 @@ namespace MediaBrowser.Api.Playback
/// </summary> /// </summary>
/// <value>The log file stream.</value> /// <value>The log file stream.</value>
public Stream LogFileStream { get; set; } public Stream LogFileStream { get; set; }
public IDirectStreamProvider DirectStreamProvider { get; set; }
public string InputContainer { get; set; } public string InputContainer { get; set; }
@ -62,7 +63,6 @@ namespace MediaBrowser.Api.Playback
get { return Request is VideoStreamRequest; } get { return Request is VideoStreamRequest; }
} }
public bool IsInputVideo { get; set; } public bool IsInputVideo { get; set; }
public bool IsInputArchive { get; set; }
public VideoType VideoType { get; set; } public VideoType VideoType { get; set; }
public IsoType? IsoType { get; set; } public IsoType? IsoType { get; set; }
@ -88,9 +88,17 @@ namespace MediaBrowser.Api.Playback
return 10; return 10;
} }
if (!RunTimeTicks.HasValue)
{
return 3;
}
return 6; return 6;
} }
if (!RunTimeTicks.HasValue)
{
return 3;
}
return 3; return 3;
} }
} }
@ -112,6 +120,7 @@ namespace MediaBrowser.Api.Playback
public string OutputVideoSync = "-1"; public string OutputVideoSync = "-1";
public List<string> SupportedAudioCodecs { get; set; } public List<string> SupportedAudioCodecs { get; set; }
public List<string> SupportedVideoCodecs { get; set; }
public string UserAgent { get; set; } public string UserAgent { get; set; }
public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger) public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger)
@ -119,6 +128,7 @@ namespace MediaBrowser.Api.Playback
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_logger = logger; _logger = logger;
SupportedAudioCodecs = new List<string>(); SupportedAudioCodecs = new List<string>();
SupportedVideoCodecs = new List<string>();
PlayableStreamFileNames = new List<string>(); PlayableStreamFileNames = new List<string>();
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
} }
@ -215,7 +225,7 @@ namespace MediaBrowser.Api.Playback
{ {
try try
{ {
await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false); await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -506,5 +516,18 @@ namespace MediaBrowser.Api.Playback
return false; return false;
} }
} }
public bool? IsTargetAVC
{
get
{
if (Request.Static)
{
return VideoStream == null ? null : VideoStream.IsAVC;
}
return true;
}
}
} }
} }

@ -517,10 +517,6 @@ namespace MediaBrowser.Api.Reports
internalHeader = HeaderMetadata.Album; internalHeader = HeaderMetadata.Album;
break; break;
case HeaderMetadata.Countries:
option.Column = (i, r) => this.GetListAsString(this.GetObject<IHasProductionLocations, List<string>>(i, (x) => x.ProductionLocations));
break;
case HeaderMetadata.Disc: case HeaderMetadata.Disc:
option.Column = (i, r) => i.ParentIndexNumber; option.Column = (i, r) => i.ParentIndexNumber;
break; break;

@ -36,7 +36,6 @@ namespace MediaBrowser.Api.Reports
result = this.GetResultStudios(result, items, topItem); result = this.GetResultStudios(result, items, topItem);
result = this.GetResultPersons(result, items, topItem); result = this.GetResultPersons(result, items, topItem);
result = this.GetResultProductionYears(result, items, topItem); result = this.GetResultProductionYears(result, items, topItem);
result = this.GetResulProductionLocations(result, items, topItem);
result = this.GetResultCommunityRatings(result, items, topItem); result = this.GetResultCommunityRatings(result, items, topItem);
result = this.GetResultParentalRatings(result, items, topItem); result = this.GetResultParentalRatings(result, items, topItem);
@ -100,30 +99,6 @@ namespace MediaBrowser.Api.Reports
} }
} }
/// <summary> Gets resul production locations. </summary>
/// <param name="result"> The result. </param>
/// <param name="items"> The items. </param>
/// <param name="topItem"> The top item. </param>
/// <returns> The resul production locations. </returns>
private ReportStatResult GetResulProductionLocations(ReportStatResult result, BaseItem[] items, int topItem = 5)
{
this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.Countries), topItem,
items.OfType<IHasProductionLocations>()
.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;
}
/// <summary> Gets result community ratings. </summary> /// <summary> Gets result community ratings. </summary>
/// <param name="result"> The result. </param> /// <param name="result"> The result. </param>
/// <param name="items"> The items. </param> /// <param name="items"> The items. </param>

@ -81,7 +81,8 @@ namespace MediaBrowser.Api
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
IncludeItemTypes = includeTypes.Select(i => i.Name).ToArray(), IncludeItemTypes = includeTypes.Select(i => i.Name).ToArray(),
Recursive = true Recursive = true,
DtoOptions = dtoOptions
}; };
// ExcludeArtistIds // ExcludeArtistIds

@ -88,8 +88,6 @@ namespace MediaBrowser.Api
var result = new StartupConfiguration var result = new StartupConfiguration
{ {
UICulture = _config.Configuration.UICulture, UICulture = _config.Configuration.UICulture,
EnableInternetProviders = _config.Configuration.EnableInternetProviders,
SaveLocalMeta = _config.Configuration.SaveLocalMeta,
MetadataCountryCode = _config.Configuration.MetadataCountryCode, MetadataCountryCode = _config.Configuration.MetadataCountryCode,
PreferredMetadataLanguage = _config.Configuration.PreferredMetadataLanguage PreferredMetadataLanguage = _config.Configuration.PreferredMetadataLanguage
}; };
@ -116,15 +114,16 @@ namespace MediaBrowser.Api
config.EnableLocalizedGuids = true; config.EnableLocalizedGuids = true;
config.EnableStandaloneMusicKeys = true; config.EnableStandaloneMusicKeys = true;
config.EnableCaseSensitiveItemIds = true; config.EnableCaseSensitiveItemIds = true;
//config.EnableFolderView = true; config.EnableFolderView = true;
config.SchemaVersion = 109; config.SchemaVersion = 109;
config.EnableSimpleArtistDetection = true;
config.SkipDeserializationForBasicTypes = true;
config.SkipDeserializationForPrograms = true;
} }
public void Post(UpdateStartupConfiguration request) public void Post(UpdateStartupConfiguration request)
{ {
_config.Configuration.UICulture = request.UICulture; _config.Configuration.UICulture = request.UICulture;
_config.Configuration.EnableInternetProviders = request.EnableInternetProviders;
_config.Configuration.SaveLocalMeta = request.SaveLocalMeta;
_config.Configuration.MetadataCountryCode = request.MetadataCountryCode; _config.Configuration.MetadataCountryCode = request.MetadataCountryCode;
_config.Configuration.PreferredMetadataLanguage = request.PreferredMetadataLanguage; _config.Configuration.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
_config.SaveConfiguration(); _config.SaveConfiguration();
@ -148,12 +147,6 @@ namespace MediaBrowser.Api
{ {
var user = _userManager.Users.First(); var user = _userManager.Users.First();
// TODO: This should be handled internally by xbmc metadata
const string metadataKey = "xbmcmetadata";
var metadata = _config.GetConfiguration<XbmcMetadataOptions>(metadataKey);
metadata.UserId = user.Id.ToString("N");
_config.SaveConfiguration(metadataKey, metadata);
user.Name = request.Name; user.Name = request.Name;
await _userManager.UpdateUser(user).ConfigureAwait(false); await _userManager.UpdateUser(user).ConfigureAwait(false);
@ -221,8 +214,6 @@ namespace MediaBrowser.Api
public class StartupConfiguration public class StartupConfiguration
{ {
public string UICulture { get; set; } public string UICulture { get; set; }
public bool EnableInternetProviders { get; set; }
public bool SaveLocalMeta { get; set; }
public string MetadataCountryCode { get; set; } public string MetadataCountryCode { get; set; }
public string PreferredMetadataLanguage { get; set; } public string PreferredMetadataLanguage { get; set; }
public string LiveTvTunerType { get; set; } public string LiveTvTunerType { get; set; }

@ -148,7 +148,7 @@ namespace MediaBrowser.Api.Subtitles
{ {
var item = (Video)_libraryManager.GetItemById(new Guid(request.Id)); 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(); var builder = new StringBuilder();

@ -302,6 +302,8 @@ namespace MediaBrowser.Api
(!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
var dtoOptions = GetDtoOptions(request);
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{ {
Limit = request.Limit, Limit = request.Limit,
@ -309,12 +311,11 @@ namespace MediaBrowser.Api
{ {
typeof(Series).Name typeof(Series).Name
}, },
SimilarTo = item SimilarTo = item,
DtoOptions = dtoOptions
}).ToList(); }).ToList();
var dtoOptions = GetDtoOptions(request);
var result = new QueryResult<BaseItemDto> var result = new QueryResult<BaseItemDto>
{ {
Items = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false)).ToArray(), 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 parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
var options = GetDtoOptions(request);
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{ {
IncludeItemTypes = new[] { typeof(Episode).Name }, IncludeItemTypes = new[] { typeof(Episode).Name },
@ -342,12 +345,11 @@ namespace MediaBrowser.Api
StartIndex = request.StartIndex, StartIndex = request.StartIndex,
Limit = request.Limit, Limit = request.Limit,
ParentId = parentIdGuid, ParentId = parentIdGuid,
Recursive = true Recursive = true,
DtoOptions = options
}).ToList(); }).ToList();
var options = GetDtoOptions(request);
var returnItems = (await _dtoService.GetBaseItemDtos(itemsResult, options, user).ConfigureAwait(false)).ToArray(); var returnItems = (await _dtoService.GetBaseItemDtos(itemsResult, options, user).ConfigureAwait(false)).ToArray();
var result = new ItemsResult var result = new ItemsResult
@ -478,7 +480,7 @@ namespace MediaBrowser.Api
} }
else else
{ {
episodes = series.GetSeasonEpisodes(user, season); episodes = series.GetSeasonEpisodes(season, user);
} }
} }
else else

@ -205,6 +205,7 @@ namespace MediaBrowser.Api.UserLibrary
private void SetItemCounts(BaseItemDto dto, ItemCounts counts) private void SetItemCounts(BaseItemDto dto, ItemCounts counts)
{ {
dto.ChildCount = counts.ItemCount; dto.ChildCount = counts.ItemCount;
dto.ProgramCount = counts.ProgramCount;
dto.SeriesCount = counts.SeriesCount; dto.SeriesCount = counts.SeriesCount;
dto.EpisodeCount = counts.EpisodeCount; dto.EpisodeCount = counts.EpisodeCount;
dto.MovieCount = counts.MovieCount; dto.MovieCount = counts.MovieCount;

@ -99,8 +99,10 @@ namespace MediaBrowser.Api.UserLibrary
private async Task<ItemsResult> GetItems(GetItems request) private async Task<ItemsResult> GetItems(GetItems request)
{ {
var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; 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) if (result == null)
{ {
@ -112,8 +114,6 @@ namespace MediaBrowser.Api.UserLibrary
throw new InvalidOperationException("GetItemsToSerialize result.Items returned null"); throw new InvalidOperationException("GetItemsToSerialize result.Items returned null");
} }
var dtoOptions = GetDtoOptions(request);
var dtoList = await _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user).ConfigureAwait(false); var dtoList = await _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user).ConfigureAwait(false);
if (dtoList == null) if (dtoList == null)
@ -131,10 +131,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <summary> /// <summary>
/// Gets the items to serialize. /// Gets the items to serialize.
/// </summary> /// </summary>
/// <param name="request">The request.</param> private async Task<QueryResult<BaseItem>> GetQueryResult(GetItems request, DtoOptions dtoOptions, User user)
/// <param name="user">The user.</param>
/// <returns>IEnumerable{BaseItem}.</returns>
private async Task<QueryResult<BaseItem>> GetQueryResult(GetItems request, User user)
{ {
var item = string.IsNullOrEmpty(request.ParentId) ? var item = string.IsNullOrEmpty(request.ParentId) ?
user == null ? _libraryManager.RootFolder : user.RootFolder : user == null ? _libraryManager.RootFolder : user.RootFolder :
@ -159,14 +156,14 @@ namespace MediaBrowser.Api.UserLibrary
if (request.Recursive || !string.IsNullOrEmpty(request.Ids) || user == null) 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; var userRoot = item as UserRootFolder;
if (userRoot == null) if (userRoot == null)
{ {
return await folder.GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); return await folder.GetItems(GetItemsQuery(request, dtoOptions, user)).ConfigureAwait(false);
} }
IEnumerable<BaseItem> items = folder.GetChildren(user, true); IEnumerable<BaseItem> items = folder.GetChildren(user, true);
@ -180,7 +177,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) var query = new InternalItemsQuery(user)
{ {
@ -241,7 +238,8 @@ namespace MediaBrowser.Api.UserLibrary
AiredDuringSeason = request.AiredDuringSeason, AiredDuringSeason = request.AiredDuringSeason,
AlbumArtistStartsWithOrGreater = request.AlbumArtistStartsWithOrGreater, AlbumArtistStartsWithOrGreater = request.AlbumArtistStartsWithOrGreater,
EnableTotalRecordCount = request.EnableTotalRecordCount, EnableTotalRecordCount = request.EnableTotalRecordCount,
ExcludeItemIds = request.GetExcludeItemIds() ExcludeItemIds = request.GetExcludeItemIds(),
DtoOptions = dtoOptions
}; };
if (!string.IsNullOrWhiteSpace(request.Ids)) if (!string.IsNullOrWhiteSpace(request.Ids))

@ -30,6 +30,7 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommonIO; using CommonIO;
using MediaBrowser.Common.IO;
namespace MediaBrowser.Common.Implementations namespace MediaBrowser.Common.Implementations
{ {
@ -192,6 +193,8 @@ namespace MediaBrowser.Common.Implementations
get { return Environment.OSVersion.VersionString; } get { return Environment.OSVersion.VersionString; }
} }
public IMemoryStreamProvider MemoryStreamProvider { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationHost{TApplicationPathsType}"/> class. /// Initializes a new instance of the <see cref="BaseApplicationHost{TApplicationPathsType}"/> class.
/// </summary> /// </summary>
@ -231,6 +234,8 @@ namespace MediaBrowser.Common.Implementations
JsonSerializer = CreateJsonSerializer(); JsonSerializer = CreateJsonSerializer();
MemoryStreamProvider = new MemoryStreamProvider();
OnLoggerLoaded(true); OnLoggerLoaded(true);
LogManager.LoggerLoaded += (s, e) => OnLoggerLoaded(false); LogManager.LoggerLoaded += (s, e) => OnLoggerLoaded(false);
@ -456,6 +461,7 @@ namespace MediaBrowser.Common.Implementations
RegisterSingleInstance(JsonSerializer); RegisterSingleInstance(JsonSerializer);
RegisterSingleInstance(XmlSerializer); RegisterSingleInstance(XmlSerializer);
RegisterSingleInstance(MemoryStreamProvider);
RegisterSingleInstance(LogManager); RegisterSingleInstance(LogManager);
RegisterSingleInstance(Logger); RegisterSingleInstance(Logger);
@ -464,7 +470,7 @@ namespace MediaBrowser.Common.Implementations
RegisterSingleInstance(FileSystemManager); RegisterSingleInstance(FileSystemManager);
HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager); HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamProvider);
RegisterSingleInstance(HttpClient); RegisterSingleInstance(HttpClient);
NetworkManager = CreateNetworkManager(LogManager.GetLogger("NetworkManager")); NetworkManager = CreateNetworkManager(LogManager.GetLogger("NetworkManager"));

@ -42,6 +42,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IMemoryStreamProvider _memoryStreamProvider;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HttpClientManager" /> class. /// Initializes a new instance of the <see cref="HttpClientManager" /> class.
@ -52,7 +53,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
/// <exception cref="System.ArgumentNullException">appPaths /// <exception cref="System.ArgumentNullException">appPaths
/// or /// or
/// logger</exception> /// logger</exception>
public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem) public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem, IMemoryStreamProvider memoryStreamProvider)
{ {
if (appPaths == null) if (appPaths == null)
{ {
@ -65,6 +66,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
_logger = logger; _logger = logger;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_memoryStreamProvider = memoryStreamProvider;
_appPaths = appPaths; _appPaths = appPaths;
// http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c // 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, Url = url,
ResourcePool = resourcePool, ResourcePool = resourcePool,
CancellationToken = cancellationToken, CancellationToken = cancellationToken,
BufferContent = resourcePool != null
}); });
} }
@ -293,12 +296,9 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
/// </exception> /// </exception>
public async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod) public async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod)
{ {
HttpResponseInfo response;
if (options.CacheMode == CacheMode.None) if (options.CacheMode == CacheMode.None)
{ {
response = await SendAsyncInternal(options, httpMethod).ConfigureAwait(false); return await SendAsyncInternal(options, httpMethod).ConfigureAwait(false);
return response;
} }
var url = options.Url; var url = options.Url;
@ -306,7 +306,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash); 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) if (response != null)
{ {
return response; return response;
@ -332,7 +332,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
{ {
using (var stream = _fileSystem.GetFileStream(responseCachePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) 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); await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0; memoryStream.Position = 0;
@ -366,7 +366,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
using (var responseStream = response.Content) using (var responseStream = response.Content)
{ {
var memoryStream = new MemoryStream(); var memoryStream = _memoryStreamProvider.CreateNew();
await responseStream.CopyToAsync(memoryStream).ConfigureAwait(false); await responseStream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0; memoryStream.Position = 0;
@ -458,7 +458,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
using (var stream = httpResponse.GetResponseStream()) using (var stream = httpResponse.GetResponseStream())
{ {
var memoryStream = new MemoryStream(); var memoryStream = _memoryStreamProvider.CreateNew();
await stream.CopyToAsync(memoryStream).ConfigureAwait(false); await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
@ -553,7 +553,8 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
{ {
Url = url, Url = url,
ResourcePool = resourcePool, ResourcePool = resourcePool,
CancellationToken = cancellationToken CancellationToken = cancellationToken,
BufferContent = resourcePool != null
}, postData); }, postData);
} }
@ -563,7 +564,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
/// </summary> /// </summary>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>Task{System.String}.</returns> /// <returns>Task{System.String}.</returns>
/// <exception cref="System.ArgumentNullException">progress</exception>
public async Task<string> GetTempFile(HttpRequestOptions options) public async Task<string> GetTempFile(HttpRequestOptions options)
{ {
var response = await GetTempFileResponse(options).ConfigureAwait(false); var response = await GetTempFileResponse(options).ConfigureAwait(false);

@ -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);
}
}
}

@ -72,6 +72,11 @@ namespace MediaBrowser.Common.Implementations.Logging
/// <param name="paramList">The param list.</param> /// <param name="paramList">The param list.</param>
public void Debug(string message, params object[] paramList) public void Debug(string message, params object[] paramList)
{ {
if (_logManager.LogSeverity == LogSeverity.Info)
{
return;
}
_logger.Debug(message, paramList); _logger.Debug(message, paramList);
} }
@ -137,6 +142,11 @@ namespace MediaBrowser.Common.Implementations.Logging
/// <param name="additionalContent">Content of the additional.</param> /// <param name="additionalContent">Content of the additional.</param>
public void LogMultiline(string message, LogSeverity severity, StringBuilder additionalContent) public void LogMultiline(string message, LogSeverity severity, StringBuilder additionalContent)
{ {
if (severity == LogSeverity.Debug && _logManager.LogSeverity == LogSeverity.Info)
{
return;
}
additionalContent.Insert(0, message + Environment.NewLine); additionalContent.Insert(0, message + Environment.NewLine);
const char tabChar = '\t'; const char tabChar = '\t';

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -14,6 +14,7 @@
<ProductVersion>10.0.0</ProductVersion> <ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion> <SchemaVersion>2.0</SchemaVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkProfile />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -23,7 +24,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType> <DebugType>none</DebugType>
@ -51,11 +52,15 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath> <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.1.0.0\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="MoreLinq"> <Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath> <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
</Reference> </Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.3.6\lib\net45\NLog.dll</HintPath> <HintPath>..\packages\NLog.4.3.8\lib\net45\NLog.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Patterns.Logging"> <Reference Include="Patterns.Logging">
@ -65,8 +70,8 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath> <HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>
</Reference> </Reference>
<Reference Include="SimpleInjector, Version=3.2.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL"> <Reference Include="SimpleInjector, Version=3.2.2.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<HintPath>..\packages\SimpleInjector.3.2.0\lib\net45\SimpleInjector.dll</HintPath> <HintPath>..\packages\SimpleInjector.3.2.2\lib\net45\SimpleInjector.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
@ -75,6 +80,9 @@
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Net" /> <Reference Include="System.Net" />
<Reference Include="System.Text.Json">
<HintPath>..\ThirdParty\fastjsonparser\System.Text.Json.dll</HintPath>
</Reference>
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="ServiceStack.Text"> <Reference Include="ServiceStack.Text">
<HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath> <HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
@ -93,6 +101,7 @@
<Compile Include="HttpClientManager\HttpClientInfo.cs" /> <Compile Include="HttpClientManager\HttpClientInfo.cs" />
<Compile Include="HttpClientManager\HttpClientManager.cs" /> <Compile Include="HttpClientManager\HttpClientManager.cs" />
<Compile Include="IO\IsoManager.cs" /> <Compile Include="IO\IsoManager.cs" />
<Compile Include="IO\MemoryStreamProvider.cs" />
<Compile Include="Logging\LogHelper.cs" /> <Compile Include="Logging\LogHelper.cs" />
<Compile Include="Logging\NLogger.cs" /> <Compile Include="Logging\NLogger.cs" />
<Compile Include="Logging\NlogManager.cs" /> <Compile Include="Logging\NlogManager.cs" />

@ -334,7 +334,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
trigger.Stop(); trigger.Stop();
TaskManager.QueueScheduledTask(ScheduledTask); TaskManager.QueueScheduledTask(ScheduledTask, e.Argument);
await Task.Delay(1000).ConfigureAwait(false); await Task.Delay(1000).ConfigureAwait(false);
@ -390,13 +390,13 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
try try
{ {
var localTask = ScheduledTask.Execute(CurrentCancellationTokenSource.Token, progress);
if (options != null && options.MaxRuntimeMs.HasValue) if (options != null && options.MaxRuntimeMs.HasValue)
{ {
CurrentCancellationTokenSource.CancelAfter(options.MaxRuntimeMs.Value); CurrentCancellationTokenSource.CancelAfter(options.MaxRuntimeMs.Value);
} }
var localTask = ScheduledTask.Execute(CurrentCancellationTokenSource.Token, progress);
await localTask.ConfigureAwait(false); await localTask.ConfigureAwait(false);
status = TaskCompletionStatus.Completed; status = TaskCompletionStatus.Completed;
@ -423,10 +423,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
CurrentProgress = null; CurrentProgress = null;
OnTaskCompleted(startTime, endTime, status, failureException); OnTaskCompleted(startTime, endTime, status, failureException);
// Bad practice, i know. But we keep a lot in memory, unfortunately.
GC.Collect(2, GCCollectionMode.Forced, true);
GC.Collect(2, GCCollectionMode.Forced, true);
} }
/// <summary> /// <summary>

@ -142,9 +142,15 @@ namespace MediaBrowser.Common.Implementations.Security
} }
set set
{ {
if (value != LicenseFile.RegKey) var newValue = value;
if (newValue != null)
{ {
LicenseFile.RegKey = value; newValue = newValue.Trim();
}
if (newValue != LicenseFile.RegKey)
{
LicenseFile.RegKey = newValue;
LicenseFile.Save(); LicenseFile.Save();
// re-load registration info // re-load registration info
@ -163,7 +169,8 @@ namespace MediaBrowser.Common.Implementations.Security
var options = new HttpRequestOptions() var options = new HttpRequestOptions()
{ {
Url = AppstoreRegUrl, Url = AppstoreRegUrl,
CancellationToken = CancellationToken.None CancellationToken = CancellationToken.None,
BufferContent = false
}; };
options.RequestHeaders.Add("X-Emby-Token", _appHost.SystemId); options.RequestHeaders.Add("X-Emby-Token", _appHost.SystemId);
options.RequestContent = parameters; options.RequestContent = parameters;
@ -263,7 +270,8 @@ namespace MediaBrowser.Common.Implementations.Security
Url = MBValidateUrl, Url = MBValidateUrl,
// Seeing block length errors // Seeing block length errors
EnableHttpCompression = false EnableHttpCompression = false,
BufferContent = false
}; };
options.SetPostData(data); options.SetPostData(data);

@ -14,16 +14,14 @@ namespace MediaBrowser.Common.Implementations.Updates
{ {
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private TimeSpan _cacheLength;
public GithubUpdater(IHttpClient httpClient, IJsonSerializer jsonSerializer, TimeSpan cacheLength) public GithubUpdater(IHttpClient httpClient, IJsonSerializer jsonSerializer)
{ {
_httpClient = httpClient; _httpClient = httpClient;
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_cacheLength = cacheLength;
} }
public async Task<CheckForUpdateResult> CheckForUpdateResult(string organzation, string repository, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename, CancellationToken cancellationToken) public async Task<CheckForUpdateResult> CheckForUpdateResult(string organzation, string repository, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename, TimeSpan cacheLength, CancellationToken cancellationToken)
{ {
var url = string.Format("https://api.github.com/repos/{0}/{1}/releases", organzation, repository); var url = string.Format("https://api.github.com/repos/{0}/{1}/releases", organzation, repository);
@ -32,13 +30,14 @@ namespace MediaBrowser.Common.Implementations.Updates
Url = url, Url = url,
EnableKeepAlive = false, EnableKeepAlive = false,
CancellationToken = cancellationToken, CancellationToken = cancellationToken,
UserAgent = "Emby/3.0" UserAgent = "Emby/3.0",
BufferContent = false
}; };
if (_cacheLength.Ticks > 0) if (cacheLength.Ticks > 0)
{ {
options.CacheMode = CacheMode.Unconditional; options.CacheMode = CacheMode.Unconditional;
options.CacheLength = _cacheLength; options.CacheLength = cacheLength;
} }
using (var stream = await _httpClient.Get(options).ConfigureAwait(false)) using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
@ -107,15 +106,10 @@ namespace MediaBrowser.Common.Implementations.Updates
Url = url, Url = url,
EnableKeepAlive = false, EnableKeepAlive = false,
CancellationToken = cancellationToken, CancellationToken = cancellationToken,
UserAgent = "Emby/3.0" UserAgent = "Emby/3.0",
BufferContent = false
}; };
if (_cacheLength.Ticks > 0)
{
options.CacheMode = CacheMode.Unconditional;
options.CacheLength = _cacheLength;
}
using (var stream = await _httpClient.Get(options).ConfigureAwait(false)) using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
{ {
var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream); var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);

@ -148,14 +148,10 @@ namespace MediaBrowser.Common.Implementations.Updates
/// <summary> /// <summary>
/// Gets all available packages. /// Gets all available packages.
/// </summary> /// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="withRegistration">if set to <c>true</c> [with registration].</param>
/// <param name="packageType">Type of the package.</param>
/// <param name="applicationVersion">The application version.</param>
/// <returns>Task{List{PackageInfo}}.</returns> /// <returns>Task{List{PackageInfo}}.</returns>
public async Task<IEnumerable<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken, public async Task<IEnumerable<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
bool withRegistration = true, bool withRegistration = true,
PackageType? packageType = null, string packageType = null,
Version applicationVersion = null) Version applicationVersion = null)
{ {
var data = new Dictionary<string, string> var data = new Dictionary<string, string>
@ -293,7 +289,7 @@ namespace MediaBrowser.Common.Implementations.Updates
return packages; return packages;
} }
protected IEnumerable<PackageInfo> FilterPackages(List<PackageInfo> packages, PackageType? packageType, Version applicationVersion) protected IEnumerable<PackageInfo> FilterPackages(List<PackageInfo> packages, string packageType, Version applicationVersion)
{ {
foreach (var package in packages) foreach (var package in packages)
{ {
@ -301,9 +297,9 @@ namespace MediaBrowser.Common.Implementations.Updates
.OrderByDescending(GetPackageVersion).ToList(); .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 // If an app version was supplied, filter the versions for each package to only include supported versions

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="CommonIO" version="1.0.0.9" targetFramework="net45" /> <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
<package id="Microsoft.IO.RecyclableMemoryStream" version="1.1.0.0" targetFramework="net45" />
<package id="morelinq" version="1.4.0" targetFramework="net45" /> <package id="morelinq" version="1.4.0" targetFramework="net45" />
<package id="NLog" version="4.3.6" targetFramework="net45" /> <package id="NLog" version="4.3.8" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
<package id="SimpleInjector" version="3.2.0" targetFramework="net45" /> <package id="SimpleInjector" version="3.2.2" targetFramework="net45" />
</packages> </packages>

@ -0,0 +1,11 @@
using System.IO;
namespace MediaBrowser.Common.IO
{
public interface IMemoryStreamProvider
{
MemoryStream CreateNew();
MemoryStream CreateNew(int capacity);
MemoryStream CreateNew(byte[] buffer);
}
}

@ -60,6 +60,7 @@
<Compile Include="Extensions\BaseExtensions.cs" /> <Compile Include="Extensions\BaseExtensions.cs" />
<Compile Include="Extensions\ResourceNotFoundException.cs" /> <Compile Include="Extensions\ResourceNotFoundException.cs" />
<Compile Include="IDependencyContainer.cs" /> <Compile Include="IDependencyContainer.cs" />
<Compile Include="IO\IMemoryStreamProvider.cs" />
<Compile Include="IO\ProgressStream.cs" /> <Compile Include="IO\ProgressStream.cs" />
<Compile Include="IO\StreamDefaults.cs" /> <Compile Include="IO\StreamDefaults.cs" />
<Compile Include="Configuration\IApplicationPaths.cs" /> <Compile Include="Configuration\IApplicationPaths.cs" />

@ -51,7 +51,7 @@ namespace MediaBrowser.Common.Updates
/// <returns>Task{List{PackageInfo}}.</returns> /// <returns>Task{List{PackageInfo}}.</returns>
Task<IEnumerable<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken, Task<IEnumerable<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
bool withRegistration = true, bool withRegistration = true,
PackageType? packageType = null, string packageType = null,
Version applicationVersion = null); Version applicationVersion = null);
/// <summary> /// <summary>

@ -31,6 +31,8 @@ namespace MediaBrowser.Controller.Channels
/// <returns>ChannelFeatures.</returns> /// <returns>ChannelFeatures.</returns>
ChannelFeatures GetChannelFeatures(string id); ChannelFeatures GetChannelFeatures(string id);
bool SupportsSync(string channelId);
/// <summary> /// <summary>
/// Gets all channel features. /// Gets all channel features.
/// </summary> /// </summary>
@ -132,15 +134,5 @@ namespace MediaBrowser.Controller.Channels
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>BaseItemDto.</returns> /// <returns>BaseItemDto.</returns>
Task<BaseItemDto> GetChannelFolder(string userId, CancellationToken cancellationToken); Task<BaseItemDto> GetChannelFolder(string userId, CancellationToken cancellationToken);
/// <summary>
/// Downloads the channel item.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="destinationPath">The destination path.</param>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task DownloadChannelItem(BaseItem item, string destinationPath, IProgress<double> progress, CancellationToken cancellationToken);
} }
} }

@ -1,19 +0,0 @@
using MediaBrowser.Model.Chapters;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Chapters
{
public class ChapterResponse
{
/// <summary>
/// Gets or sets the chapters.
/// </summary>
/// <value>The chapters.</value>
public List<RemoteChapterInfo> Chapters { get; set; }
public ChapterResponse()
{
Chapters = new List<RemoteChapterInfo>();
}
}
}

@ -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<string, string> ProviderIds { get; set; }
public bool SearchAllProviders { get; set; }
public ChapterSearchRequest()
{
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
}
}

@ -1,6 +1,4 @@
using MediaBrowser.Controller.Entities; using System.Collections.Generic;
using MediaBrowser.Model.Chapters;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
@ -13,12 +11,6 @@ namespace MediaBrowser.Controller.Chapters
/// </summary> /// </summary>
public interface IChapterManager public interface IChapterManager
{ {
/// <summary>
/// Adds the parts.
/// </summary>
/// <param name="chapterProviders">The chapter providers.</param>
void AddParts(IEnumerable<IChapterProvider> chapterProviders);
/// <summary> /// <summary>
/// Gets the chapters. /// Gets the chapters.
/// </summary> /// </summary>
@ -35,43 +27,6 @@ namespace MediaBrowser.Controller.Chapters
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task SaveChapters(string itemId, List<ChapterInfo> chapters, CancellationToken cancellationToken); Task SaveChapters(string itemId, List<ChapterInfo> chapters, CancellationToken cancellationToken);
/// <summary>
/// Searches the specified video.
/// </summary>
/// <param name="video">The video.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RemoteChapterResult}}.</returns>
Task<IEnumerable<RemoteChapterResult>> Search(Video video, CancellationToken cancellationToken);
/// <summary>
/// Searches the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RemoteChapterResult}}.</returns>
Task<IEnumerable<RemoteChapterResult>> Search(ChapterSearchRequest request, CancellationToken cancellationToken);
/// <summary>
/// Gets the chapters.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{ChapterResponse}.</returns>
Task<ChapterResponse> GetChapters(string id, CancellationToken cancellationToken);
/// <summary>
/// Gets the providers.
/// </summary>
/// <param name="itemId">The item identifier.</param>
/// <returns>IEnumerable{ChapterProviderInfo}.</returns>
IEnumerable<ChapterProviderInfo> GetProviders(string itemId);
/// <summary>
/// Gets the providers.
/// </summary>
/// <returns>IEnumerable{ChapterProviderInfo}.</returns>
IEnumerable<ChapterProviderInfo> GetProviders();
/// <summary> /// <summary>
/// Gets the configuration. /// Gets the configuration.
/// </summary> /// </summary>

@ -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
{
/// <summary>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
string Name { get; }
/// <summary>
/// Gets the supported media types.
/// </summary>
/// <value>The supported media types.</value>
IEnumerable<VideoContentType> SupportedMediaTypes { get; }
/// <summary>
/// Searches the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RemoteChapterResult}}.</returns>
Task<IEnumerable<RemoteChapterResult>> Search(ChapterSearchRequest request, CancellationToken cancellationToken);
/// <summary>
/// Gets the chapters.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{ChapterResponse}.</returns>
Task<ChapterResponse> GetChapters(string id, CancellationToken cancellationToken);
}
}

@ -1,6 +1,6 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Server.Implementations.Collections namespace MediaBrowser.Controller.Collections
{ {
public class ManualCollectionsFolder : BasePluginFolder, IHiddenFromDisplay public class ManualCollectionsFolder : BasePluginFolder, IHiddenFromDisplay
{ {

@ -1,10 +1,20 @@
using System; using System;
using System.Collections.Generic;
using System.Net;
using MediaBrowser.Model.Events;
namespace MediaBrowser.Controller.Dlna namespace MediaBrowser.Controller.Dlna
{ {
public interface IDeviceDiscovery public interface IDeviceDiscovery
{ {
event EventHandler<SsdpMessageEventArgs> DeviceDiscovered; event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered;
event EventHandler<SsdpMessageEventArgs> DeviceLeft; event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
}
public class UpnpDeviceInfo
{
public Uri Location { get; set; }
public Dictionary<string, string> Headers { get; set; }
public IPEndPoint LocalEndPoint { get; set; }
} }
} }

@ -4,6 +4,5 @@ namespace MediaBrowser.Controller.Dlna
{ {
public interface ISsdpHandler public interface ISsdpHandler
{ {
event EventHandler<SsdpMessageEventArgs> MessageReceived;
} }
} }

@ -34,6 +34,12 @@ namespace MediaBrowser.Controller.Entities
} }
} }
[IgnoreDataMember]
public override bool IsPhysicalRoot
{
get { return true; }
}
public override bool CanDelete() public override bool CanDelete()
{ {
return false; return false;

@ -23,8 +23,7 @@ namespace MediaBrowser.Controller.Entities.Audio
IHasMusicGenres, IHasMusicGenres,
IHasLookupInfo<SongInfo>, IHasLookupInfo<SongInfo>,
IHasMediaSources, IHasMediaSources,
IThemeMedia, IThemeMedia
IArchivable
{ {
public List<ChannelMediaInfo> ChannelMediaSources { get; set; } public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
@ -63,7 +62,7 @@ namespace MediaBrowser.Controller.Entities.Audio
[IgnoreDataMember] [IgnoreDataMember]
public override bool SupportsAddingToPlaylist public override bool SupportsAddingToPlaylist
{ {
get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; } get { return true; }
} }
[IgnoreDataMember] [IgnoreDataMember]
@ -84,21 +83,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() public override bool CanDownload()
{ {
var locationType = LocationType; var locationType = LocationType;
@ -262,7 +246,7 @@ namespace MediaBrowser.Controller.Entities.Audio
Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File, Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File,
MediaStreams = MediaSourceManager.GetMediaStreams(i.Id).ToList(), MediaStreams = MediaSourceManager.GetMediaStreams(i.Id).ToList(),
Name = i.Name, Name = i.Name,
Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path, Path = enablePathSubstituion ? GetMappedPath(i, i.Path, locationType) : i.Path,
RunTimeTicks = i.RunTimeTicks, RunTimeTicks = i.RunTimeTicks,
Container = i.Container, Container = i.Container,
Size = i.Size Size = i.Size

@ -16,7 +16,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// <summary> /// <summary>
/// Class MusicArtist /// Class MusicArtist
/// </summary> /// </summary>
public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasProductionLocations, IHasLookupInfo<ArtistInfo> public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasLookupInfo<ArtistInfo>
{ {
[IgnoreDataMember] [IgnoreDataMember]
public bool IsAccessedByName public bool IsAccessedByName
@ -24,8 +24,6 @@ namespace MediaBrowser.Controller.Entities.Audio
get { return ParentId == Guid.Empty; } get { return ParentId == Guid.Empty; }
} }
public List<string> ProductionLocations { get; set; }
[IgnoreDataMember] [IgnoreDataMember]
public override bool IsFolder public override bool IsFolder
{ {
@ -111,11 +109,6 @@ namespace MediaBrowser.Controller.Entities.Audio
return base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService); return base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService);
} }
public MusicArtist()
{
ProductionLocations = new List<string>();
}
public override List<string> GetUserDataKeys() public override List<string> GetUserDataKeys()
{ {
var list = base.GetUserDataKeys(); var list = base.GetUserDataKeys();

@ -37,6 +37,8 @@ namespace MediaBrowser.Controller.Entities
{ {
protected BaseItem() protected BaseItem()
{ {
ThemeSongIds = new List<Guid>();
ThemeVideoIds = new List<Guid>();
Keywords = new List<string>(); Keywords = new List<string>();
Tags = new List<string>(); Tags = new List<string>();
Genres = new List<string>(); Genres = new List<string>();
@ -44,6 +46,8 @@ namespace MediaBrowser.Controller.Entities
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
LockedFields = new List<MetadataFields>(); LockedFields = new List<MetadataFields>();
ImageInfos = new List<ItemImageInfo>(); ImageInfos = new List<ItemImageInfo>();
InheritedTags = new List<string>();
ProductionLocations = new List<string>();
} }
public static readonly char[] SlugReplaceChars = { '?', '/', '&' }; public static readonly char[] SlugReplaceChars = { '?', '/', '&' };
@ -64,6 +68,9 @@ namespace MediaBrowser.Controller.Entities
public static string ThemeSongFilename = "theme"; public static string ThemeSongFilename = "theme";
public static string ThemeVideosFolderName = "backdrops"; public static string ThemeVideosFolderName = "backdrops";
public List<Guid> ThemeSongIds { get; set; }
public List<Guid> ThemeVideoIds { get; set; }
[IgnoreDataMember] [IgnoreDataMember]
public string PreferredMetadataCountryCode { get; set; } public string PreferredMetadataCountryCode { get; set; }
[IgnoreDataMember] [IgnoreDataMember]
@ -72,6 +79,8 @@ namespace MediaBrowser.Controller.Entities
public long? Size { get; set; } public long? Size { get; set; }
public string Container { get; set; } public string Container { get; set; }
public string ShortOverview { get; set; } public string ShortOverview { get; set; }
[IgnoreDataMember]
public string Tagline { get; set; }
public List<ItemImageInfo> ImageInfos { get; set; } public List<ItemImageInfo> ImageInfos { get; set; }
@ -114,6 +123,22 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember] [IgnoreDataMember]
public bool IsInMixedFolder { get; set; } public bool IsInMixedFolder { get; set; }
[IgnoreDataMember]
protected virtual bool SupportsIsInMixedFolderDetection
{
get { return false; }
}
public bool DetectIsInMixedFolder()
{
if (SupportsIsInMixedFolderDetection)
{
}
return IsInMixedFolder;
}
[IgnoreDataMember] [IgnoreDataMember]
public virtual bool SupportsRemoteImageDownloading public virtual bool SupportsRemoteImageDownloading
{ {
@ -254,6 +279,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);
}
}
/// <summary> /// <summary>
/// Gets or sets the etag. /// Gets or sets the etag.
/// </summary> /// </summary>
@ -408,7 +446,7 @@ namespace MediaBrowser.Controller.Entities
public virtual bool IsInternetMetadataEnabled() public virtual bool IsInternetMetadataEnabled()
{ {
return ConfigurationManager.Configuration.EnableInternetProviders; return LibraryManager.GetLibraryOptions(this).EnableInternetProviders;
} }
public virtual bool CanDelete() public virtual bool CanDelete()
@ -784,6 +822,9 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember] [IgnoreDataMember]
public int InheritedParentalRatingValue { get; set; } public int InheritedParentalRatingValue { get; set; }
[IgnoreDataMember]
public List<string> InheritedTags { get; set; }
/// <summary> /// <summary>
/// Gets or sets the critic rating. /// Gets or sets the critic rating.
/// </summary> /// </summary>
@ -841,6 +882,7 @@ namespace MediaBrowser.Controller.Entities
public List<string> Tags { get; set; } public List<string> Tags { get; set; }
public List<string> Keywords { get; set; } public List<string> Keywords { get; set; }
public List<string> ProductionLocations { get; set; }
/// <summary> /// <summary>
/// Gets or sets the home page URL. /// Gets or sets the home page URL.
@ -956,7 +998,7 @@ namespace MediaBrowser.Controller.Entities
/// Loads the theme songs. /// Loads the theme songs.
/// </summary> /// </summary>
/// <returns>List{Audio.Audio}.</returns> /// <returns>List{Audio.Audio}.</returns>
private IEnumerable<Audio.Audio> LoadThemeSongs(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService) private static IEnumerable<Audio.Audio> LoadThemeSongs(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{ {
var files = fileSystemChildren.Where(i => i.IsDirectory) var files = fileSystemChildren.Where(i => i.IsDirectory)
.Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) .Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
@ -992,7 +1034,7 @@ namespace MediaBrowser.Controller.Entities
/// Loads the video backdrops. /// Loads the video backdrops.
/// </summary> /// </summary>
/// <returns>List{Video}.</returns> /// <returns>List{Video}.</returns>
private IEnumerable<Video> LoadThemeVideos(IEnumerable<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService) private static IEnumerable<Video> LoadThemeVideos(IEnumerable<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{ {
var files = fileSystemChildren.Where(i => i.IsDirectory) var files = fileSystemChildren.Where(i => i.IsDirectory)
.Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase)) .Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
@ -1078,6 +1120,12 @@ namespace MediaBrowser.Controller.Entities
get { return true; } get { return true; }
} }
[IgnoreDataMember]
public virtual bool SupportsThemeMedia
{
get { return false; }
}
/// <summary> /// <summary>
/// Refreshes owned items such as trailers, theme videos, special features, etc. /// Refreshes owned items such as trailers, theme videos, special features, etc.
/// Returns true or false indicating if changes were found. /// Returns true or false indicating if changes were found.
@ -1096,14 +1144,13 @@ namespace MediaBrowser.Controller.Entities
if (LocationType == LocationType.FileSystem && GetParent() != null) if (LocationType == LocationType.FileSystem && GetParent() != null)
{ {
var hasThemeMedia = this as IHasThemeMedia; if (SupportsThemeMedia)
if (hasThemeMedia != null)
{ {
if (!IsInMixedFolder) if (!DetectIsInMixedFolder())
{ {
themeSongsChanged = await RefreshThemeSongs(hasThemeMedia, options, fileSystemChildren, cancellationToken).ConfigureAwait(false); themeSongsChanged = await RefreshThemeSongs(this, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
themeVideosChanged = await RefreshThemeVideos(hasThemeMedia, options, fileSystemChildren, cancellationToken).ConfigureAwait(false); themeVideosChanged = await RefreshThemeVideos(this, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
} }
} }
@ -1141,7 +1188,7 @@ namespace MediaBrowser.Controller.Entities
return itemsChanged; return itemsChanged;
} }
private async Task<bool> RefreshThemeVideos(IHasThemeMedia item, MetadataRefreshOptions options, IEnumerable<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken) private static async Task<bool> RefreshThemeVideos(BaseItem item, MetadataRefreshOptions options, IEnumerable<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{ {
var newThemeVideos = LoadThemeVideos(fileSystemChildren, options.DirectoryService).ToList(); var newThemeVideos = LoadThemeVideos(fileSystemChildren, options.DirectoryService).ToList();
@ -1172,7 +1219,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary> /// <summary>
/// Refreshes the theme songs. /// Refreshes the theme songs.
/// </summary> /// </summary>
private async Task<bool> RefreshThemeSongs(IHasThemeMedia item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken) private static async Task<bool> RefreshThemeSongs(BaseItem item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{ {
var newThemeSongs = LoadThemeSongs(fileSystemChildren, options.DirectoryService).ToList(); var newThemeSongs = LoadThemeSongs(fileSystemChildren, options.DirectoryService).ToList();
var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList(); var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList();
@ -1249,7 +1296,15 @@ namespace MediaBrowser.Controller.Entities
{ {
var current = this; var current = this;
return current.IsInMixedFolder == newItem.IsInMixedFolder; if (!SupportsIsInMixedFolderDetection)
{
if (current.IsInMixedFolder != newItem.IsInMixedFolder)
{
return false;
}
}
return true;
} }
public void AfterMetadataRefresh() public void AfterMetadataRefresh()
@ -1324,7 +1379,9 @@ namespace MediaBrowser.Controller.Entities
return false; return false;
} }
return ConfigurationManager.Configuration.SaveLocalMeta; var libraryOptions = LibraryManager.GetLibraryOptions(this);
return libraryOptions.SaveLocalMetadata;
} }
/// <summary> /// <summary>
@ -2107,14 +2164,11 @@ namespace MediaBrowser.Controller.Entities
return hasChanges; return hasChanges;
} }
protected static string GetMappedPath(string path, LocationType locationType) protected static string GetMappedPath(BaseItem item, string path, LocationType locationType)
{ {
if (locationType == LocationType.FileSystem || locationType == LocationType.Offline) if (locationType == LocationType.FileSystem || locationType == LocationType.Offline)
{ {
foreach (var map in ConfigurationManager.Configuration.PathSubstitutions) return LibraryManager.GetPathAfterNetworkSubstitution(path, item);
{
path = LibraryManager.SubstitutePath(path, map.From, map.To);
}
} }
return path; return path;

@ -48,24 +48,14 @@ namespace MediaBrowser.Controller.Entities
private static readonly Dictionary<string, LibraryOptions> LibraryOptions = new Dictionary<string, LibraryOptions>(); private static readonly Dictionary<string, LibraryOptions> LibraryOptions = new Dictionary<string, LibraryOptions>();
public LibraryOptions GetLibraryOptions() public LibraryOptions GetLibraryOptions()
{ {
lock (LibraryOptions) return GetLibraryOptions(Path);
{
LibraryOptions options;
if (!LibraryOptions.TryGetValue(Path, out options))
{
options = LoadLibraryOptions();
LibraryOptions[Path] = options;
}
return options;
}
} }
private LibraryOptions LoadLibraryOptions() private static LibraryOptions LoadLibraryOptions(string path)
{ {
try try
{ {
var result = XmlSerializer.DeserializeFromFile(typeof(LibraryOptions), GetLibraryOptionsPath(Path)) as LibraryOptions; var result = XmlSerializer.DeserializeFromFile(typeof(LibraryOptions), GetLibraryOptionsPath(path)) as LibraryOptions;
if (result == null) if (result == null)
{ {
@ -100,13 +90,28 @@ namespace MediaBrowser.Controller.Entities
SaveLibraryOptions(Path, options); SaveLibraryOptions(Path, options);
} }
public static LibraryOptions GetLibraryOptions(string path)
{
lock (LibraryOptions)
{
LibraryOptions options;
if (!LibraryOptions.TryGetValue(path, out options))
{
options = LoadLibraryOptions(path);
LibraryOptions[path] = options;
}
return options;
}
}
public static void SaveLibraryOptions(string path, LibraryOptions options) public static void SaveLibraryOptions(string path, LibraryOptions options)
{ {
lock (LibraryOptions) lock (LibraryOptions)
{ {
LibraryOptions[path] = options; LibraryOptions[path] = options;
options.SchemaVersion = 1; options.SchemaVersion = 3;
XmlSerializer.SerializeToFile(options, GetLibraryOptionsPath(path)); XmlSerializer.SerializeToFile(options, GetLibraryOptionsPath(path));
} }
} }

@ -22,13 +22,18 @@ namespace MediaBrowser.Controller.Entities
/// <summary> /// <summary>
/// Class Folder /// Class Folder
/// </summary> /// </summary>
public class Folder : BaseItem, IHasThemeMedia public class Folder : BaseItem
{ {
public static IUserManager UserManager { get; set; } public static IUserManager UserManager { get; set; }
public static IUserViewManager UserViewManager { get; set; } public static IUserViewManager UserViewManager { get; set; }
public List<Guid> ThemeSongIds { get; set; } /// <summary>
public List<Guid> ThemeVideoIds { get; set; } /// Gets or sets a value indicating whether this instance is root.
/// </summary>
/// <value><c>true</c> if this instance is root; otherwise, <c>false</c>.</value>
public bool IsRoot { get; set; }
public virtual List<LinkedChild> LinkedChildren { get; set; }
[IgnoreDataMember] [IgnoreDataMember]
public DateTime? DateLastMediaAdded { get; set; } public DateTime? DateLastMediaAdded { get; set; }
@ -36,9 +41,12 @@ namespace MediaBrowser.Controller.Entities
public Folder() public Folder()
{ {
LinkedChildren = new List<LinkedChild>(); LinkedChildren = new List<LinkedChild>();
}
ThemeSongIds = new List<Guid>(); [IgnoreDataMember]
ThemeVideoIds = new List<Guid>(); public override bool SupportsThemeMedia
{
get { return true; }
} }
[IgnoreDataMember] [IgnoreDataMember]
@ -47,6 +55,12 @@ namespace MediaBrowser.Controller.Entities
get { return false; } get { return false; }
} }
[IgnoreDataMember]
public virtual bool IsPhysicalRoot
{
get { return false; }
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is folder. /// Gets a value indicating whether this instance is folder.
/// </summary> /// </summary>
@ -117,19 +131,6 @@ namespace MediaBrowser.Controller.Entities
return true; return true;
} }
/// <summary>
/// Gets or sets a value indicating whether this instance is physical root.
/// </summary>
/// <value><c>true</c> if this instance is physical root; otherwise, <c>false</c>.</value>
public bool IsPhysicalRoot { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is root.
/// </summary>
/// <value><c>true</c> if this instance is root; otherwise, <c>false</c>.</value>
public bool IsRoot { get; set; }
public virtual List<LinkedChild> LinkedChildren { get; set; }
[IgnoreDataMember] [IgnoreDataMember]
protected virtual bool SupportsShortcutChildren protected virtual bool SupportsShortcutChildren
{ {
@ -178,8 +179,6 @@ namespace MediaBrowser.Controller.Entities
item.SetParent(null); item.SetParent(null);
} }
#region Indexing
/// <summary> /// <summary>
/// Returns the valid set of index by options for this folder type. /// Returns the valid set of index by options for this folder type.
/// Override or extend to modify. /// Override or extend to modify.
@ -207,8 +206,6 @@ namespace MediaBrowser.Controller.Entities
get { return GetIndexByOptions(); } get { return GetIndexByOptions(); }
} }
#endregion
/// <summary> /// <summary>
/// Gets the actual children. /// Gets the actual children.
/// </summary> /// </summary>
@ -1057,10 +1054,20 @@ namespace MediaBrowser.Controller.Entities
/// <returns>IList{BaseItem}.</returns> /// <returns>IList{BaseItem}.</returns>
public IList<BaseItem> GetRecursiveChildren() public IList<BaseItem> GetRecursiveChildren()
{ {
return GetRecursiveChildren(i => true); return GetRecursiveChildren(true);
}
public IList<BaseItem> GetRecursiveChildren(bool includeLinkedChildren)
{
return GetRecursiveChildren(i => true, includeLinkedChildren);
} }
public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter) public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter)
{
return GetRecursiveChildren(filter, true);
}
public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter, bool includeLinkedChildren)
{ {
var result = new Dictionary<Guid, BaseItem>(); var result = new Dictionary<Guid, BaseItem>();
@ -1139,29 +1146,19 @@ namespace MediaBrowser.Controller.Entities
return LinkedChildren return LinkedChildren
.Select(i => .Select(i =>
{ {
var requiresPostFilter = true;
if (!string.IsNullOrWhiteSpace(i.Path))
{
requiresPostFilter = false;
if (!locations.Any(l => FileSystem.ContainsSubPath(l, i.Path)))
{
return null;
}
}
var child = GetLinkedChild(i); var child = GetLinkedChild(i);
if (requiresPostFilter && child != null) if (child != null)
{ {
if (string.IsNullOrWhiteSpace(child.Path)) var childLocationType = child.LocationType;
if (childLocationType == LocationType.Remote || childLocationType == LocationType.Virtual)
{ {
Logger.Debug("Found LinkedChild with null path: {0}", child.Name); if (!child.IsVisibleStandalone(user))
return child; {
return null;
}
} }
else if (childLocationType == LocationType.FileSystem && !locations.Any(l => FileSystem.ContainsSubPath(l, child.Path)))
if (!locations.Any(l => FileSystem.ContainsSubPath(l, child.Path)))
{ {
return null; return null;
} }

@ -8,11 +8,8 @@ using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
{ {
public class Game : BaseItem, IHasTrailers, IHasThemeMedia, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo<GameInfo> public class Game : BaseItem, IHasTrailers, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo<GameInfo>
{ {
public List<Guid> ThemeSongIds { get; set; }
public List<Guid> ThemeVideoIds { get; set; }
public Game() public Game()
{ {
MultiPartGameFiles = new List<string>(); MultiPartGameFiles = new List<string>();
@ -39,6 +36,12 @@ namespace MediaBrowser.Controller.Entities
get { return true; } get { return true; }
} }
[IgnoreDataMember]
public override bool SupportsThemeMedia
{
get { return true; }
}
/// <summary> /// <summary>
/// Gets or sets the remote trailers. /// Gets or sets the remote trailers.
/// </summary> /// </summary>
@ -98,7 +101,7 @@ namespace MediaBrowser.Controller.Entities
public override IEnumerable<string> GetDeletePaths() public override IEnumerable<string> GetDeletePaths()
{ {
if (!IsInMixedFolder) if (!DetectIsInMixedFolder())
{ {
return new[] { System.IO.Path.GetDirectoryName(Path) }; return new[] { System.IO.Path.GetDirectoryName(Path) };
} }

@ -1,8 +0,0 @@

namespace MediaBrowser.Controller.Entities
{
public interface IArchivable
{
bool IsArchive { get; }
}
}

@ -150,11 +150,7 @@ namespace MediaBrowser.Controller.Entities
/// <value><c>true</c> if [supports local metadata]; otherwise, <c>false</c>.</value> /// <value><c>true</c> if [supports local metadata]; otherwise, <c>false</c>.</value>
bool SupportsLocalMetadata { get; } bool SupportsLocalMetadata { get; }
/// <summary> bool DetectIsInMixedFolder();
/// Gets a value indicating whether this instance is in mixed folder.
/// </summary>
/// <value><c>true</c> if this instance is in mixed folder; otherwise, <c>false</c>.</value>
bool IsInMixedFolder { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is locked. /// Gets a value indicating whether this instance is locked.

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
{ {
@ -62,5 +63,7 @@ namespace MediaBrowser.Controller.Entities
int? GetInheritedParentalRatingValue(); int? GetInheritedParentalRatingValue();
int InheritedParentalRatingValue { get; set; } int InheritedParentalRatingValue { get; set; }
List<string> GetInheritedTags();
List<string> InheritedTags { get; set; }
} }
} }

@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace MediaBrowser.Controller.Entities
{
/// <summary>
/// Interface IHasProductionLocations
/// </summary>
public interface IHasProductionLocations
{
/// <summary>
/// Gets or sets the production locations.
/// </summary>
/// <value>The production locations.</value>
List<string> ProductionLocations { get; set; }
}
public static class ProductionLocationExtensions
{
public static void AddProductionLocation(this IHasProductionLocations item, string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException("name");
}
if (!item.ProductionLocations.Contains(name, StringComparer.OrdinalIgnoreCase))
{
item.ProductionLocations.Add(name);
}
}
}
}

@ -1,39 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace MediaBrowser.Controller.Entities
{
/// <summary>
/// Interface IHasTaglines
/// </summary>
public interface IHasTaglines
{
/// <summary>
/// Gets or sets the taglines.
/// </summary>
/// <value>The taglines.</value>
List<string> Taglines { get; set; }
}
public static class TaglineExtensions
{
/// <summary>
/// Adds the tagline.
/// </summary>
/// <param name="tagline">The tagline.</param>
/// <exception cref="System.ArgumentNullException">tagline</exception>
public static void AddTagline(this IHasTaglines item, string tagline)
{
if (string.IsNullOrWhiteSpace(tagline))
{
throw new ArgumentNullException("tagline");
}
if (!item.Taglines.Contains(tagline, StringComparer.OrdinalIgnoreCase))
{
item.Taglines.Add(tagline);
}
}
}
}

@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
/// <summary>
/// Interface IHasThemeMedia
/// </summary>
public interface IHasThemeMedia
{
/// <summary>
/// Gets or sets the theme song ids.
/// </summary>
/// <value>The theme song ids.</value>
List<Guid> ThemeSongIds { get; set; }
/// <summary>
/// Gets or sets the theme video ids.
/// </summary>
/// <value>The theme video ids.</value>
List<Guid> ThemeVideoIds { get; set; }
}
}

@ -2,6 +2,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using System.Linq;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Model.Querying;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
{ {
@ -101,6 +104,8 @@ namespace MediaBrowser.Controller.Entities
public bool? IsMovie { get; set; } public bool? IsMovie { get; set; }
public bool? IsSports { get; set; } public bool? IsSports { get; set; }
public bool? IsKids { get; set; } public bool? IsKids { get; set; }
public bool? IsNews { get; set; }
public bool? IsSeries { get; set; }
public int? MinPlayers { get; set; } public int? MinPlayers { get; set; }
public int? MaxPlayers { get; set; } public int? MaxPlayers { get; set; }
@ -137,6 +142,7 @@ namespace MediaBrowser.Controller.Entities
public DayOfWeek[] AirDays { get; set; } public DayOfWeek[] AirDays { get; set; }
public SeriesStatus[] SeriesStatuses { get; set; } public SeriesStatus[] SeriesStatuses { get; set; }
public string AlbumArtistStartsWithOrGreater { get; set; } public string AlbumArtistStartsWithOrGreater { get; set; }
public string ExternalSeriesId { get; set; }
public string[] AlbumNames { get; set; } public string[] AlbumNames { get; set; }
public string[] ArtistNames { get; set; } public string[] ArtistNames { get; set; }
@ -149,11 +155,51 @@ namespace MediaBrowser.Controller.Entities
public Dictionary<string, string> ExcludeProviderIds { get; set; } public Dictionary<string, string> ExcludeProviderIds { get; set; }
public bool EnableGroupByMetadataKey { get; set; } public bool EnableGroupByMetadataKey { get; set; }
public List<Tuple<string, SortOrder>> OrderBy { get; set; }
public DateTime? MinDateCreated { get; set; }
public DateTime? MinDateLastSaved { get; set; }
public DtoOptions DtoOptions { get; set; }
public bool HasField(ItemFields name)
{
var fields = DtoOptions.Fields;
switch (name)
{
case ItemFields.ProductionLocations:
case ItemFields.Keywords:
case ItemFields.Taglines:
case ItemFields.ShortOverview:
case ItemFields.CustomRating:
case ItemFields.DateCreated:
case ItemFields.SortName:
case ItemFields.Overview:
case ItemFields.OfficialRatingDescription:
case ItemFields.HomePageUrl:
case ItemFields.VoteCount:
case ItemFields.DisplayMediaType:
case ItemFields.ServiceName:
case ItemFields.Genres:
case ItemFields.Studios:
case ItemFields.Settings:
case ItemFields.OriginalTitle:
case ItemFields.Tags:
case ItemFields.DateLastMediaAdded:
case ItemFields.CriticRatingSummary:
return fields.Count == 0 || fields.Contains(name);
default:
return true;
}
}
public InternalItemsQuery() public InternalItemsQuery()
{ {
GroupByPresentationUniqueKey = true; GroupByPresentationUniqueKey = true;
EnableTotalRecordCount = true; EnableTotalRecordCount = true;
DtoOptions = new DtoOptions();
AlbumNames = new string[] { }; AlbumNames = new string[] { };
ArtistNames = new string[] { }; ArtistNames = new string[] { };
ExcludeArtistIds = new string[] { }; ExcludeArtistIds = new string[] { };
@ -191,6 +237,7 @@ namespace MediaBrowser.Controller.Entities
TrailerTypes = new TrailerType[] { }; TrailerTypes = new TrailerType[] { };
AirDays = new DayOfWeek[] { }; AirDays = new DayOfWeek[] { };
SeriesStatuses = new SeriesStatus[] { }; SeriesStatuses = new SeriesStatus[] { };
OrderBy = new List<Tuple<string, SortOrder>>();
} }
public InternalItemsQuery(User user) public InternalItemsQuery(User user)

@ -15,24 +15,17 @@ namespace MediaBrowser.Controller.Entities.Movies
/// <summary> /// <summary>
/// Class Movie /// Class Movie
/// </summary> /// </summary>
public class Movie : Video, IHasCriticRating, IHasSpecialFeatures, IHasProductionLocations, IHasBudget, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping, IHasOriginalTitle public class Movie : Video, IHasCriticRating, IHasSpecialFeatures, IHasBudget, IHasTrailers, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping, IHasOriginalTitle
{ {
public List<Guid> SpecialFeatureIds { get; set; } public List<Guid> SpecialFeatureIds { get; set; }
public List<Guid> ThemeSongIds { get; set; }
public List<Guid> ThemeVideoIds { get; set; }
public List<string> ProductionLocations { get; set; }
public Movie() public Movie()
{ {
SpecialFeatureIds = new List<Guid>(); SpecialFeatureIds = new List<Guid>();
RemoteTrailers = new List<MediaUrl>(); RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>(); LocalTrailerIds = new List<Guid>();
RemoteTrailerIds = new List<Guid>(); RemoteTrailerIds = new List<Guid>();
ThemeSongIds = new List<Guid>();
ThemeVideoIds = new List<Guid>();
Taglines = new List<string>(); Taglines = new List<string>();
ProductionLocations = new List<string>();
} }
public string AwardSummary { get; set; } public string AwardSummary { get; set; }
@ -81,7 +74,7 @@ namespace MediaBrowser.Controller.Entities.Movies
// Must have a parent to have special features // Must have a parent to have special features
// In other words, it must be part of the Parent/Child tree // In other words, it must be part of the Parent/Child tree
if (LocationType == LocationType.FileSystem && GetParent() != null && !IsInMixedFolder) if (LocationType == LocationType.FileSystem && GetParent() != null && !DetectIsInMixedFolder())
{ {
var specialFeaturesChanged = await RefreshSpecialFeatures(options, fileSystemChildren, cancellationToken).ConfigureAwait(false); var specialFeaturesChanged = await RefreshSpecialFeatures(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
@ -119,7 +112,7 @@ namespace MediaBrowser.Controller.Entities.Movies
{ {
var info = GetItemLookupInfo<MovieInfo>(); var info = GetItemLookupInfo<MovieInfo>();
if (!IsInMixedFolder) if (!DetectIsInMixedFolder())
{ {
info.Name = System.IO.Path.GetFileName(ContainingFolderPath); info.Name = System.IO.Path.GetFileName(ContainingFolderPath);
} }
@ -145,7 +138,7 @@ namespace MediaBrowser.Controller.Entities.Movies
else else
{ {
// Try to get the year from the folder name // Try to get the year from the folder name
if (!IsInMixedFolder) if (!DetectIsInMixedFolder())
{ {
info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath)); info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));

@ -6,7 +6,7 @@ using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
{ {
public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasProductionLocations, IHasBudget, IHasLookupInfo<MusicVideoInfo> public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasBudget, IHasLookupInfo<MusicVideoInfo>
{ {
/// <summary> /// <summary>
/// Gets or sets the budget. /// Gets or sets the budget.
@ -19,12 +19,10 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
/// <value>The revenue.</value> /// <value>The revenue.</value>
public double? Revenue { get; set; } public double? Revenue { get; set; }
public List<string> ProductionLocations { get; set; }
public List<string> Artists { get; set; } public List<string> Artists { get; set; }
public MusicVideo() public MusicVideo()
{ {
ProductionLocations = new List<string>();
Artists = new List<string>(); Artists = new List<string>();
} }
@ -37,6 +35,15 @@ namespace MediaBrowser.Controller.Entities
} }
} }
[IgnoreDataMember]
protected override bool SupportsIsInMixedFolderDetection
{
get
{
return true;
}
}
public override UnratedItem GetBlockUnratedType() public override UnratedItem GetBlockUnratedType()
{ {
return UnratedItem.Music; return UnratedItem.Music;
@ -65,7 +72,7 @@ namespace MediaBrowser.Controller.Entities
else else
{ {
// Try to get the year from the folder name // Try to get the year from the folder name
if (!IsInMixedFolder) if (!DetectIsInMixedFolder())
{ {
info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath)); info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));

@ -1,19 +1,11 @@
using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Drawing;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
{ {
public class Photo : BaseItem, IHasTaglines public class Photo : BaseItem
{ {
public List<string> Taglines { get; set; }
public Photo()
{
Taglines = new List<string>();
}
[IgnoreDataMember] [IgnoreDataMember]
public override bool SupportsLocalMetadata public override bool SupportsLocalMetadata
{ {

@ -85,9 +85,7 @@ namespace MediaBrowser.Controller.Entities.TV
public override int GetChildCount(User user) public override int GetChildCount(User user)
{ {
Logger.Debug("Season {0} getting child cound", (Path ?? Name));
var result = GetChildren(user, true).Count(); var result = GetChildren(user, true).Count();
Logger.Debug("Season {0} child cound: ", result);
return result; return result;
} }
@ -156,15 +154,10 @@ namespace MediaBrowser.Controller.Entities.TV
Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
var id = Guid.NewGuid().ToString("N");
Logger.Debug("Season.GetItemsInternal entering GetEpisodes. Request id: " + id);
var items = GetEpisodes(user).Where(filter); var items = GetEpisodes(user).Where(filter);
Logger.Debug("Season.GetItemsInternal entering PostFilterAndSort. Request id: " + id);
var result = PostFilterAndSort(items, query, false, false); var result = PostFilterAndSort(items, query, false, false);
Logger.Debug("Season.GetItemsInternal complete. Request id: " + id);
return Task.FromResult(result); return Task.FromResult(result);
} }
@ -185,34 +178,12 @@ namespace MediaBrowser.Controller.Entities.TV
public IEnumerable<Episode> GetEpisodes(Series series, User user, IEnumerable<Episode> allSeriesEpisodes) public IEnumerable<Episode> GetEpisodes(Series series, User user, IEnumerable<Episode> allSeriesEpisodes)
{ {
return series.GetSeasonEpisodes(user, this, allSeriesEpisodes); return series.GetSeasonEpisodes(this, user, allSeriesEpisodes);
} }
public IEnumerable<Episode> GetEpisodes() public IEnumerable<Episode> GetEpisodes()
{ {
var episodes = GetRecursiveChildren().OfType<Episode>(); return Series.GetSeasonEpisodes(this, null, null);
var series = Series;
if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
{
var seasonNumber = IndexNumber;
var list = episodes.ToList();
if (seasonNumber.HasValue)
{
list.AddRange(series.GetRecursiveChildren().OfType<Episode>()
.Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value));
}
else
{
list.AddRange(series.GetRecursiveChildren().OfType<Episode>()
.Where(i => !i.ParentIndexNumber.HasValue));
}
episodes = list.DistinctBy(i => i.Id);
}
return episodes;
} }
public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren) public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)

@ -17,17 +17,14 @@ namespace MediaBrowser.Controller.Entities.TV
/// <summary> /// <summary>
/// Class Series /// Class Series
/// </summary> /// </summary>
public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IHasSpecialFeatures, IMetadataContainer, IHasOriginalTitle public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IMetadataContainer, IHasOriginalTitle
{ {
public List<Guid> SpecialFeatureIds { get; set; }
public int? AnimeSeriesIndex { get; set; } public int? AnimeSeriesIndex { get; set; }
public Series() public Series()
{ {
AirDays = new List<DayOfWeek>(); AirDays = new List<DayOfWeek>();
SpecialFeatureIds = new List<Guid>();
RemoteTrailers = new List<MediaUrl>(); RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>(); LocalTrailerIds = new List<Guid>();
RemoteTrailerIds = new List<Guid>(); RemoteTrailerIds = new List<Guid>();
@ -209,7 +206,6 @@ namespace MediaBrowser.Controller.Entities.TV
var seriesKey = GetUniqueSeriesKey(this); var seriesKey = GetUniqueSeriesKey(this);
Logger.Debug("GetSeasons SeriesKey: {0}", seriesKey);
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
AncestorWithPresentationUniqueKey = seriesKey, AncestorWithPresentationUniqueKey = seriesKey,
@ -267,7 +263,6 @@ namespace MediaBrowser.Controller.Entities.TV
public IEnumerable<Episode> GetEpisodes(User user) public IEnumerable<Episode> GetEpisodes(User user)
{ {
var seriesKey = GetUniqueSeriesKey(this); var seriesKey = GetUniqueSeriesKey(this);
Logger.Debug("GetEpisodes seriesKey: {0}", seriesKey);
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
@ -291,8 +286,6 @@ namespace MediaBrowser.Controller.Entities.TV
var allItems = LibraryManager.GetItemList(query).ToList(); var allItems = LibraryManager.GetItemList(query).ToList();
Logger.Debug("GetEpisodes return {0} items from database", allItems.Count);
var allSeriesEpisodes = allItems.OfType<Episode>().ToList(); var allSeriesEpisodes = allItems.OfType<Episode>().ToList();
var allEpisodes = allItems.OfType<Season>() var allEpisodes = allItems.OfType<Season>()
@ -373,27 +366,9 @@ namespace MediaBrowser.Controller.Entities.TV
progress.Report(100); progress.Report(100);
} }
private IEnumerable<Episode> GetAllEpisodes(User user) public IEnumerable<Episode> GetSeasonEpisodes(Season parentSeason, User user)
{
Logger.Debug("Series.GetAllEpisodes entering GetItemList");
var result = LibraryManager.GetItemList(new InternalItemsQuery(user)
{
AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this),
IncludeItemTypes = new[] { typeof(Episode).Name },
SortBy = new[] { ItemSortBy.SortName }
}).Cast<Episode>().ToList();
Logger.Debug("Series.GetAllEpisodes returning {0} episodes", result.Count);
return result;
}
public IEnumerable<Episode> GetSeasonEpisodes(User user, Season parentSeason)
{ {
var seriesKey = GetUniqueSeriesKey(this); var seriesKey = GetUniqueSeriesKey(this);
Logger.Debug("GetSeasonEpisodes seriesKey: {0}", seriesKey);
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
@ -401,34 +376,35 @@ namespace MediaBrowser.Controller.Entities.TV
IncludeItemTypes = new[] { typeof(Episode).Name }, IncludeItemTypes = new[] { typeof(Episode).Name },
SortBy = new[] { ItemSortBy.SortName } SortBy = new[] { ItemSortBy.SortName }
}; };
var config = user.Configuration; if (user != null)
if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes)
{ {
query.IsVirtualItem = false; var config = user.Configuration;
} if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes)
else if (!config.DisplayMissingEpisodes) {
{ query.IsVirtualItem = false;
query.IsMissing = false; }
} else if (!config.DisplayMissingEpisodes)
else if (!config.DisplayUnairedEpisodes) {
{ query.IsMissing = false;
query.IsVirtualUnaired = false; }
else if (!config.DisplayUnairedEpisodes)
{
query.IsVirtualUnaired = false;
}
} }
var allItems = LibraryManager.GetItemList(query).OfType<Episode>(); var allItems = LibraryManager.GetItemList(query).OfType<Episode>();
return GetSeasonEpisodes(user, parentSeason, allItems); return GetSeasonEpisodes(parentSeason, user, allItems);
} }
public IEnumerable<Episode> GetSeasonEpisodes(User user, Season parentSeason, IEnumerable<Episode> allSeriesEpisodes) public IEnumerable<Episode> GetSeasonEpisodes(Season parentSeason, User user, IEnumerable<Episode> allSeriesEpisodes)
{ {
if (allSeriesEpisodes == null) if (allSeriesEpisodes == null)
{ {
Logger.Debug("GetSeasonEpisodes allSeriesEpisodes is null"); return GetSeasonEpisodes(parentSeason, user);
return GetSeasonEpisodes(user, parentSeason);
} }
Logger.Debug("GetSeasonEpisodes FilterEpisodesBySeason");
var episodes = FilterEpisodesBySeason(allSeriesEpisodes, parentSeason, ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons); var episodes = FilterEpisodesBySeason(allSeriesEpisodes, parentSeason, ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons);
var sortBy = (parentSeason.IndexNumber ?? -1) == 0 ? ItemSortBy.SortName : ItemSortBy.AiredEpisodeOrder; var sortBy = (parentSeason.IndexNumber ?? -1) == 0 ? ItemSortBy.SortName : ItemSortBy.AiredEpisodeOrder;

@ -10,16 +10,12 @@ namespace MediaBrowser.Controller.Entities
/// <summary> /// <summary>
/// Class Trailer /// Class Trailer
/// </summary> /// </summary>
public class Trailer : Video, IHasCriticRating, IHasProductionLocations, IHasBudget, IHasTaglines, IHasMetascore, IHasOriginalTitle, IHasLookupInfo<TrailerInfo> public class Trailer : Video, IHasCriticRating, IHasBudget, IHasMetascore, IHasOriginalTitle, IHasLookupInfo<TrailerInfo>
{ {
public List<string> ProductionLocations { get; set; }
public Trailer() public Trailer()
{ {
RemoteTrailers = new List<MediaUrl>(); RemoteTrailers = new List<MediaUrl>();
Taglines = new List<string>();
Keywords = new List<string>(); Keywords = new List<string>();
ProductionLocations = new List<string>();
TrailerTypes = new List<TrailerType> { TrailerType.LocalTrailer }; TrailerTypes = new List<TrailerType> { TrailerType.LocalTrailer };
} }
@ -35,12 +31,6 @@ namespace MediaBrowser.Controller.Entities
get { return TrailerTypes.Contains(TrailerType.LocalTrailer); } get { return TrailerTypes.Contains(TrailerType.LocalTrailer); }
} }
/// <summary>
/// Gets or sets the taglines.
/// </summary>
/// <value>The taglines.</value>
public List<string> Taglines { get; set; }
/// <summary> /// <summary>
/// Gets or sets the budget. /// Gets or sets the budget.
/// </summary> /// </summary>
@ -64,7 +54,7 @@ namespace MediaBrowser.Controller.Entities
info.IsLocalTrailer = TrailerTypes.Contains(TrailerType.LocalTrailer); info.IsLocalTrailer = TrailerTypes.Contains(TrailerType.LocalTrailer);
if (!IsInMixedFolder) if (!DetectIsInMixedFolder() && LocationType == LocationType.FileSystem)
{ {
info.Name = System.IO.Path.GetFileName(ContainingFolderPath); info.Name = System.IO.Path.GetFileName(ContainingFolderPath);
} }
@ -90,7 +80,7 @@ namespace MediaBrowser.Controller.Entities
else else
{ {
// Try to get the year from the folder name // Try to get the year from the folder name
if (!IsInMixedFolder) if (!DetectIsInMixedFolder())
{ {
info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath)); info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));

@ -13,7 +13,6 @@ namespace MediaBrowser.Controller.Entities
public class UserView : Folder public class UserView : Folder
{ {
public string ViewType { get; set; } public string ViewType { get; set; }
public Guid ParentId { get; set; }
public Guid DisplayParentId { get; set; } public Guid DisplayParentId { get; set; }
public Guid? UserId { get; set; } public Guid? UserId { get; set; }

@ -96,7 +96,7 @@ namespace MediaBrowser.Controller.Entities
Limit = query.Limit, Limit = query.Limit,
IsAiring = true IsAiring = true
}, CancellationToken.None).ConfigureAwait(false); }, new Dto.DtoOptions(), CancellationToken.None).ConfigureAwait(false);
return GetResult(result); return GetResult(result);
} }
@ -1497,13 +1497,7 @@ namespace MediaBrowser.Controller.Entities
{ {
var filterValue = query.HasThemeSong.Value; var filterValue = query.HasThemeSong.Value;
var themeCount = 0; var themeCount = item.ThemeSongIds.Count;
var iHasThemeMedia = item as IHasThemeMedia;
if (iHasThemeMedia != null)
{
themeCount = iHasThemeMedia.ThemeSongIds.Count;
}
var ok = filterValue ? themeCount > 0 : themeCount == 0; var ok = filterValue ? themeCount > 0 : themeCount == 0;
if (!ok) if (!ok)
@ -1516,13 +1510,7 @@ namespace MediaBrowser.Controller.Entities
{ {
var filterValue = query.HasThemeVideo.Value; var filterValue = query.HasThemeVideo.Value;
var themeCount = 0; var themeCount = item.ThemeVideoIds.Count;
var iHasThemeMedia = item as IHasThemeMedia;
if (iHasThemeMedia != null)
{
themeCount = iHasThemeMedia.ThemeVideoIds.Count;
}
var ok = filterValue ? themeCount > 0 : themeCount == 0; var ok = filterValue ? themeCount > 0 : themeCount == 0;
if (!ok) if (!ok)

@ -25,8 +25,7 @@ namespace MediaBrowser.Controller.Entities
ISupportsPlaceHolders, ISupportsPlaceHolders,
IHasMediaSources, IHasMediaSources,
IHasShortOverview, IHasShortOverview,
IThemeMedia, IThemeMedia
IArchivable
{ {
[IgnoreDataMember] [IgnoreDataMember]
public string PrimaryVersionId { get; set; } public string PrimaryVersionId { get; set; }
@ -64,6 +63,12 @@ namespace MediaBrowser.Controller.Entities
} }
} }
[IgnoreDataMember]
public override bool SupportsThemeMedia
{
get { return true; }
}
public int? TotalBitrate { get; set; } public int? TotalBitrate { get; set; }
public ExtraType? ExtraType { get; set; } public ExtraType? ExtraType { get; set; }
@ -165,7 +170,7 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember] [IgnoreDataMember]
public override bool SupportsAddingToPlaylist public override bool SupportsAddingToPlaylist
{ {
get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; } get { return true; }
} }
[IgnoreDataMember] [IgnoreDataMember]
@ -197,21 +202,6 @@ namespace MediaBrowser.Controller.Entities
get { return LocalAlternateVersions.Count > 0; } get { return LocalAlternateVersions.Count > 0; }
} }
[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 IEnumerable<Guid> GetAdditionalPartIds() public IEnumerable<Guid> GetAdditionalPartIds()
{ {
return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video))); return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
@ -480,7 +470,7 @@ namespace MediaBrowser.Controller.Entities
public override IEnumerable<string> GetDeletePaths() public override IEnumerable<string> GetDeletePaths()
{ {
if (!IsInMixedFolder) if (!DetectIsInMixedFolder())
{ {
return new[] { ContainingFolderPath }; return new[] { ContainingFolderPath };
} }
@ -600,7 +590,7 @@ namespace MediaBrowser.Controller.Entities
Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File, Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File,
MediaStreams = mediaStreams, MediaStreams = mediaStreams,
Name = GetMediaSourceName(i, mediaStreams), Name = GetMediaSourceName(i, mediaStreams),
Path = enablePathSubstitution ? GetMappedPath(i.Path, locationType) : i.Path, Path = enablePathSubstitution ? GetMappedPath(i, i.Path, locationType) : i.Path,
RunTimeTicks = i.RunTimeTicks, RunTimeTicks = i.RunTimeTicks,
Video3DFormat = i.Video3DFormat, Video3DFormat = i.Video3DFormat,
VideoType = i.VideoType, VideoType = i.VideoType,

@ -89,5 +89,7 @@ namespace MediaBrowser.Controller
string GetLocalApiUrl(IPAddress ipAddress); string GetLocalApiUrl(IPAddress ipAddress);
void LaunchUrl(string url); void LaunchUrl(string url);
void EnableLoopback(string appName);
} }
} }

@ -506,6 +506,8 @@ namespace MediaBrowser.Controller.Library
/// <returns>QueryResult&lt;BaseItem&gt;.</returns> /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
QueryResult<BaseItem> QueryItems(InternalItemsQuery query); QueryResult<BaseItem> QueryItems(InternalItemsQuery query);
string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem = null);
/// <summary> /// <summary>
/// Substitutes the path. /// Substitutes the path.
/// </summary> /// </summary>
@ -554,9 +556,10 @@ namespace MediaBrowser.Controller.Library
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
bool IgnoreFile(FileSystemMetadata file, BaseItem parent); bool IgnoreFile(FileSystemMetadata file, BaseItem parent);
void AddVirtualFolder(string name, string collectionType, string[] mediaPaths, LibraryOptions options, bool refreshLibrary); void AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary);
void RemoveVirtualFolder(string name, bool refreshLibrary); void RemoveVirtualFolder(string name, bool refreshLibrary);
void AddMediaPath(string virtualFolderName, string path); void AddMediaPath(string virtualFolderName, MediaPathInfo path);
void UpdateMediaPath(string virtualFolderName, MediaPathInfo path);
void RemoveMediaPath(string virtualFolderName, string path); void RemoveMediaPath(string virtualFolderName, string path);
QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query); QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query);
@ -566,5 +569,8 @@ namespace MediaBrowser.Controller.Library
QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query); QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query);
QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query); QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query);
QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query); QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query);
void RegisterIgnoredPath(string path);
void UnRegisterIgnoredPath(string path);
} }
} }

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.IO;
namespace MediaBrowser.Controller.Library namespace MediaBrowser.Controller.Library
{ {
@ -60,11 +61,8 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Gets the static media source. /// Gets the static media source.
/// </summary> /// </summary>
/// <param name="item">The item.</param>
/// <param name="mediaSourceId">The media source identifier.</param>
/// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param>
/// <returns>MediaSourceInfo.</returns> /// <returns>MediaSourceInfo.</returns>
Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution); Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, string liveStreamId, bool enablePathSubstitution, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Opens the media source. /// Opens the media source.
@ -82,6 +80,8 @@ namespace MediaBrowser.Controller.Library
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;MediaSourceInfo&gt;.</returns> /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
Task<MediaSourceInfo> GetLiveStream(string id, CancellationToken cancellationToken); Task<MediaSourceInfo> GetLiveStream(string id, CancellationToken cancellationToken);
Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Pings the media source. /// Pings the media source.
@ -95,8 +95,12 @@ namespace MediaBrowser.Controller.Library
/// Closes the media source. /// Closes the media source.
/// </summary> /// </summary>
/// <param name="id">The live stream identifier.</param> /// <param name="id">The live stream identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task CloseLiveStream(string id, CancellationToken cancellationToken); Task CloseLiveStream(string id);
}
public interface IDirectStreamProvider
{
Task CopyToAsync(Stream stream, CancellationToken cancellationToken);
} }
} }

@ -3,6 +3,7 @@ using MediaBrowser.Model.Dto;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System;
namespace MediaBrowser.Controller.Library namespace MediaBrowser.Controller.Library
{ {
@ -22,14 +23,13 @@ namespace MediaBrowser.Controller.Library
/// <param name="openToken">The open token.</param> /// <param name="openToken">The open token.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;MediaSourceInfo&gt;.</returns> /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
Task<MediaSourceInfo> OpenMediaSource(string openToken, CancellationToken cancellationToken); Task<Tuple<MediaSourceInfo,IDirectStreamProvider>> OpenMediaSource(string openToken, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Closes the media source. /// Closes the media source.
/// </summary> /// </summary>
/// <param name="liveStreamId">The live stream identifier.</param> /// <param name="liveStreamId">The live stream identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken); Task CloseMediaSource(string liveStreamId);
} }
} }

@ -54,7 +54,7 @@ namespace MediaBrowser.Controller.Library
} }
} }
class TextComparer : IComparer<string>, IEqualityComparer<string> public class DistinctNameComparer : IComparer<string>, IEqualityComparer<string>
{ {
public int Compare(string x, string y) public int Compare(string x, string y)
{ {
@ -63,7 +63,7 @@ namespace MediaBrowser.Controller.Library
return 0; return 0;
} }
return string.Compare(x, y, CultureInfo.InvariantCulture, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace); return string.Compare(x.RemoveDiacritics(), y.RemoveDiacritics(), StringComparison.OrdinalIgnoreCase);
} }
public bool Equals(string x, string y) public bool Equals(string x, string y)

@ -1,15 +0,0 @@
using MediaBrowser.Model.Entities;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.LiveTv
{
public interface IHasRegistrationInfo
{
/// <summary>
/// Gets the registration information.
/// </summary>
/// <param name="feature">The feature.</param>
/// <returns>Task&lt;MBRegistrationRecord&gt;.</returns>
Task<MBRegistrationRecord> GetRegistrationInfo(string feature);
}
}

@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using MediaBrowser.Controller.Library;
namespace MediaBrowser.Controller.LiveTv namespace MediaBrowser.Controller.LiveTv
{ {
@ -37,7 +38,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{SeriesTimerInfoDto}.</returns> /// <returns>Task{SeriesTimerInfoDto}.</returns>
Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken); Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Deletes the recording. /// Deletes the recording.
/// </summary> /// </summary>
@ -51,7 +52,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="recording">The recording.</param> /// <param name="recording">The recording.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task DeleteRecording(BaseItem recording); Task DeleteRecording(BaseItem recording);
/// <summary> /// <summary>
/// Cancels the timer. /// Cancels the timer.
/// </summary> /// </summary>
@ -83,7 +84,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="user">The user.</param> /// <param name="user">The user.</param>
/// <returns>Task{RecordingInfoDto}.</returns> /// <returns>Task{RecordingInfoDto}.</returns>
Task<BaseItemDto> GetRecording(string id, DtoOptions options, CancellationToken cancellationToken, User user = null); Task<BaseItemDto> GetRecording(string id, DtoOptions options, CancellationToken cancellationToken, User user = null);
/// <summary> /// <summary>
/// Gets the timer. /// Gets the timer.
/// </summary> /// </summary>
@ -108,6 +109,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>QueryResult{RecordingInfoDto}.</returns> /// <returns>QueryResult{RecordingInfoDto}.</returns>
Task<QueryResult<BaseItemDto>> GetRecordings(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken); Task<QueryResult<BaseItemDto>> GetRecordings(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken);
Task<QueryResult<BaseItemDto>> GetRecordingSeries(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the timers. /// Gets the timers.
@ -124,14 +126,14 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{QueryResult{SeriesTimerInfoDto}}.</returns> /// <returns>Task{QueryResult{SeriesTimerInfoDto}}.</returns>
Task<QueryResult<SeriesTimerInfoDto>> GetSeriesTimers(SeriesTimerQuery query, CancellationToken cancellationToken); Task<QueryResult<SeriesTimerInfoDto>> GetSeriesTimers(SeriesTimerQuery query, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the channel. /// Gets the channel.
/// </summary> /// </summary>
/// <param name="id">The identifier.</param> /// <param name="id">The identifier.</param>
/// <returns>Channel.</returns> /// <returns>Channel.</returns>
LiveTvChannel GetInternalChannel(string id); LiveTvChannel GetInternalChannel(string id);
/// <summary> /// <summary>
/// Gets the recording. /// Gets the recording.
/// </summary> /// </summary>
@ -155,8 +157,8 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="mediaSourceId">The media source identifier.</param> /// <param name="mediaSourceId">The media source identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{StreamResponseInfo}.</returns> /// <returns>Task{StreamResponseInfo}.</returns>
Task<MediaSourceInfo> GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken); Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the program. /// Gets the program.
/// </summary> /// </summary>
@ -219,9 +221,8 @@ namespace MediaBrowser.Controller.LiveTv
/// Closes the live stream. /// Closes the live stream.
/// </summary> /// </summary>
/// <param name="id">The identifier.</param> /// <param name="id">The identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task CloseLiveStream(string id, CancellationToken cancellationToken); Task CloseLiveStream(string id);
/// <summary> /// <summary>
/// Gets the guide information. /// Gets the guide information.
@ -241,10 +242,8 @@ namespace MediaBrowser.Controller.LiveTv
/// <summary> /// <summary>
/// Gets the recommended programs internal. /// Gets the recommended programs internal.
/// </summary> /// </summary>
/// <param name="query">The query.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;QueryResult&lt;LiveTvProgram&gt;&gt;.</returns> /// <returns>Task&lt;QueryResult&lt;LiveTvProgram&gt;&gt;.</returns>
Task<QueryResult<LiveTvProgram>> GetRecommendedProgramsInternal(RecommendedProgramQuery query, CancellationToken cancellationToken); Task<QueryResult<LiveTvProgram>> GetRecommendedProgramsInternal(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the live tv information. /// Gets the live tv information.
@ -302,18 +301,12 @@ namespace MediaBrowser.Controller.LiveTv
/// <summary> /// <summary>
/// Gets the recording media sources. /// Gets the recording media sources.
/// </summary> /// </summary>
/// <param name="id">The identifier.</param> Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(IHasMediaSources item, CancellationToken cancellationToken);
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;IEnumerable&lt;MediaSourceInfo&gt;&gt;.</returns>
Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(string id, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the channel media sources. /// Gets the channel media sources.
/// </summary> /// </summary>
/// <param name="id">The identifier.</param> Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(IHasMediaSources item, CancellationToken cancellationToken);
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;IEnumerable&lt;MediaSourceInfo&gt;&gt;.</returns>
Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(string id, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Adds the information to recording dto. /// Adds the information to recording dto.
@ -330,8 +323,8 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="fields">The fields.</param> /// <param name="fields">The fields.</param>
/// <param name="user">The user.</param> /// <param name="user">The user.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task AddInfoToProgramDto(List<Tuple<BaseItem,BaseItemDto>> programs, List<ItemFields> fields, User user = null); Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> programs, List<ItemFields> fields, User user = null);
/// <summary> /// <summary>
/// Saves the tuner host. /// Saves the tuner host.
/// </summary> /// </summary>
@ -364,11 +357,9 @@ namespace MediaBrowser.Controller.LiveTv
/// <summary> /// <summary>
/// Gets the registration information. /// Gets the registration information.
/// </summary> /// </summary>
/// <param name="channelId">The channel identifier.</param>
/// <param name="programId">The program identifier.</param>
/// <param name="feature">The feature.</param> /// <param name="feature">The feature.</param>
/// <returns>Task&lt;MBRegistrationRecord&gt;.</returns> /// <returns>Task&lt;MBRegistrationRecord&gt;.</returns>
Task<MBRegistrationRecord> GetRegistrationInfo(string channelId, string programId, string feature); Task<MBRegistrationRecord> GetRegistrationInfo(string feature);
/// <summary> /// <summary>
/// Adds the channel information. /// Adds the channel information.
@ -396,7 +387,7 @@ namespace MediaBrowser.Controller.LiveTv
Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken); Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken);
Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken); Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken);
List<IListingsProvider> ListingProviders { get;} List<IListingsProvider> ListingProviders { get; }
event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled; event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled; event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;

@ -33,6 +33,7 @@ namespace MediaBrowser.Controller.LiveTv
bool CanDelete(User user); bool CanDelete(User user);
string SeriesTimerId { get; set; } string SeriesTimerId { get; set; }
string TimerId { get; set; }
RecordingStatus Status { get; set; } RecordingStatus Status { get; set; }
DateTime? EndDate { get; set; } DateTime? EndDate { get; set; }
DateTime DateLastSaved { get; set; } DateTime DateLastSaved { get; set; }

@ -4,6 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
namespace MediaBrowser.Controller.LiveTv namespace MediaBrowser.Controller.LiveTv
{ {
@ -245,4 +246,14 @@ namespace MediaBrowser.Controller.LiveTv
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task<string> CreateSeriesTimer(SeriesTimerInfo info, CancellationToken cancellationToken); Task<string> CreateSeriesTimer(SeriesTimerInfo info, CancellationToken cancellationToken);
} }
public interface ISupportsDirectStreamProvider
{
Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetChannelStreamWithDirectStreamProvider(string channelId, string streamId, CancellationToken cancellationToken);
}
public interface ISupportsUpdatingDefaults
{
Task UpdateTimerDefaults(SeriesTimerInfo info, CancellationToken cancellationToken);
}
} }

@ -22,9 +22,8 @@ namespace MediaBrowser.Controller.LiveTv
/// <summary> /// <summary>
/// Gets the channels. /// Gets the channels.
/// </summary> /// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;IEnumerable&lt;ChannelInfo&gt;&gt;.</returns> /// <returns>Task&lt;IEnumerable&lt;ChannelInfo&gt;&gt;.</returns>
Task<IEnumerable<ChannelInfo>> GetChannels(CancellationToken cancellationToken); Task<IEnumerable<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the tuner infos. /// Gets the tuner infos.
/// </summary> /// </summary>
@ -38,7 +37,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="streamId">The stream identifier.</param> /// <param name="streamId">The stream identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;MediaSourceInfo&gt;.</returns> /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
Task<Tuple<MediaSourceInfo,SemaphoreSlim>> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken); Task<LiveStream> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the channel stream media sources. /// Gets the channel stream media sources.
/// </summary> /// </summary>
@ -46,8 +45,6 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns> /// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns>
Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
string ApplyDuration(string streamPath, TimeSpan duration);
} }
public interface IConfigurableTunerHost public interface IConfigurableTunerHost
{ {

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Controller.LiveTv
{
public class LiveStream
{
public MediaSourceInfo OriginalMediaSource { get; set; }
public MediaSourceInfo OpenedMediaSource { get; set; }
public int ConsumerCount {
get { return SharedStreamIds.Count; }
}
public ITunerHost TunerHost { get; set; }
public string OriginalStreamId { get; set; }
public bool EnableStreamSharing { get; set; }
public string UniqueId = Guid.NewGuid().ToString("N");
public List<string> SharedStreamIds = new List<string>();
public LiveStream(MediaSourceInfo mediaSource)
{
OriginalMediaSource = mediaSource;
OpenedMediaSource = mediaSource;
EnableStreamSharing = true;
}
public Task Open(CancellationToken cancellationToken)
{
return OpenInternal(cancellationToken);
}
protected virtual Task OpenInternal(CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
public virtual Task Close()
{
return Task.FromResult(true);
}
}
}

@ -20,6 +20,7 @@ namespace MediaBrowser.Controller.LiveTv
[IgnoreDataMember] [IgnoreDataMember]
public bool IsSeries { get; set; } public bool IsSeries { get; set; }
public string SeriesTimerId { get; set; } public string SeriesTimerId { get; set; }
public string TimerId { get; set; }
[IgnoreDataMember] [IgnoreDataMember]
public DateTime StartDate { get; set; } public DateTime StartDate { get; set; }
public RecordingStatus Status { get; set; } public RecordingStatus Status { get; set; }
@ -112,7 +113,7 @@ namespace MediaBrowser.Controller.LiveTv
public override bool CanDelete() public override bool CanDelete()
{ {
return true; return Status == RecordingStatus.Completed;
} }
public override bool IsAuthorizedToDelete(User user) public override bool IsAuthorizedToDelete(User user)

@ -9,7 +9,7 @@ using System.Runtime.Serialization;
namespace MediaBrowser.Controller.LiveTv namespace MediaBrowser.Controller.LiveTv
{ {
public class LiveTvChannel : BaseItem, IHasMediaSources public class LiveTvChannel : BaseItem, IHasMediaSources, IHasProgramAttributes
{ {
public override List<string> GetUserDataKeys() public override List<string> GetUserDataKeys()
{ {
@ -137,5 +137,56 @@ namespace MediaBrowser.Controller.LiveTv
{ {
return false; return false;
} }
[IgnoreDataMember]
public bool IsMovie { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is sports.
/// </summary>
/// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
[IgnoreDataMember]
public bool IsSports { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is series.
/// </summary>
/// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
[IgnoreDataMember]
public bool IsSeries { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is live.
/// </summary>
/// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
[IgnoreDataMember]
public bool IsLive { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is news.
/// </summary>
/// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
[IgnoreDataMember]
public bool IsNews { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is kids.
/// </summary>
/// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
[IgnoreDataMember]
public bool IsKids { get; set; }
[IgnoreDataMember]
public bool IsPremiere { get; set; }
[IgnoreDataMember]
public bool IsRepeat { get; set; }
/// <summary>
/// Gets or sets the episode title.
/// </summary>
/// <value>The episode title.</value>
[IgnoreDataMember]
public string EpisodeTitle { get; set; }
} }
} }

@ -19,6 +19,7 @@ namespace MediaBrowser.Controller.LiveTv
[IgnoreDataMember] [IgnoreDataMember]
public bool IsSeries { get; set; } public bool IsSeries { get; set; }
public string SeriesTimerId { get; set; } public string SeriesTimerId { get; set; }
public string TimerId { get; set; }
[IgnoreDataMember] [IgnoreDataMember]
public DateTime StartDate { get; set; } public DateTime StartDate { get; set; }
public RecordingStatus Status { get; set; } public RecordingStatus Status { get; set; }
@ -111,7 +112,7 @@ namespace MediaBrowser.Controller.LiveTv
public override bool CanDelete() public override bool CanDelete()
{ {
return true; return Status == RecordingStatus.Completed;
} }
public override bool IsAuthorizedToDelete(User user) public override bool IsAuthorizedToDelete(User user)

@ -1,6 +1,7 @@
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.LiveTv namespace MediaBrowser.Controller.LiveTv
{ {
@ -66,6 +67,8 @@ namespace MediaBrowser.Controller.LiveTv
/// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value>
public bool? IsHD { get; set; } public bool? IsHD { get; set; }
public bool? Is3D { get; set; }
/// <summary> /// <summary>
/// Gets or sets the audio. /// Gets or sets the audio.
/// </summary> /// </summary>
@ -84,6 +87,8 @@ namespace MediaBrowser.Controller.LiveTv
/// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value>
public bool IsRepeat { get; set; } public bool IsRepeat { get; set; }
public bool IsSubjectToBlackout { get; set; }
/// <summary> /// <summary>
/// Gets or sets the episode title. /// Gets or sets the episode title.
/// </summary> /// </summary>
@ -102,6 +107,8 @@ namespace MediaBrowser.Controller.LiveTv
/// <value>The image URL.</value> /// <value>The image URL.</value>
public string ImageUrl { get; set; } public string ImageUrl { get; set; }
public string LogoImageUrl { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this instance has image. /// Gets or sets a value indicating whether this instance has image.
/// </summary> /// </summary>
@ -144,6 +151,8 @@ namespace MediaBrowser.Controller.LiveTv
/// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
public bool IsKids { get; set; } public bool IsKids { get; set; }
public bool IsEducational { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this instance is premiere. /// Gets or sets a value indicating whether this instance is premiere.
/// </summary> /// </summary>

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using MediaBrowser.Model.LiveTv;
namespace MediaBrowser.Controller.LiveTv namespace MediaBrowser.Controller.LiveTv
{ {
@ -26,6 +27,8 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
public string ServiceName { get; set; }
/// <summary> /// <summary>
/// Description of the recording. /// Description of the recording.
/// </summary> /// </summary>
@ -53,6 +56,11 @@ namespace MediaBrowser.Controller.LiveTv
/// <value><c>true</c> if [record any channel]; otherwise, <c>false</c>.</value> /// <value><c>true</c> if [record any channel]; otherwise, <c>false</c>.</value>
public bool RecordAnyChannel { get; set; } public bool RecordAnyChannel { get; set; }
public int KeepUpTo { get; set; }
public KeepUntil KeepUntil { get; set; }
public bool SkipEpisodesInLibrary { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether [record new only]. /// Gets or sets a value indicating whether [record new only].
/// </summary> /// </summary>
@ -104,6 +112,8 @@ namespace MediaBrowser.Controller.LiveTv
public SeriesTimerInfo() public SeriesTimerInfo()
{ {
Days = new List<DayOfWeek>(); Days = new List<DayOfWeek>();
SkipEpisodesInLibrary = true;
KeepUntil = KeepUntil.UntilDeleted;
} }
} }
} }

@ -1,10 +1,17 @@
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
using System; using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.LiveTv namespace MediaBrowser.Controller.LiveTv
{ {
public class TimerInfo public class TimerInfo
{ {
public TimerInfo()
{
Genres = new List<string>();
KeepUntil = KeepUntil.UntilDeleted;
}
/// <summary> /// <summary>
/// Id of the recording. /// Id of the recording.
/// </summary> /// </summary>
@ -15,7 +22,7 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary> /// </summary>
/// <value>The series timer identifier.</value> /// <value>The series timer identifier.</value>
public string SeriesTimerId { get; set; } public string SeriesTimerId { get; set; }
/// <summary> /// <summary>
/// ChannelId of the recording. /// ChannelId of the recording.
/// </summary> /// </summary>
@ -26,7 +33,7 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary> /// </summary>
/// <value>The program identifier.</value> /// <value>The program identifier.</value>
public string ProgramId { get; set; } public string ProgramId { get; set; }
/// <summary> /// <summary>
/// Name of the recording. /// Name of the recording.
/// </summary> /// </summary>
@ -76,11 +83,36 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary> /// </summary>
/// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value>
public bool IsPostPaddingRequired { get; set; } public bool IsPostPaddingRequired { get; set; }
/// <summary> /// <summary>
/// Gets or sets the priority. /// Gets or sets the priority.
/// </summary> /// </summary>
/// <value>The priority.</value> /// <value>The priority.</value>
public int Priority { get; set; } public int Priority { get; set; }
// Program properties
public int? SeasonNumber { get; set; }
/// <summary>
/// Gets or sets the episode number.
/// </summary>
/// <value>The episode number.</value>
public int? EpisodeNumber { get; set; }
public bool IsMovie { get; set; }
public bool IsKids { get; set; }
public bool IsSports { get; set; }
public bool IsNews { get; set; }
public int? ProductionYear { get; set; }
public string EpisodeTitle { get; set; }
public DateTime? OriginalAirDate { get; set; }
public bool IsProgramSeries { get; set; }
public bool IsRepeat { get; set; }
public string HomePageUrl { get; set; }
public float? CommunityRating { get; set; }
public string ShortOverview { get; set; }
public string OfficialRating { get; set; }
public List<string> Genres { get; set; }
public string RecordingPath { get; set; }
public KeepUntil KeepUntil { get; set; }
} }
} }

@ -91,13 +91,11 @@
<Compile Include="Channels\InternalChannelItemQuery.cs" /> <Compile Include="Channels\InternalChannelItemQuery.cs" />
<Compile Include="Channels\IRequiresMediaInfoCallback.cs" /> <Compile Include="Channels\IRequiresMediaInfoCallback.cs" />
<Compile Include="Channels\ISearchableChannel.cs" /> <Compile Include="Channels\ISearchableChannel.cs" />
<Compile Include="Chapters\ChapterSearchRequest.cs" />
<Compile Include="Chapters\IChapterManager.cs" /> <Compile Include="Chapters\IChapterManager.cs" />
<Compile Include="Chapters\IChapterProvider.cs" />
<Compile Include="Chapters\ChapterResponse.cs" />
<Compile Include="Collections\CollectionCreationOptions.cs" /> <Compile Include="Collections\CollectionCreationOptions.cs" />
<Compile Include="Collections\CollectionEvents.cs" /> <Compile Include="Collections\CollectionEvents.cs" />
<Compile Include="Collections\ICollectionManager.cs" /> <Compile Include="Collections\ICollectionManager.cs" />
<Compile Include="Collections\ManualCollectionsFolder.cs" />
<Compile Include="Connect\ConnectSupporterSummary.cs" /> <Compile Include="Connect\ConnectSupporterSummary.cs" />
<Compile Include="Connect\IConnectManager.cs" /> <Compile Include="Connect\IConnectManager.cs" />
<Compile Include="Connect\UserLinkResult.cs" /> <Compile Include="Connect\UserLinkResult.cs" />
@ -134,7 +132,6 @@
<Compile Include="Entities\Game.cs" /> <Compile Include="Entities\Game.cs" />
<Compile Include="Entities\GameGenre.cs" /> <Compile Include="Entities\GameGenre.cs" />
<Compile Include="Entities\GameSystem.cs" /> <Compile Include="Entities\GameSystem.cs" />
<Compile Include="Entities\IArchivable.cs" />
<Compile Include="Entities\IByReferenceItem.cs" /> <Compile Include="Entities\IByReferenceItem.cs" />
<Compile Include="Entities\IHasAspectRatio.cs" /> <Compile Include="Entities\IHasAspectRatio.cs" />
<Compile Include="Entities\IHasBudget.cs" /> <Compile Include="Entities\IHasBudget.cs" />
@ -146,15 +143,12 @@
<Compile Include="Entities\IHasMediaSources.cs" /> <Compile Include="Entities\IHasMediaSources.cs" />
<Compile Include="Entities\IHasMetascore.cs" /> <Compile Include="Entities\IHasMetascore.cs" />
<Compile Include="Entities\IHasOriginalTitle.cs" /> <Compile Include="Entities\IHasOriginalTitle.cs" />
<Compile Include="Entities\IHasProductionLocations.cs" />
<Compile Include="Entities\IHasProgramAttributes.cs" /> <Compile Include="Entities\IHasProgramAttributes.cs" />
<Compile Include="Entities\IHasScreenshots.cs" /> <Compile Include="Entities\IHasScreenshots.cs" />
<Compile Include="Entities\IHasSeries.cs" /> <Compile Include="Entities\IHasSeries.cs" />
<Compile Include="Entities\IHasShortOverview.cs" /> <Compile Include="Entities\IHasShortOverview.cs" />
<Compile Include="Entities\IHasSpecialFeatures.cs" /> <Compile Include="Entities\IHasSpecialFeatures.cs" />
<Compile Include="Entities\IHasStartDate.cs" /> <Compile Include="Entities\IHasStartDate.cs" />
<Compile Include="Entities\IHasTaglines.cs" />
<Compile Include="Entities\IHasThemeMedia.cs" />
<Compile Include="Entities\IHasTrailers.cs" /> <Compile Include="Entities\IHasTrailers.cs" />
<Compile Include="Entities\IHasUserData.cs" /> <Compile Include="Entities\IHasUserData.cs" />
<Compile Include="Entities\IHiddenFromDisplay.cs" /> <Compile Include="Entities\IHiddenFromDisplay.cs" />
@ -198,9 +192,9 @@
<Compile Include="Library\NameExtensions.cs" /> <Compile Include="Library\NameExtensions.cs" />
<Compile Include="Library\PlaybackStopEventArgs.cs" /> <Compile Include="Library\PlaybackStopEventArgs.cs" />
<Compile Include="Library\UserDataSaveEventArgs.cs" /> <Compile Include="Library\UserDataSaveEventArgs.cs" />
<Compile Include="LiveTv\IHasRegistrationInfo.cs" />
<Compile Include="LiveTv\IListingsProvider.cs" /> <Compile Include="LiveTv\IListingsProvider.cs" />
<Compile Include="LiveTv\ITunerHost.cs" /> <Compile Include="LiveTv\ITunerHost.cs" />
<Compile Include="LiveTv\LiveStream.cs" />
<Compile Include="LiveTv\RecordingGroup.cs" /> <Compile Include="LiveTv\RecordingGroup.cs" />
<Compile Include="LiveTv\RecordingStatusChangedEventArgs.cs" /> <Compile Include="LiveTv\RecordingStatusChangedEventArgs.cs" />
<Compile Include="LiveTv\ILiveTvRecording.cs" /> <Compile Include="LiveTv\ILiveTvRecording.cs" />
@ -266,7 +260,6 @@
<Compile Include="Playlists\IPlaylistManager.cs" /> <Compile Include="Playlists\IPlaylistManager.cs" />
<Compile Include="Playlists\Playlist.cs" /> <Compile Include="Playlists\Playlist.cs" />
<Compile Include="Plugins\ILocalizablePlugin.cs" /> <Compile Include="Plugins\ILocalizablePlugin.cs" />
<Compile Include="Power\IPowerManagement.cs" />
<Compile Include="Providers\AlbumInfo.cs" /> <Compile Include="Providers\AlbumInfo.cs" />
<Compile Include="Providers\ArtistInfo.cs" /> <Compile Include="Providers\ArtistInfo.cs" />
<Compile Include="Providers\BookInfo.cs" /> <Compile Include="Providers\BookInfo.cs" />
@ -290,9 +283,7 @@
<Compile Include="Providers\IHasItemChangeMonitor.cs" /> <Compile Include="Providers\IHasItemChangeMonitor.cs" />
<Compile Include="Providers\IHasLookupInfo.cs" /> <Compile Include="Providers\IHasLookupInfo.cs" />
<Compile Include="Providers\IHasOrder.cs" /> <Compile Include="Providers\IHasOrder.cs" />
<Compile Include="Providers\IImageFileSaver.cs" />
<Compile Include="Providers\IImageProvider.cs" /> <Compile Include="Providers\IImageProvider.cs" />
<Compile Include="Providers\IImageSaver.cs" />
<Compile Include="Providers\ILocalImageFileProvider.cs" /> <Compile Include="Providers\ILocalImageFileProvider.cs" />
<Compile Include="Providers\ILocalMetadataProvider.cs" /> <Compile Include="Providers\ILocalMetadataProvider.cs" />
<Compile Include="Providers\ImageRefreshMode.cs" /> <Compile Include="Providers\ImageRefreshMode.cs" />
@ -319,7 +310,6 @@
<Compile Include="Providers\SongInfo.cs" /> <Compile Include="Providers\SongInfo.cs" />
<Compile Include="Providers\TrailerInfo.cs" /> <Compile Include="Providers\TrailerInfo.cs" />
<Compile Include="Providers\VideoContentType.cs" /> <Compile Include="Providers\VideoContentType.cs" />
<Compile Include="RelatedMedia\IRelatedMediaProvider.cs" />
<Compile Include="Security\AuthenticationInfo.cs" /> <Compile Include="Security\AuthenticationInfo.cs" />
<Compile Include="Security\AuthenticationInfoQuery.cs" /> <Compile Include="Security\AuthenticationInfoQuery.cs" />
<Compile Include="Security\IAuthenticationRepository.cs" /> <Compile Include="Security\IAuthenticationRepository.cs" />

@ -45,9 +45,9 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <param name="offset">The offset.</param> /// <param name="offset">The offset.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{Stream}.</returns> /// <returns>Task{Stream}.</returns>
Task<string> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken); Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
Task<string> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, int? imageStreamIndex, CancellationToken cancellationToken); Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, int? imageStreamIndex, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Extracts the video images on interval. /// Extracts the video images on interval.

@ -120,7 +120,7 @@ namespace MediaBrowser.Controller.Net
var cancellationTokenSource = new CancellationTokenSource(); var cancellationTokenSource = new CancellationTokenSource();
Logger.Info("{1} Begin transmitting over websocket to {0}", message.Connection.RemoteEndPoint, GetType().Name); Logger.Debug("{1} Begin transmitting over websocket to {0}", message.Connection.RemoteEndPoint, GetType().Name);
var timer = SendOnTimer ? var timer = SendOnTimer ?
new Timer(TimerCallback, message.Connection, Timeout.Infinite, Timeout.Infinite) : new Timer(TimerCallback, message.Connection, Timeout.Infinite, Timeout.Infinite) :
@ -267,7 +267,7 @@ namespace MediaBrowser.Controller.Net
/// <param name="connection">The connection.</param> /// <param name="connection">The connection.</param>
private void DisposeConnection(Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> connection) private void DisposeConnection(Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> connection)
{ {
Logger.Info("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name); Logger.Debug("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name);
var timer = connection.Item3; var timer = connection.Item3;

@ -11,14 +11,6 @@ namespace MediaBrowser.Controller.Net
/// </summary> /// </summary>
public interface IHttpResultFactory public interface IHttpResultFactory
{ {
/// <summary>
/// Throws the error.
/// </summary>
/// <param name="statusCode">The status code.</param>
/// <param name="errorMessage">The error message.</param>
/// <param name="responseHeaders">The response headers.</param>
void ThrowError(int statusCode, string errorMessage, IDictionary<string, string> responseHeaders = null);
/// <summary> /// <summary>
/// Gets the result. /// Gets the result.
/// </summary> /// </summary>

@ -1,13 +0,0 @@
using System;
namespace MediaBrowser.Controller.Power
{
public interface IPowerManagement
{
/// <summary>
/// Schedules the wake.
/// </summary>
/// <param name="utcTime">The UTC time.</param>
void ScheduleWake(DateTime utcTime);
}
}

@ -23,14 +23,18 @@ namespace MediaBrowser.Controller.Providers
/// The logger /// The logger
/// </summary> /// </summary>
protected ILogger Logger { get; private set; } protected ILogger Logger { get; private set; }
protected IProviderManager ProviderManager { get; private set; }
private Dictionary<string, string> _validProviderIds;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class. /// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class.
/// </summary> /// </summary>
/// <param name="logger">The logger.</param> /// <param name="logger">The logger.</param>
public BaseItemXmlParser(ILogger logger) public BaseItemXmlParser(ILogger logger, IProviderManager providerManager)
{ {
Logger = logger; Logger = logger;
ProviderManager = providerManager;
} }
/// <summary> /// <summary>
@ -60,6 +64,22 @@ namespace MediaBrowser.Controller.Providers
ValidationType = ValidationType.None ValidationType = ValidationType.None
}; };
_validProviderIds = _validProviderIds = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
var idInfos = ProviderManager.GetExternalIdInfos(item.Item);
foreach (var info in idInfos)
{
var id = info.Key + "Id";
if (!_validProviderIds.ContainsKey(id))
{
_validProviderIds.Add(id, info.Key);
}
}
//Additional Mappings
_validProviderIds.Add("IMDB", "Imdb");
//Fetch(item, metadataFile, settings, Encoding.GetEncoding("ISO-8859-1"), cancellationToken); //Fetch(item, metadataFile, settings, Encoding.GetEncoding("ISO-8859-1"), cancellationToken);
Fetch(item, metadataFile, settings, Encoding.UTF8, cancellationToken); Fetch(item, metadataFile, settings, Encoding.UTF8, cancellationToken);
} }
@ -327,7 +347,7 @@ namespace MediaBrowser.Controller.Providers
var person = item as Person; var person = item as Person;
if (person != null) if (person != null)
{ {
person.PlaceOfBirth = val; person.ProductionLocations = new List<string> { val };
} }
} }
@ -657,14 +677,6 @@ namespace MediaBrowser.Controller.Providers
break; break;
} }
case "TvDbId":
var tvdbId = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(tvdbId))
{
item.SetProviderId(MetadataProviders.Tvdb, tvdbId);
}
break;
case "VoteCount": case "VoteCount":
{ {
var val = reader.ReadElementContentAsString(); var val = reader.ReadElementContentAsString();
@ -679,95 +691,7 @@ namespace MediaBrowser.Controller.Providers
} }
break; break;
} }
case "MusicBrainzAlbumId": case "CollectionNumber":
{
var mbz = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(mbz))
{
item.SetProviderId(MetadataProviders.MusicBrainzAlbum, mbz);
}
break;
}
case "MusicBrainzAlbumArtistId":
{
var mbz = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(mbz))
{
item.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, mbz);
}
break;
}
case "MusicBrainzArtistId":
{
var mbz = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(mbz))
{
item.SetProviderId(MetadataProviders.MusicBrainzArtist, mbz);
}
break;
}
case "MusicBrainzReleaseGroupId":
{
var mbz = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(mbz))
{
item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, mbz);
}
break;
}
case "TVRageId":
{
var id = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(id))
{
item.SetProviderId(MetadataProviders.TvRage, id);
}
break;
}
case "TvMazeId":
{
var id = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(id))
{
item.SetProviderId(MetadataProviders.TvMaze, id);
}
break;
}
case "AudioDbArtistId":
{
var id = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(id))
{
item.SetProviderId(MetadataProviders.AudioDbArtist, id);
}
break;
}
case "AudioDbAlbumId":
{
var id = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(id))
{
item.SetProviderId(MetadataProviders.AudioDbAlbum, id);
}
break;
}
case "RottenTomatoesId":
var rtId = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(rtId))
{
item.SetProviderId(MetadataProviders.RottenTomatoes, rtId);
}
break;
case "TMDbId":
var tmdb = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(tmdb))
{
item.SetProviderId(MetadataProviders.Tmdb, tmdb);
}
break;
case "TMDbCollectionId":
var tmdbCollection = reader.ReadElementContentAsString(); var tmdbCollection = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(tmdbCollection)) if (!string.IsNullOrWhiteSpace(tmdbCollection))
{ {
@ -775,30 +699,6 @@ namespace MediaBrowser.Controller.Providers
} }
break; break;
case "TVcomId":
var TVcomId = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(TVcomId))
{
item.SetProviderId(MetadataProviders.Tvcom, TVcomId);
}
break;
case "Zap2ItId":
var zap2ItId = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(zap2ItId))
{
item.SetProviderId(MetadataProviders.Zap2It, zap2ItId);
}
break;
case "IMDB":
var imDbId = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(imDbId))
{
item.SetProviderId(MetadataProviders.Imdb, imDbId);
}
break;
case "Genres": case "Genres":
{ {
using (var subtree = reader.ReadSubtree()) using (var subtree = reader.ReadSubtree())
@ -890,8 +790,25 @@ namespace MediaBrowser.Controller.Providers
} }
default: default:
reader.Skip(); {
break; string readerName = reader.Name;
string providerIdValue;
if (_validProviderIds.TryGetValue(readerName, out providerIdValue))
{
var id = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(id))
{
item.SetProviderId(providerIdValue, id);
}
}
else
{
reader.Skip();
}
break;
}
} }
} }
@ -976,14 +893,6 @@ namespace MediaBrowser.Controller.Providers
if (!string.IsNullOrWhiteSpace(val)) if (!string.IsNullOrWhiteSpace(val))
{ {
var hasProductionLocations = item as IHasProductionLocations;
if (hasProductionLocations != null)
{
if (!string.IsNullOrWhiteSpace(val))
{
hasProductionLocations.AddProductionLocation(val);
}
}
} }
break; break;
} }
@ -1017,14 +926,7 @@ namespace MediaBrowser.Controller.Providers
if (!string.IsNullOrWhiteSpace(val)) if (!string.IsNullOrWhiteSpace(val))
{ {
var hasTaglines = item as IHasTaglines; item.Tagline = val;
if (hasTaglines != null)
{
if (!string.IsNullOrWhiteSpace(val))
{
hasTaglines.AddTagline(val);
}
}
} }
break; break;
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save