Merge pull request #2224 from MediaBrowser/beta

Beta
pull/1154/head
Luke 8 years ago committed by GitHub
commit 25ef9777ca

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

@ -243,11 +243,7 @@ namespace MediaBrowser.Api
hasBudget.Revenue = request.Revenue;
}
var hasOriginalTitle = item as IHasOriginalTitle;
if (hasOriginalTitle != null)
{
hasOriginalTitle.OriginalTitle = hasOriginalTitle.OriginalTitle;
}
item.OriginalTitle = string.IsNullOrWhiteSpace(request.OriginalTitle) ? null : request.OriginalTitle;
var hasCriticRating = item as IHasCriticRating;
if (hasCriticRating != null)
@ -278,10 +274,9 @@ namespace MediaBrowser.Api
item.Tags = request.Tags;
var hasTaglines = item as IHasTaglines;
if (hasTaglines != null)
if (request.Taglines != null)
{
hasTaglines.Taglines = request.Taglines;
item.Tagline = request.Taglines.FirstOrDefault();
}
var hasShortOverview = item as IHasShortOverview;
@ -308,8 +303,6 @@ namespace MediaBrowser.Api
item.OfficialRating = string.IsNullOrWhiteSpace(request.OfficialRating) ? null : request.OfficialRating;
item.CustomRating = request.CustomRating;
SetProductionLocations(item, request);
item.PreferredMetadataCountryCode = request.PreferredMetadataCountryCode;
item.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
@ -417,23 +410,5 @@ namespace MediaBrowser.Api
series.AirTime = request.AirTime;
}
}
private void SetProductionLocations(BaseItem item, BaseItemDto request)
{
var hasProductionLocations = item as IHasProductionLocations;
if (hasProductionLocations != null)
{
hasProductionLocations.ProductionLocations = request.ProductionLocations;
}
var person = item as Person;
if (person != null)
{
person.PlaceOfBirth = request.ProductionLocations == null
? null
: request.ProductionLocations.FirstOrDefault();
}
}
}
}

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

@ -835,14 +835,14 @@ namespace MediaBrowser.Api.Library
: (Folder)_libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id);
while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
while (item.ThemeSongIds.Count == 0 && request.InheritFromParent && item.GetParent() != null)
{
item = item.GetParent();
}
var dtoOptions = GetDtoOptions(request);
var dtos = GetThemeSongIds(item).Select(_libraryManager.GetItemById)
var dtos = item.ThemeSongIds.Select(_libraryManager.GetItemById)
.Where(i => i != null)
.OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
@ -879,14 +879,14 @@ namespace MediaBrowser.Api.Library
: (Folder)_libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id);
while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
while (item.ThemeVideoIds.Count == 0 && request.InheritFromParent && item.GetParent() != null)
{
item = item.GetParent();
}
var dtoOptions = GetDtoOptions(request);
var dtos = GetThemeVideoIds(item).Select(_libraryManager.GetItemById)
var dtos = item.ThemeVideoIds.Select(_libraryManager.GetItemById)
.Where(i => i != null)
.OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
@ -901,30 +901,6 @@ namespace MediaBrowser.Api.Library
};
}
private List<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;
@ -159,6 +199,7 @@ namespace MediaBrowser.Api.LiveTv
public bool? IsSeries { get; set; }
public bool? IsKids { get; set; }
public bool? IsSports { get; set; }
public bool? IsNews { get; set; }
public GetRecordings()
{
@ -275,6 +316,8 @@ namespace MediaBrowser.Api.LiveTv
public string SeriesTimerId { get; set; }
public bool? IsActive { get; set; }
public bool? IsScheduled { get; set; }
}
[Route("/LiveTv/Programs", "GET,POST", Summary = "Gets available live tv epgs..")]
@ -305,6 +348,12 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsMovie { get; set; }
[ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsSeries { get; set; }
[ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsNews { get; set; }
[ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsKids { get; set; }
@ -340,6 +389,8 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableUserData { get; set; }
public string SeriesTimerId { get; set; }
/// <summary>
/// Fields to return within the items, in addition to basic information
/// </summary>
@ -376,15 +427,21 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "HasAired", Description = "Optional. Filter by programs that have completed airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? HasAired { get; set; }
[ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsSports { get; set; }
[ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsSeries { get; set; }
[ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
[ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsMovie { get; set; }
[ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
[ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsNews { get; set; }
[ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsKids { get; set; }
[ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsSports { get; set; }
[ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableImages { get; set; }
@ -613,16 +670,30 @@ namespace MediaBrowser.Api.LiveTv
}
[Route("/LiveTv/LiveStreamFiles/{Id}/stream.{Container}", "GET", Summary = "Gets a live tv channel")]
public class GetLiveStreamFile
{
public string Id { get; set; }
public string Container { get; set; }
}
[Route("/LiveTv/LiveRecordings/{Id}/stream", "GET", Summary = "Gets a live tv channel")]
public class GetLiveRecordingFile
{
public string Id { get; set; }
}
public class LiveTvService : BaseApiService
{
private readonly ILiveTvManager _liveTvManager;
private readonly IUserManager _userManager;
private readonly IConfigurationManager _config;
private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
private readonly IFileSystem _fileSystem;
public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService)
public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem)
{
_liveTvManager = liveTvManager;
_userManager = userManager;
@ -630,6 +701,41 @@ namespace MediaBrowser.Api.LiveTv
_httpClient = httpClient;
_libraryManager = libraryManager;
_dtoService = dtoService;
_fileSystem = fileSystem;
}
public async Task<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)
@ -702,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);
@ -786,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);
@ -868,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);
@ -886,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
};
@ -919,6 +1038,7 @@ namespace MediaBrowser.Api.LiveTv
IsInProgress = request.IsInProgress,
EnableTotalRecordCount = request.EnableTotalRecordCount,
IsMovie = request.IsMovie,
IsNews = request.IsNews,
IsSeries = request.IsSeries,
IsKids = request.IsKids,
IsSports = request.IsSports
@ -975,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" />

@ -156,18 +156,19 @@ namespace MediaBrowser.Api.Movies
itemTypes.Add(typeof(LiveTvProgram).Name);
}
var dtoOptions = GetDtoOptions(request);
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
Limit = request.Limit,
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(),
@ -198,7 +199,8 @@ namespace MediaBrowser.Api.Movies
Limit = 7,
ParentId = parentIdGuid,
Recursive = true,
IsPlayed = true
IsPlayed = true,
DtoOptions = dtoOptions
};
var recentlyPlayedMovies = _libraryManager.GetItemList(query).ToList();
@ -221,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();
@ -302,7 +305,8 @@ namespace MediaBrowser.Api.Movies
PersonTypes = new[] { PersonType.Director },
IncludeItemTypes = itemTypes.ToArray(),
IsMovie = true,
EnableGroupByMetadataKey = true
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
}).DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
.Take(itemLimit)
@ -339,7 +343,8 @@ namespace MediaBrowser.Api.Movies
Limit = itemLimit + 2,
IncludeItemTypes = itemTypes.ToArray(),
IsMovie = true,
EnableGroupByMetadataKey = true
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
}).DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
.Take(itemLimit)
@ -375,7 +380,8 @@ namespace MediaBrowser.Api.Movies
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;

@ -367,6 +367,8 @@ namespace MediaBrowser.Api.Playback
{
param += " -crf 23";
}
param += " -tune zerolatency";
}
else if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase))
@ -461,13 +463,15 @@ namespace MediaBrowser.Api.Playback
var level = NormalizeTranscodingLevel(state.OutputVideoCodec, state.VideoRequest.Level);
// h264_qsv and h264_nvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format
// also needed for libx264 due to https://trac.ffmpeg.org/ticket/3307
if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) ||
string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) ||
string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
{
switch (level)
{
case "30":
param += " -level 3";
param += " -level 3.0";
break;
case "31":
param += " -level 3.1";
@ -476,7 +480,7 @@ namespace MediaBrowser.Api.Playback
param += " -level 3.2";
break;
case "40":
param += " -level 4";
param += " -level 4.0";
break;
case "41":
param += " -level 4.1";
@ -485,7 +489,7 @@ namespace MediaBrowser.Api.Playback
param += " -level 4.2";
break;
case "50":
param += " -level 5";
param += " -level 5.0";
break;
case "51":
param += " -level 5.1";
@ -767,7 +771,20 @@ namespace MediaBrowser.Api.Playback
if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
{
outputSizeParam = GetOutputSizeParam(state, outputVideoCodec).TrimEnd('"');
outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase));
if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase));
}
else
{
outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase));
}
}
if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) && outputSizeParam.Length == 0)
{
outputSizeParam = ",format=nv12|vaapi,hwupload";
}
var videoSizeParam = string.Empty;
@ -802,10 +819,10 @@ namespace MediaBrowser.Api.Playback
{
if (state.PlayableStreamFileNames.Count > 0)
{
return MediaEncoder.GetProbeSizeArgument(state.PlayableStreamFileNames.ToArray(), state.InputProtocol);
return MediaEncoder.GetProbeSizeAndAnalyzeDurationArgument(state.PlayableStreamFileNames.ToArray(), state.InputProtocol);
}
return MediaEncoder.GetProbeSizeArgument(new[] { state.MediaPath }, state.InputProtocol);
return MediaEncoder.GetProbeSizeAndAnalyzeDurationArgument(new[] { state.MediaPath }, state.InputProtocol);
}
/// <summary>
@ -1022,7 +1039,15 @@ namespace MediaBrowser.Api.Playback
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
if (GetVideoEncoder(state).IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1)
{
arg = "-hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device " + encodingOptions.VaapiDevice + " " + arg;
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
var hwOutputFormat = "vaapi";
if (hasGraphicalSubs)
{
hwOutputFormat = "yuv420p";
}
arg = "-hwaccel vaapi -hwaccel_output_format " + hwOutputFormat + " -vaapi_device " + encodingOptions.VaapiDevice + " " + arg;
}
}
@ -1219,7 +1244,7 @@ namespace MediaBrowser.Api.Playback
private void StartThrottler(StreamState state, TranscodingJob transcodingJob)
{
if (EnableThrottling(state) && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
if (EnableThrottling(state))
{
transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager);
state.TranscodingThrottler.Start();
@ -1832,26 +1857,37 @@ namespace MediaBrowser.Api.Playback
state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
var archivable = item as IArchivable;
state.IsInputArchive = archivable != null && archivable.IsArchive;
MediaSourceInfo mediaSource;
MediaSourceInfo mediaSource = null;
if (string.IsNullOrWhiteSpace(request.LiveStreamId))
{
var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList();
TranscodingJob currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ?
ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId)
: null;
mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
? mediaSources.First()
: mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId));
if (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;
@ -2294,7 +2330,8 @@ namespace MediaBrowser.Api.Playback
state.TargetRefFrames,
state.TargetVideoStreamCount,
state.TargetAudioStreamCount,
state.TargetVideoCodecTag);
state.TargetVideoCodecTag,
state.IsTargetAVC);
if (mediaProfile != null)
{
@ -2429,7 +2466,8 @@ namespace MediaBrowser.Api.Playback
Url = "https://mb3admin.com/admin/service/transcoding/report",
CancellationToken = CancellationToken.None,
LogRequest = false,
LogErrors = false
LogErrors = false,
BufferContent = false
};
options.RequestContent = JsonSerializer.SerializeToString(dict);
options.RequestContentType = "application/json";
@ -2512,7 +2550,8 @@ namespace MediaBrowser.Api.Playback
state.TargetRefFrames,
state.TargetVideoStreamCount,
state.TargetAudioStreamCount,
state.TargetVideoCodecTag
state.TargetVideoCodecTag,
state.IsTargetAVC
).FirstOrDefault() ?? string.Empty;
}
@ -2567,6 +2606,7 @@ namespace MediaBrowser.Api.Playback
inputModifier += " " + GetFastSeekCommandLineParameter(state.Request);
inputModifier = inputModifier.Trim();
//inputModifier += " -fflags +genpts+ignidx+igndts";
if (state.VideoRequest != null && genPts)
{
inputModifier += " -fflags +genpts";

@ -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),

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

@ -107,7 +107,7 @@ namespace MediaBrowser.Api.Playback
{
var authInfo = AuthorizationContext.GetAuthorizationInfo(Request);
var result = await _mediaSourceManager.OpenLiveStream(request, false, CancellationToken.None).ConfigureAwait(false);
var result = await _mediaSourceManager.OpenLiveStream(request, true, CancellationToken.None).ConfigureAwait(false);
var profile = request.DeviceProfile;
if (profile == null)
@ -140,7 +140,7 @@ namespace MediaBrowser.Api.Playback
public void Post(CloseMediaSource request)
{
var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId, CancellationToken.None);
var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId);
Task.WaitAll(task);
}

@ -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;
}
}
@ -217,7 +225,7 @@ namespace MediaBrowser.Api.Playback
{
try
{
await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).ConfigureAwait(false);
}
catch (Exception ex)
{
@ -508,5 +516,18 @@ namespace MediaBrowser.Api.Playback
return false;
}
}
public bool? IsTargetAVC
{
get
{
if (Request.Static)
{
return VideoStream == null ? null : VideoStream.IsAVC;
}
return true;
}
}
}
}

@ -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();
@ -215,8 +214,6 @@ namespace MediaBrowser.Api
public class StartupConfiguration
{
public string UICulture { get; set; }
public bool EnableInternetProviders { get; set; }
public bool SaveLocalMeta { get; set; }
public string MetadataCountryCode { get; set; }
public string PreferredMetadataLanguage { get; set; }
public string LiveTvTunerType { get; set; }

@ -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

@ -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,24 +131,31 @@ 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 :
null :
_libraryManager.GetItemById(request.ParentId);
if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase))
{
//item = user == null ? _libraryManager.RootFolder : user.RootFolder;
if (item == null || user != null)
{
item = _libraryManager.RootFolder.Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, "PlaylistsFolder", StringComparison.OrdinalIgnoreCase));
}
}
else if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase))
{
item = user == null ? _libraryManager.RootFolder : user.RootFolder;
}
if (item == null)
{
item = string.IsNullOrEmpty(request.ParentId) ?
user == null ? _libraryManager.RootFolder : user.RootFolder :
_libraryManager.GetItemById(request.ParentId);
}
// Default list type = children
var folder = item as Folder;
@ -159,14 +166,14 @@ namespace MediaBrowser.Api.UserLibrary
if (request.Recursive || !string.IsNullOrEmpty(request.Ids) || user == null)
{
return await folder.GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
return await folder.GetItems(GetItemsQuery(request, dtoOptions, user)).ConfigureAwait(false);
}
var userRoot = item as UserRootFolder;
if (userRoot == null)
{
return await folder.GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
return await folder.GetItems(GetItemsQuery(request, dtoOptions, user)).ConfigureAwait(false);
}
IEnumerable<BaseItem> items = folder.GetChildren(user, true);
@ -180,7 +187,7 @@ namespace MediaBrowser.Api.UserLibrary
};
}
private InternalItemsQuery GetItemsQuery(GetItems request, User user)
private InternalItemsQuery GetItemsQuery(GetItems request, DtoOptions dtoOptions, User user)
{
var query = new InternalItemsQuery(user)
{
@ -241,7 +248,8 @@ namespace MediaBrowser.Api.UserLibrary
AiredDuringSeason = request.AiredDuringSeason,
AlbumArtistStartsWithOrGreater = request.AlbumArtistStartsWithOrGreater,
EnableTotalRecordCount = request.EnableTotalRecordCount,
ExcludeItemIds = request.GetExcludeItemIds()
ExcludeItemIds = request.GetExcludeItemIds(),
DtoOptions = dtoOptions
};
if (!string.IsNullOrWhiteSpace(request.Ids))

@ -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" />

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

@ -169,7 +169,8 @@ namespace MediaBrowser.Common.Implementations.Security
var options = new HttpRequestOptions()
{
Url = AppstoreRegUrl,
CancellationToken = CancellationToken.None
CancellationToken = CancellationToken.None,
BufferContent = false
};
options.RequestHeaders.Add("X-Emby-Token", _appHost.SystemId);
options.RequestContent = parameters;
@ -269,7 +270,8 @@ namespace MediaBrowser.Common.Implementations.Security
Url = MBValidateUrl,
// Seeing block length errors
EnableHttpCompression = false
EnableHttpCompression = false,
BufferContent = false
};
options.SetPostData(data);

@ -30,7 +30,8 @@ namespace MediaBrowser.Common.Implementations.Updates
Url = url,
EnableKeepAlive = false,
CancellationToken = cancellationToken,
UserAgent = "Emby/3.0"
UserAgent = "Emby/3.0",
BufferContent = false
};
if (cacheLength.Ticks > 0)
@ -105,7 +106,8 @@ namespace MediaBrowser.Common.Implementations.Updates
Url = url,
EnableKeepAlive = false,
CancellationToken = cancellationToken,
UserAgent = "Emby/3.0"
UserAgent = "Emby/3.0",
BufferContent = false
};
using (var stream = await _httpClient.Get(options).ConfigureAwait(false))

@ -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>

@ -134,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,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,11 +34,26 @@ namespace MediaBrowser.Controller.Entities
}
}
[IgnoreDataMember]
public override bool IsPhysicalRoot
{
get { return true; }
}
public override bool CanDelete()
{
return false;
}
[IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
{
return false;
}
}
/// <summary>
/// The _virtual children
/// </summary>

@ -23,8 +23,7 @@ namespace MediaBrowser.Controller.Entities.Audio
IHasMusicGenres,
IHasLookupInfo<SongInfo>,
IHasMediaSources,
IThemeMedia,
IArchivable
IThemeMedia
{
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
@ -60,10 +59,19 @@ namespace MediaBrowser.Controller.Entities.Audio
AlbumArtists = new List<string>();
}
[IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
{
return true;
}
}
[IgnoreDataMember]
public override bool SupportsAddingToPlaylist
{
get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; }
get { return true; }
}
[IgnoreDataMember]
@ -84,21 +92,6 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
[IgnoreDataMember]
public bool IsArchive
{
get
{
if (string.IsNullOrWhiteSpace(Path))
{
return false;
}
var ext = System.IO.Path.GetExtension(Path) ?? string.Empty;
return new[] { ".zip", ".rar", ".7z" }.Contains(ext, StringComparer.OrdinalIgnoreCase);
}
}
public override bool CanDownload()
{
var locationType = LocationType;
@ -262,7 +255,7 @@ namespace MediaBrowser.Controller.Entities.Audio
Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File,
MediaStreams = MediaSourceManager.GetMediaStreams(i.Id).ToList(),
Name = i.Name,
Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path,
Path = enablePathSubstituion ? GetMappedPath(i, i.Path, locationType) : i.Path,
RunTimeTicks = i.RunTimeTicks,
Container = i.Container,
Size = i.Size

@ -17,6 +17,9 @@ namespace MediaBrowser.Controller.Entities.Audio
/// </summary>
public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo>, IMetadataContainer
{
public List<string> AlbumArtists { get; set; }
public List<string> Artists { get; set; }
public MusicAlbum()
{
Artists = new List<string>();
@ -48,6 +51,15 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
[IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
{
return false;
}
}
[IgnoreDataMember]
public override bool SupportsCumulativeRunTimeTicks
{
@ -83,8 +95,6 @@ namespace MediaBrowser.Controller.Entities.Audio
get { return false; }
}
public List<string> AlbumArtists { get; set; }
/// <summary>
/// Gets the tracks.
/// </summary>
@ -103,8 +113,6 @@ namespace MediaBrowser.Controller.Entities.Audio
return Tracks;
}
public List<string> Artists { get; set; }
public override List<string> GetUserDataKeys()
{
var list = base.GetUserDataKeys();

@ -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
{
@ -50,6 +48,15 @@ namespace MediaBrowser.Controller.Entities.Audio
get { return true; }
}
[IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
{
return false;
}
}
public override bool CanDelete()
{
return !IsAccessedByName;
@ -111,11 +118,6 @@ namespace MediaBrowser.Controller.Entities.Audio
return base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService);
}
public MusicArtist()
{
ProductionLocations = new List<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,31 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public bool IsInMixedFolder { get; set; }
[IgnoreDataMember]
protected virtual bool SupportsIsInMixedFolderDetection
{
get { return false; }
}
[IgnoreDataMember]
public virtual bool SupportsPlayedStatus
{
get
{
return false;
}
}
public bool DetectIsInMixedFolder()
{
if (SupportsIsInMixedFolderDetection)
{
}
return IsInMixedFolder;
}
[IgnoreDataMember]
public virtual bool SupportsRemoteImageDownloading
{
@ -254,6 +288,19 @@ namespace MediaBrowser.Controller.Entities
}
}
[IgnoreDataMember]
public string ExternalSeriesId { get; set; }
[IgnoreDataMember]
public string ExternalSeriesIdLegacy
{
get { return this.GetProviderId("ProviderExternalSeriesId"); }
set
{
this.SetProviderId("ProviderExternalSeriesId", value);
}
}
/// <summary>
/// Gets or sets the etag.
/// </summary>
@ -408,7 +455,7 @@ namespace MediaBrowser.Controller.Entities
public virtual bool IsInternetMetadataEnabled()
{
return ConfigurationManager.Configuration.EnableInternetProviders;
return LibraryManager.GetLibraryOptions(this).EnableInternetProviders;
}
public virtual bool CanDelete()
@ -784,6 +831,9 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public int InheritedParentalRatingValue { get; set; }
[IgnoreDataMember]
public List<string> InheritedTags { get; set; }
/// <summary>
/// Gets or sets the critic rating.
/// </summary>
@ -841,6 +891,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 +1007,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 +1043,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 +1129,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 +1153,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 +1197,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 +1228,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 +1305,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 +1388,9 @@ namespace MediaBrowser.Controller.Entities
return false;
}
return ConfigurationManager.Configuration.SaveLocalMeta;
var libraryOptions = LibraryManager.GetLibraryOptions(this);
return libraryOptions.SaveLocalMetadata;
}
/// <summary>
@ -2107,14 +2173,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;

@ -38,6 +38,15 @@ namespace MediaBrowser.Controller.Entities
}
}
[IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
{
return false;
}
}
public override bool CanDelete()
{
return false;
@ -48,24 +57,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 +99,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 = 2;
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,21 @@ namespace MediaBrowser.Controller.Entities
get { return false; }
}
[IgnoreDataMember]
public virtual bool IsPhysicalRoot
{
get { return false; }
}
[IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
{
return true;
}
}
/// <summary>
/// Gets a value indicating whether this instance is folder.
/// </summary>
@ -117,19 +140,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 +188,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 +215,6 @@ namespace MediaBrowser.Controller.Entities
get { return GetIndexByOptions(); }
}
#endregion
/// <summary>
/// Gets the actual children.
/// </summary>
@ -801,18 +807,6 @@ namespace MediaBrowser.Controller.Entities
return true;
}
if (query.HasThemeSong.HasValue)
{
Logger.Debug("Query requires post-filtering due to HasThemeSong");
return true;
}
if (query.HasThemeVideo.HasValue)
{
Logger.Debug("Query requires post-filtering due to HasThemeVideo");
return true;
}
// Filter by VideoType
if (query.VideoTypes.Length > 0)
{
@ -1149,29 +1143,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,19 +8,14 @@ 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>();
RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>();
RemoteTrailerIds = new List<Guid>();
ThemeSongIds = new List<Guid>();
ThemeVideoIds = new List<Guid>();
}
public List<Guid> LocalTrailerIds { get; set; }
@ -39,6 +34,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 +99,7 @@ namespace MediaBrowser.Controller.Entities
public override IEnumerable<string> GetDeletePaths()
{
if (!IsInMixedFolder)
if (!DetectIsInMixedFolder())
{
return new[] { System.IO.Path.GetDirectoryName(Path) };
}

@ -26,6 +26,15 @@ namespace MediaBrowser.Controller.Entities
}
}
[IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
{
return false;
}
}
/// <summary>
/// Gets or sets the game system.
/// </summary>

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

@ -20,5 +20,7 @@ namespace MediaBrowser.Controller.Entities
Task FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user);
bool EnableRememberingTrackSelections { get; }
bool SupportsPlayedStatus { get; }
}
}

@ -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,53 @@ 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.ThemeSongIds:
case ItemFields.ThemeVideoIds:
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 +239,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; }
@ -75,13 +68,22 @@ namespace MediaBrowser.Controller.Entities.Movies
set { TmdbCollectionName = value; }
}
[IgnoreDataMember]
protected override bool SupportsIsInMixedFolderDetection
{
get
{
return false;
}
}
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{
var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
// 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 +121,7 @@ namespace MediaBrowser.Controller.Entities.Movies
{
var info = GetItemLookupInfo<MovieInfo>();
if (!IsInMixedFolder)
if (!DetectIsInMixedFolder())
{
info.Name = System.IO.Path.GetFileName(ContainingFolderPath);
}
@ -145,7 +147,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
{
@ -37,13 +29,13 @@ namespace MediaBrowser.Controller.Entities
{
get
{
return Album;
return AlbumEntity;
}
}
[IgnoreDataMember]
public PhotoAlbum Album
public PhotoAlbum AlbumEntity
{
get
{

@ -16,6 +16,15 @@ namespace MediaBrowser.Controller.Entities
}
}
[IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
{
return false;
}
}
protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Other);

@ -154,8 +154,6 @@ namespace MediaBrowser.Controller.Entities.TV
Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
var id = Guid.NewGuid().ToString("N");
var items = GetEpisodes(user).Where(filter);
var result = PostFilterAndSort(items, query, false, false);

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

@ -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 && LocationType == LocationType.FileSystem)
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));

@ -33,6 +33,15 @@ namespace MediaBrowser.Controller.Entities
}
}
[IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
{
return false;
}
}
private void ClearCache()
{
lock (_childIdsLock)

@ -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; }
@ -45,6 +44,15 @@ namespace MediaBrowser.Controller.Entities
return list;
}
[IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
{
return false;
}
}
public override int GetChildCount(User user)
{
return GetChildren(user, true).Count();

@ -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; }
@ -45,6 +44,24 @@ namespace MediaBrowser.Controller.Entities
}
}
[IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
{
return true;
}
}
[IgnoreDataMember]
protected override bool SupportsIsInMixedFolderDetection
{
get
{
return true;
}
}
public override string CreatePresentationUniqueKey()
{
if (!string.IsNullOrWhiteSpace(PrimaryVersionId))
@ -64,6 +81,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 +188,7 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public override bool SupportsAddingToPlaylist
{
get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; }
get { return true; }
}
[IgnoreDataMember]
@ -197,21 +220,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 +488,7 @@ namespace MediaBrowser.Controller.Entities
public override IEnumerable<string> GetDeletePaths()
{
if (!IsInMixedFolder)
if (!DetectIsInMixedFolder())
{
return new[] { ContainingFolderPath };
}
@ -600,7 +608,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,

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

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

@ -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>
@ -125,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>
@ -156,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>
@ -220,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.
@ -242,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.
@ -303,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.
@ -331,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>
@ -395,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)

@ -5,11 +5,12 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.LiveTv
{
public class LiveTvChannel : BaseItem, IHasMediaSources
public class LiveTvChannel : BaseItem, IHasMediaSources, IHasProgramAttributes
{
public override List<string> GetUserDataKeys()
{
@ -81,10 +82,10 @@ namespace MediaBrowser.Controller.LiveTv
if (!string.IsNullOrEmpty(Number))
{
double.TryParse(Number, out number);
double.TryParse(Number, NumberStyles.Any, CultureInfo.InvariantCulture, out number);
}
return number.ToString("000-") + (Name ?? string.Empty);
return number.ToString("00000-") + (Name ?? string.Empty);
}
[IgnoreDataMember]
@ -137,5 +138,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; }
@ -53,6 +54,24 @@ namespace MediaBrowser.Controller.LiveTv
}
}
[IgnoreDataMember]
protected override bool SupportsIsInMixedFolderDetection
{
get
{
return false;
}
}
[IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
{
return Status == RecordingStatus.Completed && base.SupportsPlayedStatus;
}
}
[IgnoreDataMember]
public override LocationType LocationType
{
@ -111,7 +130,7 @@ namespace MediaBrowser.Controller.LiveTv
public override bool CanDelete()
{
return true;
return Status == RecordingStatus.Completed;
}
public override bool IsAuthorizedToDelete(User user)

@ -107,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>

@ -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,10 +91,7 @@
<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" />
@ -135,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" />
@ -147,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" />
@ -201,6 +194,7 @@
<Compile Include="Library\UserDataSaveEventArgs.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.
@ -84,7 +84,7 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <param name="inputFiles">The input files.</param>
/// <param name="protocol">The protocol.</param>
/// <returns>System.String.</returns>
string GetProbeSizeArgument(string[] inputFiles, MediaProtocol protocol);
string GetProbeSizeAndAnalyzeDurationArgument(string[] inputFiles, MediaProtocol protocol);
/// <summary>
/// Gets the input argument.

@ -15,6 +15,7 @@ namespace MediaBrowser.Controller.MediaEncoding
public IIsoMount MountedIso { get; set; }
public VideoType VideoType { get; set; }
public List<string> PlayableStreamFileNames { get; set; }
public int AnalyzeDurationSections { get; set; }
public MediaInfoRequest()
{

@ -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>

@ -31,6 +31,15 @@ namespace MediaBrowser.Controller.Playlists
}
}
[IgnoreDataMember]
public override bool SupportsPlayedStatus
{
get
{
return string.Equals(MediaType, "Video", StringComparison.OrdinalIgnoreCase);
}
}
[IgnoreDataMember]
public override bool AlwaysScanInternalMetadataPath
{
@ -198,15 +207,15 @@ namespace MediaBrowser.Controller.Playlists
public override bool IsVisible(User user)
{
if (base.IsVisible(user))
{
var userId = user.Id.ToString("N");
var userId = user.Id.ToString("N");
return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) ||
string.Equals(OwnerUserId, userId, StringComparison.OrdinalIgnoreCase);
}
return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) ||
string.Equals(OwnerUserId, userId, StringComparison.OrdinalIgnoreCase);
}
return false;
public override bool IsVisibleStandalone(User user)
{
return IsVisible(user);
}
}
}

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

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

Loading…
Cancel
Save