pull/702/head
hatharry 8 years ago
commit d6862cefd8

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

@ -829,18 +829,7 @@ namespace Emby.Drawing
// Run the enhancers sequentially in order of priority
foreach (var enhancer in imageEnhancers)
{
var typeName = enhancer.GetType().Name;
try
{
await enhancer.EnhanceImageAsync(item, inputPath, outputPath, imageType, imageIndex).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("{0} failed enhancing {1}", ex, typeName, item.Name);
throw;
}
await enhancer.EnhanceImageAsync(item, inputPath, outputPath, imageType, imageIndex).ConfigureAwait(false);
// Feed the output into the next enhancer as input
inputPath = outputPath;

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

@ -7,6 +7,7 @@ using ServiceStack;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Controller.Session;
namespace MediaBrowser.Api
{
@ -76,12 +77,12 @@ namespace MediaBrowser.Api
public class ConnectService : BaseApiService
{
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;
_userManager = userManager;
_sessionManager = sessionManager;
}
public object Post(CreateConnectLink request)
@ -141,10 +142,33 @@ namespace MediaBrowser.Api
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
{
AccessToken = user.ConnectAccessKey,
LocalUserId = user.Id.ToString("N")
AccessToken = session.AccessToken,
LocalUserId = session.User.Id
});
}
}

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

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

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

@ -70,12 +70,13 @@ namespace MediaBrowser.Api
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 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.ContentType = configuredContentType;
@ -242,11 +243,7 @@ namespace MediaBrowser.Api
hasBudget.Revenue = request.Revenue;
}
var hasOriginalTitle = item as IHasOriginalTitle;
if (hasOriginalTitle != null)
{
hasOriginalTitle.OriginalTitle = hasOriginalTitle.OriginalTitle;
}
item.OriginalTitle = string.IsNullOrWhiteSpace(request.OriginalTitle) ? null : request.OriginalTitle;
var hasCriticRating = item as IHasCriticRating;
if (hasCriticRating != null)
@ -277,10 +274,9 @@ namespace MediaBrowser.Api
item.Tags = request.Tags;
var hasTaglines = item as IHasTaglines;
if (hasTaglines != null)
if (request.Taglines != null)
{
hasTaglines.Taglines = request.Taglines;
item.Tagline = request.Taglines.FirstOrDefault();
}
var hasShortOverview = item as IHasShortOverview;
@ -307,8 +303,6 @@ namespace MediaBrowser.Api
item.OfficialRating = string.IsNullOrWhiteSpace(request.OfficialRating) ? null : request.OfficialRating;
item.CustomRating = request.CustomRating;
SetProductionLocations(item, request);
item.PreferredMetadataCountryCode = request.PreferredMetadataCountryCode;
item.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
@ -416,23 +410,5 @@ namespace MediaBrowser.Api
series.AirTime = request.AirTime;
}
}
private void SetProductionLocations(BaseItem item, BaseItemDto request)
{
var hasProductionLocations = item as IHasProductionLocations;
if (hasProductionLocations != null)
{
hasProductionLocations.ProductionLocations = request.ProductionLocations;
}
var person = item as Person;
if (person != null)
{
person.PlaceOfBirth = request.ProductionLocations == null
? null
: request.ProductionLocations.FirstOrDefault();
}
}
}
}

@ -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.Tasks;
using CommonIO;
using MediaBrowser.Controller.Configuration;
namespace MediaBrowser.Api.Library
{
@ -288,12 +289,13 @@ namespace MediaBrowser.Api.Library
private readonly ITVSeriesManager _tvManager;
private readonly ILibraryMonitor _libraryMonitor;
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _config;
/// <summary>
/// Initializes a new instance of the <see cref="LibraryService" /> class.
/// </summary>
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;
_libraryManager = libraryManager;
@ -307,6 +309,7 @@ namespace MediaBrowser.Api.Library
_tvManager = tvManager;
_libraryMonitor = libraryMonitor;
_fileSystem = fileSystem;
_config = config;
}
public object Get(GetSimilarItems request)
@ -377,7 +380,7 @@ namespace MediaBrowser.Api.Library
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,
Logger = Logger,
@ -832,14 +835,14 @@ namespace MediaBrowser.Api.Library
: (Folder)_libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id);
while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
while (item.ThemeSongIds.Count == 0 && request.InheritFromParent && item.GetParent() != null)
{
item = item.GetParent();
}
var dtoOptions = GetDtoOptions(request);
var dtos = GetThemeSongIds(item).Select(_libraryManager.GetItemById)
var dtos = item.ThemeSongIds.Select(_libraryManager.GetItemById)
.Where(i => i != null)
.OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
@ -876,14 +879,14 @@ namespace MediaBrowser.Api.Library
: (Folder)_libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id);
while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
while (item.ThemeVideoIds.Count == 0 && request.InheritFromParent && item.GetParent() != null)
{
item = item.GetParent();
}
var dtoOptions = GetDtoOptions(request);
var dtos = GetThemeVideoIds(item).Select(_libraryManager.GetItemById)
var dtos = item.ThemeVideoIds.Select(_libraryManager.GetItemById)
.Where(i => i != null)
.OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
@ -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");
public object Get(GetYearIndex request)

@ -112,6 +112,8 @@ namespace MediaBrowser.Api.Library
/// <value>The name.</value>
public string Path { get; set; }
public MediaPathInfo PathInfo { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [refresh library].
/// </summary>
@ -119,6 +121,18 @@ namespace MediaBrowser.Api.Library
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")]
public class RemoveMediaPath : IReturnVoid
{
@ -212,7 +226,12 @@ namespace MediaBrowser.Api.Library
{
var libraryOptions = request.LibraryOptions ?? new LibraryOptions();
_libraryManager.AddVirtualFolder(request.Name, request.CollectionType, request.Paths, libraryOptions, request.RefreshLibrary);
if (request.Paths != null && request.Paths.Length > 0)
{
libraryOptions.PathInfos = request.Paths.Select(i => new MediaPathInfo { Path = i }).ToArray();
}
_libraryManager.AddVirtualFolder(request.Name, request.CollectionType, libraryOptions, request.RefreshLibrary);
}
/// <summary>
@ -308,7 +327,16 @@ namespace MediaBrowser.Api.Library
try
{
_libraryManager.AddMediaPath(request.Name, request.Path);
var mediaPath = request.PathInfo;
if (mediaPath == null)
{
mediaPath = new MediaPathInfo
{
Path = request.Path
};
}
_libraryManager.AddMediaPath(request.Name, mediaPath);
}
finally
{
@ -332,6 +360,20 @@ namespace MediaBrowser.Api.Library
}
}
/// <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>
/// Deletes the specified request.
/// </summary>

@ -12,9 +12,14 @@ using ServiceStack;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
using MediaBrowser.Api.Playback.Progressive;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Server.Implementations.LiveTv.EmbyTV;
namespace MediaBrowser.Api.LiveTv
{
@ -44,6 +49,21 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? StartIndex { get; set; }
[ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsMovie { get; set; }
[ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsSeries { get; set; }
[ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsNews { get; set; }
[ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsKids { get; set; }
[ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsSports { get; set; }
/// <summary>
/// The maximum number of items to return
/// </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")]
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()
{
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")]
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()
{
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")]
[Authenticated]
public class GetRecordingGroups : IReturn<QueryResult<BaseItemDto>>
@ -215,6 +316,8 @@ namespace MediaBrowser.Api.LiveTv
public string SeriesTimerId { get; set; }
public bool? IsActive { get; set; }
public bool? IsScheduled { get; set; }
}
[Route("/LiveTv/Programs", "GET,POST", Summary = "Gets available live tv epgs..")]
@ -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")]
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; }
@ -280,6 +389,8 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableUserData { get; set; }
public string SeriesTimerId { get; set; }
/// <summary>
/// Fields to return within the items, in addition to basic information
/// </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")]
public bool? HasAired { get; set; }
[ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsSports { get; set; }
[ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsSeries { get; set; }
[ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
[ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsMovie { get; set; }
[ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
[ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsNews { get; set; }
[ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsKids { get; set; }
[ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsSports { get; set; }
[ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableImages { get; set; }
@ -535,12 +652,6 @@ namespace MediaBrowser.Api.LiveTv
[Authenticated]
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")]
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
{
private readonly ILiveTvManager _liveTvManager;
private readonly IUserManager _userManager;
private readonly IConfigurationManager _config;
private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
private readonly IFileSystem _fileSystem;
public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService)
public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem)
{
_liveTvManager = liveTvManager;
_userManager = userManager;
@ -576,6 +701,41 @@ namespace MediaBrowser.Api.LiveTv
_httpClient = httpClient;
_libraryManager = libraryManager;
_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)
@ -592,7 +752,7 @@ namespace MediaBrowser.Api.LiveTv
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);
}
@ -648,7 +808,8 @@ namespace MediaBrowser.Api.LiveTv
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);
@ -732,6 +893,13 @@ namespace MediaBrowser.Api.LiveTv
IsLiked = request.IsLiked,
IsDisliked = request.IsDisliked,
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
}, CancellationToken.None).ConfigureAwait(false);
@ -814,9 +982,12 @@ namespace MediaBrowser.Api.LiveTv
query.Limit = request.Limit;
query.SortBy = (request.SortBy ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
query.SortOrder = request.SortOrder;
query.IsNews = request.IsNews;
query.IsMovie = request.IsMovie;
query.IsSeries = request.IsSeries;
query.IsKids = request.IsKids;
query.IsSports = request.IsSports;
query.SeriesTimerId = request.SeriesTimerId;
query.Genres = (request.Genres ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false);
@ -832,8 +1003,10 @@ namespace MediaBrowser.Api.LiveTv
IsAiring = request.IsAiring,
Limit = request.Limit,
HasAired = request.HasAired,
IsSeries = request.IsSeries,
IsMovie = request.IsMovie,
IsKids = request.IsKids,
IsNews = request.IsNews,
IsSports = request.IsSports,
EnableTotalRecordCount = request.EnableTotalRecordCount
};
@ -854,6 +1027,33 @@ namespace MediaBrowser.Api.LiveTv
options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;
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,
UserId = request.UserId,
@ -895,7 +1095,8 @@ namespace MediaBrowser.Api.LiveTv
{
ChannelId = request.ChannelId,
SeriesTimerId = request.SeriesTimerId,
IsActive = request.IsActive
IsActive = request.IsActive,
IsScheduled = request.IsScheduled
}, CancellationToken.None).ConfigureAwait(false);

@ -1,5 +1,5 @@
<?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')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -11,9 +11,10 @@
<AssemblyName>MediaBrowser.Api</AssemblyName>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<ReleaseVersion>
</ReleaseVersion>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -79,7 +80,6 @@
<Compile Include="Dlna\DlnaService.cs" />
<Compile Include="FilterService.cs" />
<Compile Include="IHasDtoOptions.cs" />
<Compile Include="Library\ChapterService.cs" />
<Compile Include="Playback\MediaInfoService.cs" />
<Compile Include="Playback\TranscodingThrottler.cs" />
<Compile Include="PlaylistService.cs" />
@ -198,6 +198,10 @@
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
<Name>MediaBrowser.Model</Name>
</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>
<None Include="packages.config" />

@ -14,6 +14,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.LiveTv;
namespace MediaBrowser.Api.Movies
@ -88,6 +89,7 @@ namespace MediaBrowser.Api.Movies
private readonly IItemRepository _itemRepo;
private readonly IDtoService _dtoService;
private readonly IServerConfigurationManager _config;
/// <summary>
/// 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="itemRepo">The item repo.</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;
_userDataRepository = userDataRepository;
_libraryManager = libraryManager;
_itemRepo = itemRepo;
_dtoService = dtoService;
_config = config;
}
/// <summary>
@ -146,23 +149,26 @@ namespace MediaBrowser.Api.Movies
(!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
_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)
{
Limit = request.Limit,
IncludeItemTypes = new[]
{
typeof(Movie).Name,
typeof(Trailer).Name,
typeof(LiveTvProgram).Name
},
IncludeItemTypes = itemTypes.ToArray(),
IsMovie = true,
SimilarTo = item,
EnableGroupByMetadataKey = true
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
}).ToList();
var dtoOptions = GetDtoOptions(request);
var result = new QueryResult<BaseItemDto>
{
Items = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false)).ToArray(),
@ -193,19 +199,22 @@ namespace MediaBrowser.Api.Movies
Limit = 7,
ParentId = parentIdGuid,
Recursive = true,
IsPlayed = true
IsPlayed = true,
DtoOptions = dtoOptions
};
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)
{
IncludeItemTypes = new[]
{
typeof(Movie).Name,
typeof(Trailer).Name,
typeof(LiveTvProgram).Name
},
IncludeItemTypes = itemTypes.ToArray(),
IsMovie = true,
SortBy = new[] { ItemSortBy.Random },
SortOrder = SortOrder.Descending,
@ -214,7 +223,8 @@ namespace MediaBrowser.Api.Movies
ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id.ToString("N")).ToArray(),
EnableGroupByMetadataKey = true,
ParentId = parentIdGuid,
Recursive = true
Recursive = true,
DtoOptions = dtoOptions
}).ToList();
@ -278,6 +288,13 @@ namespace MediaBrowser.Api.Movies
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)
{
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
Limit = itemLimit + 2,
PersonTypes = new[] { PersonType.Director },
IncludeItemTypes = new[]
{
typeof(Movie).Name,
typeof(Trailer).Name,
typeof(LiveTvProgram).Name
},
IncludeItemTypes = itemTypes.ToArray(),
IsMovie = true,
EnableGroupByMetadataKey = true
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
}).DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
.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)
{
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)
{
var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
@ -321,14 +341,10 @@ namespace MediaBrowser.Api.Movies
Person = name,
// Account for duplicates by imdb id, since the database doesn't support this yet
Limit = itemLimit + 2,
IncludeItemTypes = new[]
{
typeof(Movie).Name,
typeof(Trailer).Name,
typeof(LiveTvProgram).Name
},
IncludeItemTypes = itemTypes.ToArray(),
IsMovie = true,
EnableGroupByMetadataKey = true
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
}).DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
.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)
{
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)
{
var similar = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
Limit = itemLimit,
IncludeItemTypes = new[]
{
typeof(Movie).Name,
typeof(Trailer).Name,
typeof(LiveTvProgram).Name
},
IncludeItemTypes = itemTypes.ToArray(),
IsMovie = true,
SimilarTo = item,
EnableGroupByMetadataKey = true
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
}).ToList();

@ -46,7 +46,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The name.</value>
[ApiMember(Name = "PackageType", Description = "Optional package type filter (System/UserInstalled)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public PackageType? PackageType { get; set; }
public string PackageType { get; set; }
[ApiMember(Name = "TargetSystems", Description = "Optional. Filter by target system type. Allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET", AllowMultiple = true)]
public string TargetSystems { get; set; }
@ -72,7 +72,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The name.</value>
[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>
@ -149,12 +149,12 @@ namespace MediaBrowser.Api
{
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());
}
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;

@ -337,44 +337,62 @@ namespace MediaBrowser.Api.Playback
/// Gets the video bitrate to specify on the command line
/// </summary>
/// <param name="state">The state.</param>
/// <param name="videoCodec">The video codec.</param>
/// <param name="videoEncoder">The video codec.</param>
/// <returns>System.String.</returns>
protected string GetVideoQualityParam(StreamState state, string videoCodec)
protected string GetVideoQualityParam(StreamState state, string videoEncoder)
{
var param = string.Empty;
var isVc1 = state.VideoStream != null &&
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";
}
// 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)
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
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
var profileScore = 0;
@ -394,30 +412,30 @@ namespace MediaBrowser.Api.Playback
profileScore = Math.Min(profileScore, 2);
// 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),
crf,
qmin,
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
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);
if (framerate.HasValue)
@ -432,8 +450,8 @@ namespace MediaBrowser.Api.Playback
if (!string.IsNullOrEmpty(state.VideoRequest.Profile))
{
if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
// not supported by h264_omx
param += " -profile:v " + state.VideoRequest.Profile;
@ -442,14 +460,18 @@ namespace MediaBrowser.Api.Playback
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
if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) ||
string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
// also needed for libx264 due to https://trac.ffmpeg.org/ticket/3307
if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) ||
string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) ||
string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
{
switch (state.VideoRequest.Level)
switch (level)
{
case "30":
param += " -level 3";
param += " -level 3.0";
break;
case "31":
param += " -level 3.1";
@ -458,7 +480,7 @@ namespace MediaBrowser.Api.Playback
param += " -level 3.2";
break;
case "40":
param += " -level 4";
param += " -level 4.0";
break;
case "41":
param += " -level 4.1";
@ -467,7 +489,7 @@ namespace MediaBrowser.Api.Playback
param += " -level 4.2";
break;
case "50":
param += " -level 5";
param += " -level 5.0";
break;
case "51":
param += " -level 5.1";
@ -476,20 +498,20 @@ namespace MediaBrowser.Api.Playback
param += " -level 5.2";
break;
default:
param += " -level " + state.VideoRequest.Level;
param += " -level " + level;
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) &&
!string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
param = "-pix_fmt yuv420p " + param;
}
@ -497,6 +519,25 @@ namespace MediaBrowser.Api.Playback
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)
{
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)
{
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;
@ -985,7 +1039,15 @@ namespace MediaBrowser.Api.Playback
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
if (GetVideoEncoder(state).IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1)
{
arg = "-hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device " + encodingOptions.VaapiDevice + " " + arg;
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
var hwOutputFormat = "vaapi";
if (hasGraphicalSubs)
{
hwOutputFormat = "yuv420p";
}
arg = "-hwaccel vaapi -hwaccel_output_format " + hwOutputFormat + " -vaapi_device " + encodingOptions.VaapiDevice + " " + arg;
}
}
@ -1160,17 +1222,21 @@ namespace MediaBrowser.Api.Playback
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);
if (state.ReadInputAtNativeFramerate)
if (state.ReadInputAtNativeFramerate && !transcodingJob.HasExited)
{
await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
}
}
StartThrottler(state, transcodingJob);
if (!transcodingJob.HasExited)
{
StartThrottler(state, transcodingJob);
}
ReportUsage(state);
return transcodingJob;
@ -1178,14 +1244,14 @@ namespace MediaBrowser.Api.Playback
private void StartThrottler(StreamState state, TranscodingJob transcodingJob)
{
if (EnableThrottling(state) && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
if (EnableThrottling(state))
{
transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager);
state.TranscodingThrottler.Start();
}
}
protected virtual bool EnableThrottling(StreamState state)
private bool EnableThrottling(StreamState state)
{
// do not use throttling with hardware encoders
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
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);
}
@ -1770,6 +1837,15 @@ namespace MediaBrowser.Api.Playback
// 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))
{
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);
var archivable = item as IArchivable;
state.IsInputArchive = archivable != null && archivable.IsArchive;
MediaSourceInfo mediaSource;
MediaSourceInfo mediaSource = null;
if (string.IsNullOrWhiteSpace(request.LiveStreamId))
{
var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList();
TranscodingJob currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ?
ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId)
: null;
mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
? mediaSources.First()
: mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId));
if (currentJob != null)
{
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
{
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;
@ -2012,7 +2099,7 @@ namespace MediaBrowser.Api.Playback
}
// 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;
}
@ -2243,7 +2330,8 @@ namespace MediaBrowser.Api.Playback
state.TargetRefFrames,
state.TargetVideoStreamCount,
state.TargetAudioStreamCount,
state.TargetVideoCodecTag);
state.TargetVideoCodecTag,
state.IsTargetAVC);
if (mediaProfile != null)
{
@ -2378,7 +2466,8 @@ namespace MediaBrowser.Api.Playback
Url = "https://mb3admin.com/admin/service/transcoding/report",
CancellationToken = CancellationToken.None,
LogRequest = false,
LogErrors = false
LogErrors = false,
BufferContent = false
};
options.RequestContent = JsonSerializer.SerializeToString(dict);
options.RequestContentType = "application/json";
@ -2461,7 +2550,8 @@ namespace MediaBrowser.Api.Playback
state.TargetRefFrames,
state.TargetVideoStreamCount,
state.TargetAudioStreamCount,
state.TargetVideoCodecTag
state.TargetVideoCodecTag,
state.IsTargetAVC
).FirstOrDefault() ?? string.Empty;
}
@ -2516,6 +2606,7 @@ namespace MediaBrowser.Api.Playback
inputModifier += " " + GetFastSeekCommandLineParameter(state.Request);
inputModifier = inputModifier.Trim();
//inputModifier += " -fflags +genpts+ignidx+igndts";
if (state.VideoRequest != null && genPts)
{
inputModifier += " -fflags +genpts";

@ -87,7 +87,8 @@ namespace MediaBrowser.Api.Playback.Hls
if (!FileSystem.FileExists(playlist))
{
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(playlist);
await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
try
{
if (!FileSystem.FileExists(playlist))
@ -104,13 +105,13 @@ namespace MediaBrowser.Api.Playback.Hls
throw;
}
var waitForSegments = state.SegmentLength >= 10 ? 2 : 3;
var waitForSegments = state.SegmentLength >= 10 ? 2 : (state.SegmentLength > 3 || !isLive ? 3 : 3);
await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false);
}
}
finally
{
ApiEntryPoint.Instance.TranscodingStartLock.Release();
transcodingLock.Release();
}
}
@ -128,10 +129,9 @@ namespace MediaBrowser.Api.Playback.Hls
var audioBitrate = state.OutputAudioBitrate ?? 0;
var videoBitrate = state.OutputVideoBitrate ?? 0;
var appendBaselineStream = false;
var baselineStreamBitrate = 64000;
var playlistText = GetMasterPlaylistFileText(playlist, videoBitrate + audioBitrate, appendBaselineStream, baselineStreamBitrate);
var playlistText = GetMasterPlaylistFileText(playlist, videoBitrate + audioBitrate, baselineStreamBitrate);
job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType);
@ -151,9 +151,10 @@ namespace MediaBrowser.Api.Playback.Hls
{
var text = reader.ReadToEnd();
text = text.Replace("#EXTM3U", "#EXTM3U\n#EXT-X-PLAYLIST-TYPE:EVENT");
var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(UsCulture);
// ffmpeg pads the reported length by a full second
text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase);
return text;
@ -161,7 +162,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
}
private string GetMasterPlaylistFileText(string firstPlaylist, int bitrate, bool includeBaselineStream, int baselineStreamBitrate)
private string GetMasterPlaylistFileText(string firstPlaylist, int bitrate, int baselineStreamBitrate)
{
var builder = new StringBuilder();
@ -175,14 +176,6 @@ namespace MediaBrowser.Api.Playback.Hls
var playlistUrl = "hls/" + Path.GetFileName(firstPlaylist).Replace(".m3u8", "/stream.m3u8");
builder.AppendLine(playlistUrl);
// Low bitrate stream
if (includeBaselineStream)
{
builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + baselineStreamBitrate.ToString(UsCulture));
playlistUrl = "hls/" + Path.GetFileName(firstPlaylist).Replace(".m3u8", "-low/stream.m3u8");
builder.AppendLine(playlistUrl);
}
return builder.ToString();
}
@ -190,32 +183,41 @@ namespace MediaBrowser.Api.Playback.Hls
{
Logger.Debug("Waiting for {0} segments in {1}", segmentCount, playlist);
while (true)
while (!cancellationToken.IsCancellationRequested)
{
// Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
using (var fileStream = GetPlaylistFileStream(playlist))
try
{
using (var reader = new StreamReader(fileStream))
// Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
using (var fileStream = GetPlaylistFileStream(playlist))
{
var count = 0;
while (!reader.EndOfStream)
using (var reader = new StreamReader(fileStream))
{
var line = await reader.ReadLineAsync().ConfigureAwait(false);
var count = 0;
if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
while (!reader.EndOfStream)
{
count++;
if (count >= segmentCount)
var line = await reader.ReadLineAsync().ConfigureAwait(false);
if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
{
Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
return;
count++;
if (count >= segmentCount)
{
Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
return;
}
}
}
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
}
catch (IOException)
{
// May get an error if the file is locked
}
await Task.Delay(50, cancellationToken).ConfigureAwait(false);
}
}

@ -171,14 +171,15 @@ namespace MediaBrowser.Api.Playback.Hls
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(playlistPath);
await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
var released = false;
try
{
if (FileSystem.FileExists(segmentPath))
{
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
ApiEntryPoint.Instance.TranscodingStartLock.Release();
transcodingLock.Release();
released = true;
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
@ -242,7 +243,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
if (!released)
{
ApiEntryPoint.Instance.TranscodingStartLock.Release();
transcodingLock.Release();
}
}
@ -906,6 +907,7 @@ namespace MediaBrowser.Api.Playback.Hls
).Trim();
}
// TODO: check libavformat version for 57 50.100 and use -hls_flags split_by_time
return string.Format("{0}{11} {1} -map_metadata -1 -threads {2} {3} {4}{5} {6} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"",
inputModifier,
GetInputArgument(state),
@ -922,11 +924,6 @@ namespace MediaBrowser.Api.Playback.Hls
).Trim();
}
protected override bool EnableThrottling(StreamState state)
{
return true;
}
/// <summary>
/// Gets the segment file extension.
/// </summary>

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

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

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

@ -17,6 +17,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
using ServiceStack;
namespace MediaBrowser.Api.Playback.Progressive
{
@ -26,12 +27,10 @@ namespace MediaBrowser.Api.Playback.Progressive
public abstract class BaseProgressiveStreamingService : BaseStreamingService
{
protected readonly IImageProcessor ImageProcessor;
protected readonly IHttpClient HttpClient;
protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer)
{
ImageProcessor = imageProcessor;
HttpClient = httpClient;
}
/// <summary>
@ -122,6 +121,25 @@ namespace MediaBrowser.Api.Playback.Progressive
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
if (request.Static && state.InputProtocol == MediaProtocol.Http)
{
@ -129,8 +147,7 @@ namespace MediaBrowser.Api.Playback.Progressive
using (state)
{
return await GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource)
.ConfigureAwait(false);
return await GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).ConfigureAwait(false);
}
}
@ -154,6 +171,19 @@ namespace MediaBrowser.Api.Playback.Progressive
using (state)
{
if (state.MediaSource.IsInfiniteStream)
{
var outputHeaders = new Dictionary<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;
if (!string.IsNullOrEmpty(request.Tag))
@ -345,7 +375,8 @@ namespace MediaBrowser.Api.Playback.Progressive
return streamResult;
}
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(outputPath);
await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
try
{
TranscodingJob job;
@ -376,7 +407,7 @@ namespace MediaBrowser.Api.Playback.Progressive
}
finally
{
ApiEntryPoint.Instance.TranscodingStartLock.Release();
transcodingLock.Release();
}
}

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

@ -37,6 +37,7 @@ namespace MediaBrowser.Api.Playback
/// </summary>
/// <value>The log file stream.</value>
public Stream LogFileStream { get; set; }
public IDirectStreamProvider DirectStreamProvider { get; set; }
public string InputContainer { get; set; }
@ -62,7 +63,6 @@ namespace MediaBrowser.Api.Playback
get { return Request is VideoStreamRequest; }
}
public bool IsInputVideo { get; set; }
public bool IsInputArchive { get; set; }
public VideoType VideoType { get; set; }
public IsoType? IsoType { get; set; }
@ -88,9 +88,17 @@ namespace MediaBrowser.Api.Playback
return 10;
}
if (!RunTimeTicks.HasValue)
{
return 3;
}
return 6;
}
if (!RunTimeTicks.HasValue)
{
return 3;
}
return 3;
}
}
@ -112,6 +120,7 @@ namespace MediaBrowser.Api.Playback
public string OutputVideoSync = "-1";
public List<string> SupportedAudioCodecs { get; set; }
public List<string> SupportedVideoCodecs { get; set; }
public string UserAgent { get; set; }
public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger)
@ -119,6 +128,7 @@ namespace MediaBrowser.Api.Playback
_mediaSourceManager = mediaSourceManager;
_logger = logger;
SupportedAudioCodecs = new List<string>();
SupportedVideoCodecs = new List<string>();
PlayableStreamFileNames = new List<string>();
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
@ -215,7 +225,7 @@ namespace MediaBrowser.Api.Playback
{
try
{
await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).ConfigureAwait(false);
}
catch (Exception ex)
{
@ -506,5 +516,18 @@ namespace MediaBrowser.Api.Playback
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;
break;
case HeaderMetadata.Countries:
option.Column = (i, r) => this.GetListAsString(this.GetObject<IHasProductionLocations, List<string>>(i, (x) => x.ProductionLocations));
break;
case HeaderMetadata.Disc:
option.Column = (i, r) => i.ParentIndexNumber;
break;

@ -36,7 +36,6 @@ namespace MediaBrowser.Api.Reports
result = this.GetResultStudios(result, items, topItem);
result = this.GetResultPersons(result, items, topItem);
result = this.GetResultProductionYears(result, items, topItem);
result = this.GetResulProductionLocations(result, items, topItem);
result = this.GetResultCommunityRatings(result, items, topItem);
result = this.GetResultParentalRatings(result, items, topItem);
@ -100,30 +99,6 @@ namespace MediaBrowser.Api.Reports
}
}
/// <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>
/// <param name="result"> The result. </param>
/// <param name="items"> The items. </param>

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

@ -88,8 +88,6 @@ namespace MediaBrowser.Api
var result = new StartupConfiguration
{
UICulture = _config.Configuration.UICulture,
EnableInternetProviders = _config.Configuration.EnableInternetProviders,
SaveLocalMeta = _config.Configuration.SaveLocalMeta,
MetadataCountryCode = _config.Configuration.MetadataCountryCode,
PreferredMetadataLanguage = _config.Configuration.PreferredMetadataLanguage
};
@ -116,15 +114,16 @@ namespace MediaBrowser.Api
config.EnableLocalizedGuids = true;
config.EnableStandaloneMusicKeys = true;
config.EnableCaseSensitiveItemIds = true;
//config.EnableFolderView = true;
config.EnableFolderView = true;
config.SchemaVersion = 109;
config.EnableSimpleArtistDetection = true;
config.SkipDeserializationForBasicTypes = true;
config.SkipDeserializationForPrograms = true;
}
public void Post(UpdateStartupConfiguration request)
{
_config.Configuration.UICulture = request.UICulture;
_config.Configuration.EnableInternetProviders = request.EnableInternetProviders;
_config.Configuration.SaveLocalMeta = request.SaveLocalMeta;
_config.Configuration.MetadataCountryCode = request.MetadataCountryCode;
_config.Configuration.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
_config.SaveConfiguration();
@ -148,12 +147,6 @@ namespace MediaBrowser.Api
{
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;
await _userManager.UpdateUser(user).ConfigureAwait(false);
@ -221,8 +214,6 @@ namespace MediaBrowser.Api
public class StartupConfiguration
{
public string UICulture { get; set; }
public bool EnableInternetProviders { get; set; }
public bool SaveLocalMeta { get; set; }
public string MetadataCountryCode { get; set; }
public string PreferredMetadataLanguage { get; set; }
public string LiveTvTunerType { get; set; }

@ -148,7 +148,7 @@ namespace MediaBrowser.Api.Subtitles
{
var item = (Video)_libraryManager.GetItemById(new Guid(request.Id));
var mediaSource = await _mediaSourceManager.GetMediaSource(item, request.MediaSourceId, false).ConfigureAwait(false);
var mediaSource = await _mediaSourceManager.GetMediaSource(item, request.MediaSourceId, null, false, CancellationToken.None).ConfigureAwait(false);
var builder = new StringBuilder();

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

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

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

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

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

@ -1,5 +1,5 @@
<?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')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -14,6 +14,7 @@
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -23,7 +24,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
@ -51,11 +52,15 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
</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">
<HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<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>
</Reference>
<Reference Include="Patterns.Logging">
@ -65,8 +70,8 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>
</Reference>
<Reference Include="SimpleInjector, Version=3.2.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<HintPath>..\packages\SimpleInjector.3.2.0\lib\net45\SimpleInjector.dll</HintPath>
<Reference Include="SimpleInjector, Version=3.2.2.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<HintPath>..\packages\SimpleInjector.3.2.2\lib\net45\SimpleInjector.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
@ -75,6 +80,9 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net" />
<Reference Include="System.Text.Json">
<HintPath>..\ThirdParty\fastjsonparser\System.Text.Json.dll</HintPath>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="ServiceStack.Text">
<HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
@ -93,6 +101,7 @@
<Compile Include="HttpClientManager\HttpClientInfo.cs" />
<Compile Include="HttpClientManager\HttpClientManager.cs" />
<Compile Include="IO\IsoManager.cs" />
<Compile Include="IO\MemoryStreamProvider.cs" />
<Compile Include="Logging\LogHelper.cs" />
<Compile Include="Logging\NLogger.cs" />
<Compile Include="Logging\NlogManager.cs" />

@ -334,7 +334,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
trigger.Stop();
TaskManager.QueueScheduledTask(ScheduledTask);
TaskManager.QueueScheduledTask(ScheduledTask, e.Argument);
await Task.Delay(1000).ConfigureAwait(false);
@ -390,13 +390,13 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
try
{
var localTask = ScheduledTask.Execute(CurrentCancellationTokenSource.Token, progress);
if (options != null && options.MaxRuntimeMs.HasValue)
{
CurrentCancellationTokenSource.CancelAfter(options.MaxRuntimeMs.Value);
}
var localTask = ScheduledTask.Execute(CurrentCancellationTokenSource.Token, progress);
await localTask.ConfigureAwait(false);
status = TaskCompletionStatus.Completed;
@ -423,10 +423,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
CurrentProgress = null;
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>

@ -142,9 +142,15 @@ namespace MediaBrowser.Common.Implementations.Security
}
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();
// re-load registration info
@ -163,7 +169,8 @@ namespace MediaBrowser.Common.Implementations.Security
var options = new HttpRequestOptions()
{
Url = AppstoreRegUrl,
CancellationToken = CancellationToken.None
CancellationToken = CancellationToken.None,
BufferContent = false
};
options.RequestHeaders.Add("X-Emby-Token", _appHost.SystemId);
options.RequestContent = parameters;
@ -263,7 +270,8 @@ namespace MediaBrowser.Common.Implementations.Security
Url = MBValidateUrl,
// Seeing block length errors
EnableHttpCompression = false
EnableHttpCompression = false,
BufferContent = false
};
options.SetPostData(data);

@ -14,16 +14,14 @@ namespace MediaBrowser.Common.Implementations.Updates
{
private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _jsonSerializer;
private TimeSpan _cacheLength;
public GithubUpdater(IHttpClient httpClient, IJsonSerializer jsonSerializer, TimeSpan cacheLength)
public GithubUpdater(IHttpClient httpClient, IJsonSerializer jsonSerializer)
{
_httpClient = httpClient;
_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);
@ -32,13 +30,14 @@ namespace MediaBrowser.Common.Implementations.Updates
Url = url,
EnableKeepAlive = false,
CancellationToken = cancellationToken,
UserAgent = "Emby/3.0"
UserAgent = "Emby/3.0",
BufferContent = false
};
if (_cacheLength.Ticks > 0)
if (cacheLength.Ticks > 0)
{
options.CacheMode = CacheMode.Unconditional;
options.CacheLength = _cacheLength;
options.CacheLength = cacheLength;
}
using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
@ -107,15 +106,10 @@ namespace MediaBrowser.Common.Implementations.Updates
Url = url,
EnableKeepAlive = false,
CancellationToken = cancellationToken,
UserAgent = "Emby/3.0"
UserAgent = "Emby/3.0",
BufferContent = false
};
if (_cacheLength.Ticks > 0)
{
options.CacheMode = CacheMode.Unconditional;
options.CacheLength = _cacheLength;
}
using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
{
var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);

@ -148,14 +148,10 @@ namespace MediaBrowser.Common.Implementations.Updates
/// <summary>
/// Gets all available packages.
/// </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>
public async Task<IEnumerable<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
bool withRegistration = true,
PackageType? packageType = null,
string packageType = null,
Version applicationVersion = null)
{
var data = new Dictionary<string, string>
@ -293,7 +289,7 @@ namespace MediaBrowser.Common.Implementations.Updates
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)
{
@ -301,9 +297,9 @@ namespace MediaBrowser.Common.Implementations.Updates
.OrderByDescending(GetPackageVersion).ToList();
}
if (packageType.HasValue)
if (!string.IsNullOrWhiteSpace(packageType))
{
packages = packages.Where(p => p.type == packageType.Value).ToList();
packages = packages.Where(p => string.Equals(p.type, packageType, StringComparison.OrdinalIgnoreCase)).ToList();
}
// If an app version was supplied, filter the versions for each package to only include supported versions

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<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="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="SimpleInjector" version="3.2.0" targetFramework="net45" />
<package id="SimpleInjector" version="3.2.2" targetFramework="net45" />
</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\ResourceNotFoundException.cs" />
<Compile Include="IDependencyContainer.cs" />
<Compile Include="IO\IMemoryStreamProvider.cs" />
<Compile Include="IO\ProgressStream.cs" />
<Compile Include="IO\StreamDefaults.cs" />
<Compile Include="Configuration\IApplicationPaths.cs" />

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

@ -31,6 +31,8 @@ namespace MediaBrowser.Controller.Channels
/// <returns>ChannelFeatures.</returns>
ChannelFeatures GetChannelFeatures(string id);
bool SupportsSync(string channelId);
/// <summary>
/// Gets all channel features.
/// </summary>
@ -132,15 +134,5 @@ namespace MediaBrowser.Controller.Channels
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>BaseItemDto.</returns>
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 MediaBrowser.Model.Chapters;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Configuration;
@ -13,12 +11,6 @@ namespace MediaBrowser.Controller.Chapters
/// </summary>
public interface IChapterManager
{
/// <summary>
/// Adds the parts.
/// </summary>
/// <param name="chapterProviders">The chapter providers.</param>
void AddParts(IEnumerable<IChapterProvider> chapterProviders);
/// <summary>
/// Gets the chapters.
/// </summary>
@ -35,43 +27,6 @@ namespace MediaBrowser.Controller.Chapters
/// <returns>Task.</returns>
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>
/// Gets the configuration.
/// </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;
namespace MediaBrowser.Server.Implementations.Collections
namespace MediaBrowser.Controller.Collections
{
public class ManualCollectionsFolder : BasePluginFolder, IHiddenFromDisplay
{

@ -1,10 +1,20 @@
using System;
using System.Collections.Generic;
using System.Net;
using MediaBrowser.Model.Events;
namespace MediaBrowser.Controller.Dlna
{
public interface IDeviceDiscovery
{
event EventHandler<SsdpMessageEventArgs> DeviceDiscovered;
event EventHandler<SsdpMessageEventArgs> DeviceLeft;
event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered;
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
{
event EventHandler<SsdpMessageEventArgs> MessageReceived;
}
}

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

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

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

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

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

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

@ -8,11 +8,8 @@ using System.Runtime.Serialization;
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()
{
MultiPartGameFiles = new List<string>();
@ -39,6 +36,12 @@ namespace MediaBrowser.Controller.Entities
get { return true; }
}
[IgnoreDataMember]
public override bool SupportsThemeMedia
{
get { return true; }
}
/// <summary>
/// Gets or sets the remote trailers.
/// </summary>
@ -98,7 +101,7 @@ namespace MediaBrowser.Controller.Entities
public override IEnumerable<string> GetDeletePaths()
{
if (!IsInMixedFolder)
if (!DetectIsInMixedFolder())
{
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>
bool SupportsLocalMetadata { get; }
/// <summary>
/// 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; }
bool DetectIsInMixedFolder();
/// <summary>
/// Gets a value indicating whether this instance is locked.

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
@ -62,5 +63,7 @@ namespace MediaBrowser.Controller.Entities
int? GetInheritedParentalRatingValue();
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.Collections.Generic;
using MediaBrowser.Model.Configuration;
using System.Linq;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Model.Querying;
namespace MediaBrowser.Controller.Entities
{
@ -101,6 +104,8 @@ namespace MediaBrowser.Controller.Entities
public bool? IsMovie { get; set; }
public bool? IsSports { get; set; }
public bool? IsKids { get; set; }
public bool? IsNews { get; set; }
public bool? IsSeries { get; set; }
public int? MinPlayers { get; set; }
public int? MaxPlayers { get; set; }
@ -137,6 +142,7 @@ namespace MediaBrowser.Controller.Entities
public DayOfWeek[] AirDays { get; set; }
public SeriesStatus[] SeriesStatuses { get; set; }
public string AlbumArtistStartsWithOrGreater { get; set; }
public string ExternalSeriesId { get; set; }
public string[] AlbumNames { get; set; }
public string[] ArtistNames { get; set; }
@ -149,11 +155,51 @@ namespace MediaBrowser.Controller.Entities
public Dictionary<string, string> ExcludeProviderIds { 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()
{
GroupByPresentationUniqueKey = true;
EnableTotalRecordCount = true;
DtoOptions = new DtoOptions();
AlbumNames = new string[] { };
ArtistNames = new string[] { };
ExcludeArtistIds = new string[] { };
@ -191,6 +237,7 @@ namespace MediaBrowser.Controller.Entities
TrailerTypes = new TrailerType[] { };
AirDays = new DayOfWeek[] { };
SeriesStatuses = new SeriesStatus[] { };
OrderBy = new List<Tuple<string, SortOrder>>();
}
public InternalItemsQuery(User user)

@ -15,24 +15,17 @@ namespace MediaBrowser.Controller.Entities.Movies
/// <summary>
/// Class Movie
/// </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> ThemeSongIds { get; set; }
public List<Guid> ThemeVideoIds { get; set; }
public List<string> ProductionLocations { get; set; }
public Movie()
{
SpecialFeatureIds = new List<Guid>();
RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>();
RemoteTrailerIds = new List<Guid>();
ThemeSongIds = new List<Guid>();
ThemeVideoIds = new List<Guid>();
Taglines = new List<string>();
ProductionLocations = new List<string>();
}
public string AwardSummary { get; set; }
@ -81,7 +74,7 @@ namespace MediaBrowser.Controller.Entities.Movies
// Must have a parent to have special features
// 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);
@ -119,7 +112,7 @@ namespace MediaBrowser.Controller.Entities.Movies
{
var info = GetItemLookupInfo<MovieInfo>();
if (!IsInMixedFolder)
if (!DetectIsInMixedFolder())
{
info.Name = System.IO.Path.GetFileName(ContainingFolderPath);
}
@ -145,7 +138,7 @@ namespace MediaBrowser.Controller.Entities.Movies
else
{
// Try to get the year from the folder name
if (!IsInMixedFolder)
if (!DetectIsInMixedFolder())
{
info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));

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

@ -1,19 +1,11 @@
using MediaBrowser.Model.Drawing;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
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]
public override bool SupportsLocalMetadata
{

@ -85,9 +85,7 @@ namespace MediaBrowser.Controller.Entities.TV
public override int GetChildCount(User user)
{
Logger.Debug("Season {0} getting child cound", (Path ?? Name));
var result = GetChildren(user, true).Count();
Logger.Debug("Season {0} child cound: ", result);
return result;
}
@ -156,15 +154,10 @@ namespace MediaBrowser.Controller.Entities.TV
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);
Logger.Debug("Season.GetItemsInternal entering PostFilterAndSort. Request id: " + id);
var result = PostFilterAndSort(items, query, false, false);
Logger.Debug("Season.GetItemsInternal complete. Request id: " + id);
return Task.FromResult(result);
}
@ -185,34 +178,12 @@ namespace MediaBrowser.Controller.Entities.TV
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()
{
var episodes = GetRecursiveChildren().OfType<Episode>();
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;
return Series.GetSeasonEpisodes(this, null, null);
}
public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)

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

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

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

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

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

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

@ -506,6 +506,8 @@ namespace MediaBrowser.Controller.Library
/// <returns>QueryResult&lt;BaseItem&gt;.</returns>
QueryResult<BaseItem> QueryItems(InternalItemsQuery query);
string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem = null);
/// <summary>
/// Substitutes the path.
/// </summary>
@ -554,9 +556,10 @@ namespace MediaBrowser.Controller.Library
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
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 AddMediaPath(string virtualFolderName, string path);
void AddMediaPath(string virtualFolderName, MediaPathInfo path);
void UpdateMediaPath(string virtualFolderName, MediaPathInfo path);
void RemoveMediaPath(string virtualFolderName, string path);
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>> GetAlbumArtists(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.Threading;
using System.Threading.Tasks;
using System.IO;
namespace MediaBrowser.Controller.Library
{
@ -60,11 +61,8 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Gets the static media source.
/// </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>
Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution);
Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, string liveStreamId, bool enablePathSubstitution, CancellationToken cancellationToken);
/// <summary>
/// Opens the media source.
@ -82,6 +80,8 @@ namespace MediaBrowser.Controller.Library
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
Task<MediaSourceInfo> GetLiveStream(string id, CancellationToken cancellationToken);
Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken);
/// <summary>
/// Pings the media source.
@ -95,8 +95,12 @@ namespace MediaBrowser.Controller.Library
/// Closes the media source.
/// </summary>
/// <param name="id">The live stream identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <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.Threading;
using System.Threading.Tasks;
using System;
namespace MediaBrowser.Controller.Library
{
@ -22,14 +23,13 @@ namespace MediaBrowser.Controller.Library
/// <param name="openToken">The open token.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
Task<MediaSourceInfo> OpenMediaSource(string openToken, CancellationToken cancellationToken);
Task<Tuple<MediaSourceInfo,IDirectStreamProvider>> OpenMediaSource(string openToken, CancellationToken cancellationToken);
/// <summary>
/// Closes the media source.
/// </summary>
/// <param name="liveStreamId">The live stream identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <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)
{
@ -63,7 +63,7 @@ namespace MediaBrowser.Controller.Library
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)

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

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

@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
namespace MediaBrowser.Controller.LiveTv
{
@ -245,4 +246,14 @@ namespace MediaBrowser.Controller.LiveTv
/// <returns>Task.</returns>
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>
/// Gets the channels.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;IEnumerable&lt;ChannelInfo&gt;&gt;.</returns>
Task<IEnumerable<ChannelInfo>> GetChannels(CancellationToken cancellationToken);
Task<IEnumerable<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken);
/// <summary>
/// Gets the tuner infos.
/// </summary>
@ -38,7 +37,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="streamId">The stream identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <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>
/// Gets the channel stream media sources.
/// </summary>
@ -46,8 +45,6 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns>
Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
string ApplyDuration(string streamPath, TimeSpan duration);
}
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]
public bool IsSeries { get; set; }
public string SeriesTimerId { get; set; }
public string TimerId { get; set; }
[IgnoreDataMember]
public DateTime StartDate { get; set; }
public RecordingStatus Status { get; set; }
@ -112,7 +113,7 @@ namespace MediaBrowser.Controller.LiveTv
public override bool CanDelete()
{
return true;
return Status == RecordingStatus.Completed;
}
public override bool IsAuthorizedToDelete(User user)

@ -9,7 +9,7 @@ using System.Runtime.Serialization;
namespace MediaBrowser.Controller.LiveTv
{
public class LiveTvChannel : BaseItem, IHasMediaSources
public class LiveTvChannel : BaseItem, IHasMediaSources, IHasProgramAttributes
{
public override List<string> GetUserDataKeys()
{
@ -137,5 +137,56 @@ namespace MediaBrowser.Controller.LiveTv
{
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]
public bool IsSeries { get; set; }
public string SeriesTimerId { get; set; }
public string TimerId { get; set; }
[IgnoreDataMember]
public DateTime StartDate { get; set; }
public RecordingStatus Status { get; set; }
@ -111,7 +112,7 @@ namespace MediaBrowser.Controller.LiveTv
public override bool CanDelete()
{
return true;
return Status == RecordingStatus.Completed;
}
public override bool IsAuthorizedToDelete(User user)

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

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

@ -1,10 +1,17 @@
using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.LiveTv
{
public class TimerInfo
{
public TimerInfo()
{
Genres = new List<string>();
KeepUntil = KeepUntil.UntilDeleted;
}
/// <summary>
/// Id of the recording.
/// </summary>
@ -15,7 +22,7 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <value>The series timer identifier.</value>
public string SeriesTimerId { get; set; }
/// <summary>
/// ChannelId of the recording.
/// </summary>
@ -26,7 +33,7 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <value>The program identifier.</value>
public string ProgramId { get; set; }
/// <summary>
/// Name of the recording.
/// </summary>
@ -76,11 +83,36 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value>
public bool IsPostPaddingRequired { get; set; }
/// <summary>
/// Gets or sets the priority.
/// </summary>
/// <value>The priority.</value>
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\IRequiresMediaInfoCallback.cs" />
<Compile Include="Channels\ISearchableChannel.cs" />
<Compile Include="Chapters\ChapterSearchRequest.cs" />
<Compile Include="Chapters\IChapterManager.cs" />
<Compile Include="Chapters\IChapterProvider.cs" />
<Compile Include="Chapters\ChapterResponse.cs" />
<Compile Include="Collections\CollectionCreationOptions.cs" />
<Compile Include="Collections\CollectionEvents.cs" />
<Compile Include="Collections\ICollectionManager.cs" />
<Compile Include="Collections\ManualCollectionsFolder.cs" />
<Compile Include="Connect\ConnectSupporterSummary.cs" />
<Compile Include="Connect\IConnectManager.cs" />
<Compile Include="Connect\UserLinkResult.cs" />
@ -134,7 +132,6 @@
<Compile Include="Entities\Game.cs" />
<Compile Include="Entities\GameGenre.cs" />
<Compile Include="Entities\GameSystem.cs" />
<Compile Include="Entities\IArchivable.cs" />
<Compile Include="Entities\IByReferenceItem.cs" />
<Compile Include="Entities\IHasAspectRatio.cs" />
<Compile Include="Entities\IHasBudget.cs" />
@ -146,15 +143,12 @@
<Compile Include="Entities\IHasMediaSources.cs" />
<Compile Include="Entities\IHasMetascore.cs" />
<Compile Include="Entities\IHasOriginalTitle.cs" />
<Compile Include="Entities\IHasProductionLocations.cs" />
<Compile Include="Entities\IHasProgramAttributes.cs" />
<Compile Include="Entities\IHasScreenshots.cs" />
<Compile Include="Entities\IHasSeries.cs" />
<Compile Include="Entities\IHasShortOverview.cs" />
<Compile Include="Entities\IHasSpecialFeatures.cs" />
<Compile Include="Entities\IHasStartDate.cs" />
<Compile Include="Entities\IHasTaglines.cs" />
<Compile Include="Entities\IHasThemeMedia.cs" />
<Compile Include="Entities\IHasTrailers.cs" />
<Compile Include="Entities\IHasUserData.cs" />
<Compile Include="Entities\IHiddenFromDisplay.cs" />
@ -198,9 +192,9 @@
<Compile Include="Library\NameExtensions.cs" />
<Compile Include="Library\PlaybackStopEventArgs.cs" />
<Compile Include="Library\UserDataSaveEventArgs.cs" />
<Compile Include="LiveTv\IHasRegistrationInfo.cs" />
<Compile Include="LiveTv\IListingsProvider.cs" />
<Compile Include="LiveTv\ITunerHost.cs" />
<Compile Include="LiveTv\LiveStream.cs" />
<Compile Include="LiveTv\RecordingGroup.cs" />
<Compile Include="LiveTv\RecordingStatusChangedEventArgs.cs" />
<Compile Include="LiveTv\ILiveTvRecording.cs" />
@ -266,7 +260,6 @@
<Compile Include="Playlists\IPlaylistManager.cs" />
<Compile Include="Playlists\Playlist.cs" />
<Compile Include="Plugins\ILocalizablePlugin.cs" />
<Compile Include="Power\IPowerManagement.cs" />
<Compile Include="Providers\AlbumInfo.cs" />
<Compile Include="Providers\ArtistInfo.cs" />
<Compile Include="Providers\BookInfo.cs" />
@ -290,9 +283,7 @@
<Compile Include="Providers\IHasItemChangeMonitor.cs" />
<Compile Include="Providers\IHasLookupInfo.cs" />
<Compile Include="Providers\IHasOrder.cs" />
<Compile Include="Providers\IImageFileSaver.cs" />
<Compile Include="Providers\IImageProvider.cs" />
<Compile Include="Providers\IImageSaver.cs" />
<Compile Include="Providers\ILocalImageFileProvider.cs" />
<Compile Include="Providers\ILocalMetadataProvider.cs" />
<Compile Include="Providers\ImageRefreshMode.cs" />
@ -319,7 +310,6 @@
<Compile Include="Providers\SongInfo.cs" />
<Compile Include="Providers\TrailerInfo.cs" />
<Compile Include="Providers\VideoContentType.cs" />
<Compile Include="RelatedMedia\IRelatedMediaProvider.cs" />
<Compile Include="Security\AuthenticationInfo.cs" />
<Compile Include="Security\AuthenticationInfoQuery.cs" />
<Compile Include="Security\IAuthenticationRepository.cs" />

@ -45,9 +45,9 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <param name="offset">The offset.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <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>
/// Extracts the video images on interval.

@ -120,7 +120,7 @@ namespace MediaBrowser.Controller.Net
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 ?
new Timer(TimerCallback, message.Connection, Timeout.Infinite, Timeout.Infinite) :
@ -267,7 +267,7 @@ namespace MediaBrowser.Controller.Net
/// <param name="connection">The connection.</param>
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;

@ -11,14 +11,6 @@ namespace MediaBrowser.Controller.Net
/// </summary>
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>
/// Gets the result.
/// </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
/// </summary>
protected ILogger Logger { get; private set; }
protected IProviderManager ProviderManager { get; private set; }
private Dictionary<string, string> _validProviderIds;
/// <summary>
/// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class.
/// </summary>
/// <param name="logger">The logger.</param>
public BaseItemXmlParser(ILogger logger)
public BaseItemXmlParser(ILogger logger, IProviderManager providerManager)
{
Logger = logger;
ProviderManager = providerManager;
}
/// <summary>
@ -60,6 +64,22 @@ namespace MediaBrowser.Controller.Providers
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.UTF8, cancellationToken);
}
@ -327,7 +347,7 @@ namespace MediaBrowser.Controller.Providers
var person = item as Person;
if (person != null)
{
person.PlaceOfBirth = val;
person.ProductionLocations = new List<string> { val };
}
}
@ -657,14 +677,6 @@ namespace MediaBrowser.Controller.Providers
break;
}
case "TvDbId":
var tvdbId = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(tvdbId))
{
item.SetProviderId(MetadataProviders.Tvdb, tvdbId);
}
break;
case "VoteCount":
{
var val = reader.ReadElementContentAsString();
@ -679,95 +691,7 @@ namespace MediaBrowser.Controller.Providers
}
break;
}
case "MusicBrainzAlbumId":
{
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":
case "CollectionNumber":
var tmdbCollection = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(tmdbCollection))
{
@ -775,30 +699,6 @@ namespace MediaBrowser.Controller.Providers
}
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":
{
using (var subtree = reader.ReadSubtree())
@ -890,8 +790,25 @@ namespace MediaBrowser.Controller.Providers
}
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))
{
var hasProductionLocations = item as IHasProductionLocations;
if (hasProductionLocations != null)
{
if (!string.IsNullOrWhiteSpace(val))
{
hasProductionLocations.AddProductionLocation(val);
}
}
}
break;
}
@ -1017,14 +926,7 @@ namespace MediaBrowser.Controller.Providers
if (!string.IsNullOrWhiteSpace(val))
{
var hasTaglines = item as IHasTaglines;
if (hasTaglines != null)
{
if (!string.IsNullOrWhiteSpace(val))
{
hasTaglines.AddTagline(val);
}
}
item.Tagline = val;
}
break;
}

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

Loading…
Cancel
Save