Merge branch 'beta'

pull/1154/head
Luke Pulverenti 9 years ago
commit daa0b6cd0e

@ -183,50 +183,6 @@ namespace MediaBrowser.Api
return libraryManager.GetPerson(DeSlugPersonName(name, libraryManager));
}
protected IList<BaseItem> GetAllLibraryItems(string userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem,bool> filter)
{
if (!string.IsNullOrEmpty(parentId))
{
var folder = (Folder)libraryManager.GetItemById(new Guid(parentId));
if (!string.IsNullOrWhiteSpace(userId))
{
var user = userManager.GetUserById(userId);
if (user == null)
{
throw new ArgumentException("User not found");
}
return folder
.GetRecursiveChildren(user, filter)
.ToList();
}
return folder
.GetRecursiveChildren(filter);
}
if (!string.IsNullOrWhiteSpace(userId))
{
var user = userManager.GetUserById(userId);
if (user == null)
{
throw new ArgumentException("User not found");
}
return userManager
.GetUserById(userId)
.RootFolder
.GetRecursiveChildren(user, filter)
.ToList();
}
return libraryManager
.RootFolder
.GetRecursiveChildren(filter);
}
/// <summary>
/// Deslugs an artist name by finding the correct entry in the library
/// </summary>

@ -102,12 +102,16 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns>
public object Get(GetGameSystemSummaries request)
{
var gameSystems = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i is GameSystem)
var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId);
var query = new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(GameSystem).Name }
};
var parentIds = new string[] { } ;
var gameSystems = _libraryManager.GetItems(query, parentIds)
.Cast<GameSystem>()
.ToList();
var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId);
var result = gameSystems
.Select(i => GetSummary(i, user))
.ToList();
@ -119,8 +123,15 @@ namespace MediaBrowser.Api
public object Get(GetPlayerIndex request)
{
var games = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i is Game)
.Cast<Game>();
var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId);
var query = new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(Game).Name }
};
var parentIds = new string[] { };
var games = _libraryManager.GetItems(query, parentIds)
.Cast<Game>()
.ToList();
var lookup = games
.ToLookup(i => i.PlayersSupported ?? -1)

@ -1,9 +1,11 @@
using MediaBrowser.Controller.FileOrganization;
using System.Collections.Generic;
using MediaBrowser.Controller.FileOrganization;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.FileOrganization;
using MediaBrowser.Model.Querying;
using ServiceStack;
using System.Threading.Tasks;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Api.Library
{
@ -74,6 +76,31 @@ namespace MediaBrowser.Api.Library
public bool RememberCorrection { get; set; }
}
[Route("/Library/FileOrganizations/SmartMatches", "GET", Summary = "Gets smart match entries")]
public class GetSmartMatchInfos : IReturn<QueryResult<SmartMatchInfo>>
{
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
/// </summary>
/// <value>The start index.</value>
[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; }
/// <summary>
/// The maximum number of items to return
/// </summary>
/// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? Limit { get; set; }
}
[Route("/Library/FileOrganizations/SmartMatches/Delete", "POST", Summary = "Deletes a smart match entry")]
public class DeleteSmartMatchEntry
{
[ApiMember(Name = "Entries", Description = "SmartMatch Entry", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public List<NameValuePair> Entries { get; set; }
}
[Authenticated(Roles = "Admin")]
public class FileOrganizationService : BaseApiService
{
@ -130,5 +157,24 @@ namespace MediaBrowser.Api.Library
Task.WaitAll(task);
}
public object Get(GetSmartMatchInfos request)
{
var result = _iFileOrganizationService.GetSmartMatchInfos(new FileOrganizationResultQuery
{
Limit = request.Limit,
StartIndex = request.StartIndex
});
return ToOptimizedSerializedResultUsingCache(result);
}
public void Post(DeleteSmartMatchEntry request)
{
request.Entries.ForEach(entry =>
{
_iFileOrganizationService.DeleteSmartMatchEntry(entry.Name, entry.Value);
});
}
}
}

@ -424,7 +424,7 @@ namespace MediaBrowser.Api.Library
public object Get(GetMediaFolders request)
{
var items = _libraryManager.GetUserRootFolder().Children.OrderBy(i => i.SortName).ToList();
var items = _libraryManager.GetUserRootFolder().Children.Concat(_libraryManager.RootFolder.VirtualChildren).OrderBy(i => i.SortName).ToList();
if (request.IsHidden.HasValue)
{
@ -569,7 +569,7 @@ namespace MediaBrowser.Api.Library
{
throw new ArgumentException("This command cannot be used for remote or virtual items.");
}
if (_fileSystem.DirectoryExists(item.Path))
if (_fileSystem.DirectoryExists(item.Path))
{
throw new ArgumentException("This command cannot be used for directories.");
}
@ -618,7 +618,7 @@ namespace MediaBrowser.Api.Library
var dtoOptions = GetDtoOptions(request);
BaseItem parent = item.Parent;
BaseItem parent = item.GetParent();
while (parent != null)
{
@ -629,7 +629,7 @@ namespace MediaBrowser.Api.Library
baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user));
parent = parent.Parent;
parent = parent.GetParent();
}
return baseItemDtos.ToList();
@ -637,7 +637,7 @@ namespace MediaBrowser.Api.Library
private BaseItem TranslateParentItem(BaseItem item, User user)
{
if (item.Parent is AggregateFolder)
if (item.GetParent() is AggregateFolder)
{
return user.RootFolder.GetChildren(user, true).FirstOrDefault(i => i.PhysicalLocations.Contains(item.Path));
}
@ -685,6 +685,50 @@ namespace MediaBrowser.Api.Library
return ToOptimizedSerializedResultUsingCache(counts);
}
private IList<BaseItem> GetAllLibraryItems(string userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem, bool> filter)
{
if (!string.IsNullOrEmpty(parentId))
{
var folder = (Folder)libraryManager.GetItemById(new Guid(parentId));
if (!string.IsNullOrWhiteSpace(userId))
{
var user = userManager.GetUserById(userId);
if (user == null)
{
throw new ArgumentException("User not found");
}
return folder
.GetRecursiveChildren(user, filter)
.ToList();
}
return folder
.GetRecursiveChildren(filter);
}
if (!string.IsNullOrWhiteSpace(userId))
{
var user = userManager.GetUserById(userId);
if (user == null)
{
throw new ArgumentException("User not found");
}
return userManager
.GetUserById(userId)
.RootFolder
.GetRecursiveChildren(user, filter)
.ToList();
}
return libraryManager
.RootFolder
.GetRecursiveChildren(filter);
}
private bool FilterItem(BaseItem item, GetItemCounts request, string userId)
{
if (!string.IsNullOrWhiteSpace(userId))
@ -745,12 +789,10 @@ namespace MediaBrowser.Api.Library
return Task.FromResult(true);
}
if (item is ILiveTvRecording)
return item.Delete(new DeleteOptions
{
return _liveTv.DeleteRecording(i);
}
return _libraryManager.DeleteItem(item);
DeleteFileLocation = true
});
}).ToArray();
Task.WaitAll(tasks);
@ -847,9 +889,9 @@ namespace MediaBrowser.Api.Library
: (Folder)_libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id);
while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.Parent != null)
while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
{
item = item.Parent;
item = item.GetParent();
}
var dtoOptions = GetDtoOptions(request);
@ -890,9 +932,9 @@ namespace MediaBrowser.Api.Library
: (Folder)_libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id);
while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.Parent != null)
while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
{
item = item.Parent;
item = item.GetParent();
}
var dtoOptions = GetDtoOptions(request);

@ -80,6 +80,7 @@
<Compile Include="FilterService.cs" />
<Compile Include="IHasDtoOptions.cs" />
<Compile Include="Library\ChapterService.cs" />
<Compile Include="PinLoginService.cs" />
<Compile Include="Playback\Dash\ManifestBuilder.cs" />
<Compile Include="Playback\Dash\MpegDashService.cs" />
<Compile Include="Playback\MediaInfoService.cs" />

@ -117,10 +117,7 @@ namespace MediaBrowser.Api.Movies
public async Task<object> Get(GetSimilarMovies request)
{
var result = await GetSimilarItemsResult(
// Strip out secondary versions
request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue,
SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false);
request, SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false);
return ToOptimizedSerializedResultUsingCache(result);
}
@ -128,10 +125,7 @@ namespace MediaBrowser.Api.Movies
public async Task<object> Get(GetSimilarTrailers request)
{
var result = await GetSimilarItemsResult(
// Strip out secondary versions
request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue,
SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false);
request, SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false);
return ToOptimizedSerializedResultUsingCache(result);
}
@ -140,8 +134,12 @@ namespace MediaBrowser.Api.Movies
{
var user = _userManager.GetUserById(request.UserId);
IEnumerable<BaseItem> movies = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Movie);
var query = new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(Movie).Name }
};
var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId };
var movies = _libraryManager.GetItems(query, parentIds);
movies = _libraryManager.ReplaceVideosWithPrimaryVersions(movies);
var listEligibleForCategories = new List<BaseItem>();
@ -184,21 +182,27 @@ namespace MediaBrowser.Api.Movies
return ToOptimizedResult(result);
}
private async Task<ItemsResult> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
private async Task<ItemsResult> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
{
var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
var item = string.IsNullOrEmpty(request.Id) ?
(!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
Func<BaseItem, bool> filter = i => i.Id != item.Id && includeInSearch(i);
var inputItems = user == null
? _libraryManager.RootFolder.GetRecursiveChildren(filter)
: user.RootFolder.GetRecursiveChildren(user, filter);
var list = inputItems.ToList();
var query = new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(Movie).Name }
};
var parentIds = new string[] { };
var list = _libraryManager.GetItems(query, parentIds)
.Where(i =>
{
// Strip out secondary versions
var v = i as Video;
return v != null && !v.PrimaryVersionId.HasValue;
})
.ToList();
if (user != null && user.Configuration.IncludeTrailersInSuggestions)
{
@ -379,9 +383,10 @@ namespace MediaBrowser.Api.Movies
{
foreach (var name in names)
{
var itemsWithActor = _libraryManager.GetItemIds(new InternalItemsQuery
var itemsWithActor = _libraryManager.GetItemIds(new InternalItemsQuery(user)
{
Person = name
});
var items = allMovies

@ -102,7 +102,7 @@ namespace MediaBrowser.Api
{
private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _serializer;
private const string MbAdminUrl = "http://www.mb3admin.com/admin/";
private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
private readonly IServerApplicationHost _appHost;
public PackageReviewService(IHttpClient httpClient, IJsonSerializer serializer, IServerApplicationHost appHost)

@ -0,0 +1,202 @@
using System;
using System.Collections.Concurrent;
using System.Globalization;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Connect;
using ServiceStack;
namespace MediaBrowser.Api
{
[Route("/Auth/Pin", "POST", Summary = "Creates a pin request")]
public class CreatePinRequest : IReturn<PinCreationResult>
{
[ApiMember(Name = "DeviceId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string DeviceId { get; set; }
}
[Route("/Auth/Pin", "GET", Summary = "Gets pin status")]
public class GetPinStatusRequest : IReturn<PinStatusResult>
{
[ApiMember(Name = "DeviceId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string DeviceId { get; set; }
[ApiMember(Name = "Pin", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Pin { get; set; }
}
[Route("/Auth/Pin/Exchange", "POST", Summary = "Exchanges a pin")]
public class ExchangePinRequest : IReturn<PinExchangeResult>
{
[ApiMember(Name = "DeviceId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string DeviceId { get; set; }
[ApiMember(Name = "Pin", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Pin { get; set; }
}
[Route("/Auth/Pin/Validate", "POST", Summary = "Validates a pin")]
[Authenticated]
public class ValidatePinRequest : IReturnVoid
{
[ApiMember(Name = "Pin", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Pin { get; set; }
}
public class PinLoginService : BaseApiService
{
private readonly ConcurrentDictionary<string, MyPinStatus> _activeRequests = new ConcurrentDictionary<string, MyPinStatus>(StringComparer.OrdinalIgnoreCase);
public object Post(CreatePinRequest request)
{
var pin = GetNewPin();
var value = new MyPinStatus
{
CreationTimeUtc = DateTime.UtcNow,
IsConfirmed = false,
IsExpired = false,
Pin = pin,
DeviceId = request.DeviceId
};
_activeRequests.AddOrUpdate(pin, value, (k, v) => value);
return ToOptimizedResult(new PinCreationResult
{
DeviceId = request.DeviceId,
IsConfirmed = false,
IsExpired = false,
Pin = pin
});
}
public object Get(GetPinStatusRequest request)
{
MyPinStatus status;
if (!_activeRequests.TryGetValue(request.Pin, out status))
{
throw new ResourceNotFoundException();
}
EnsureValid(request.DeviceId, status);
return ToOptimizedResult(new PinStatusResult
{
Pin = status.Pin,
IsConfirmed = status.IsConfirmed,
IsExpired = status.IsExpired
});
}
public object Post(ExchangePinRequest request)
{
MyPinStatus status;
if (!_activeRequests.TryGetValue(request.Pin, out status))
{
throw new ResourceNotFoundException();
}
EnsureValid(request.DeviceId, status);
if (!status.IsConfirmed)
{
throw new ResourceNotFoundException();
}
return ToOptimizedResult(new PinExchangeResult
{
// TODO: Add access token
UserId = status.UserId
});
}
public void Post(ValidatePinRequest request)
{
MyPinStatus status;
if (!_activeRequests.TryGetValue(request.Pin, out status))
{
throw new ResourceNotFoundException();
}
EnsureValid(status);
status.IsConfirmed = true;
status.UserId = AuthorizationContext.GetAuthorizationInfo(Request).UserId;
}
private void EnsureValid(string requestedDeviceId, MyPinStatus status)
{
if (!string.Equals(requestedDeviceId, status.DeviceId, StringComparison.OrdinalIgnoreCase))
{
throw new ResourceNotFoundException();
}
EnsureValid(status);
}
private void EnsureValid(MyPinStatus status)
{
if ((DateTime.UtcNow - status.CreationTimeUtc).TotalMinutes > 10)
{
status.IsExpired = true;
}
if (status.IsExpired)
{
throw new ResourceNotFoundException();
}
}
private string GetNewPin()
{
var pin = GetNewPinInternal();
while (IsPinActive(pin))
{
pin = GetNewPinInternal();
}
return pin;
}
private string GetNewPinInternal()
{
var length = 5;
var pin = string.Empty;
while (pin.Length < length)
{
var digit = new Random().Next(0, 9);
pin += digit.ToString(CultureInfo.InvariantCulture);
}
return pin;
}
private bool IsPinActive(string pin)
{
MyPinStatus status;
if (!_activeRequests.TryGetValue(pin, out status))
{
return true;
}
if (status.IsExpired)
{
return true;
}
return false;
}
public class MyPinStatus : PinStatusResult
{
public DateTime CreationTimeUtc { get; set; }
public string DeviceId { get; set; }
public string UserId { get; set; }
}
}
}

@ -305,9 +305,8 @@ namespace MediaBrowser.Api.Playback
/// </summary>
/// <param name="state">The state.</param>
/// <param name="videoCodec">The video codec.</param>
/// <param name="isHls">if set to <c>true</c> [is HLS].</param>
/// <returns>System.String.</returns>
protected string GetVideoQualityParam(StreamState state, string videoCodec, bool isHls)
protected string GetVideoQualityParam(StreamState state, string videoCodec)
{
var param = string.Empty;
@ -385,7 +384,7 @@ namespace MediaBrowser.Api.Playback
param = "-mbd 2";
}
param += GetVideoBitrateParam(state, videoCodec, isHls);
param += GetVideoBitrateParam(state, videoCodec);
var framerate = GetFramerateParam(state);
if (framerate.HasValue)
@ -1190,7 +1189,7 @@ namespace MediaBrowser.Api.Playback
return bitrate;
}
protected string GetVideoBitrateParam(StreamState state, string videoCodec, bool isHls)
protected string GetVideoBitrateParam(StreamState state, string videoCodec)
{
var bitrate = state.OutputVideoBitrate;
@ -1209,14 +1208,9 @@ namespace MediaBrowser.Api.Playback
}
// h264
if (isHls)
{
return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}",
bitrate.Value.ToString(UsCulture),
(bitrate.Value * 2).ToString(UsCulture));
}
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}",
bitrate.Value.ToString(UsCulture),
(bitrate.Value * 2).ToString(UsCulture));
}
return string.Empty;

@ -430,7 +430,7 @@ namespace MediaBrowser.Api.Playback.Dash
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
args += " " + GetVideoQualityParam(state, GetH264Encoder(state), true) + keyFrameArg;
args += " " + GetVideoQualityParam(state, GetH264Encoder(state)) + keyFrameArg;
// Add resolution params, if specified
if (!hasGraphicalSubs)

@ -822,7 +822,7 @@ namespace MediaBrowser.Api.Playback.Hls
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
args += " " + GetVideoQualityParam(state, GetH264Encoder(state), true) + keyFrameArg;
args += " " + GetVideoQualityParam(state, GetH264Encoder(state)) + keyFrameArg;
//args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";

@ -106,7 +106,7 @@ namespace MediaBrowser.Api.Playback.Hls
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
args += " " + GetVideoQualityParam(state, GetH264Encoder(state), true) + keyFrameArg;
args += " " + GetVideoQualityParam(state, GetH264Encoder(state)) + keyFrameArg;
// Add resolution params, if specified
if (!hasGraphicalSubs)

@ -166,7 +166,7 @@ namespace MediaBrowser.Api.Playback.Progressive
args += GetOutputSizeParam(state, videoCodec);
}
var qualityParam = GetVideoQualityParam(state, videoCodec, false);
var qualityParam = GetVideoQualityParam(state, videoCodec);
if (!string.IsNullOrEmpty(qualityParam))
{

@ -284,7 +284,7 @@ namespace MediaBrowser.Api
private T GetParentWithImage<T>(BaseItem item, ImageType type)
where T : BaseItem
{
return item.Parents.OfType<T>().FirstOrDefault(i => i.HasImage(type));
return item.GetParents().OfType<T>().FirstOrDefault(i => i.HasImage(type));
}
}
}

@ -66,12 +66,8 @@ namespace MediaBrowser.Api
{
_config.Configuration.IsStartupWizardCompleted = true;
_config.Configuration.EnableLocalizedGuids = true;
_config.Configuration.EnableLibraryMetadataSubFolder = true;
_config.Configuration.EnableCustomPathSubFolders = true;
_config.Configuration.DisableStartupScan = true;
_config.Configuration.EnableUserViews = true;
_config.Configuration.EnableDateLastRefresh = true;
_config.Configuration.MergeMetadataAndImagesByName = true;
_config.SaveConfiguration();
}

@ -159,7 +159,7 @@ namespace MediaBrowser.Api
[ApiMember(Name = "StartItemId", Description = "Optional. Skip through the list until a given item is found.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string StartItemId { get; set; }
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
/// </summary>
@ -273,29 +273,28 @@ namespace MediaBrowser.Api
{
var user = _userManager.GetUserById(request.UserId);
var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Episode);
var itemsList = _libraryManager
.Sort(items, user, new[] { "PremiereDate", "AirTime", "SortName" }, SortOrder.Ascending)
.Cast<Episode>()
.ToList();
var unairedEpisodes = itemsList.Where(i => i.IsUnaired).ToList();
var minPremiereDate = DateTime.Now.Date.AddDays(-1).ToUniversalTime();
var previousEpisodes = itemsList.Where(i => !i.IsUnaired && (i.PremiereDate ?? DateTime.MinValue) >= minPremiereDate).ToList();
previousEpisodes.AddRange(unairedEpisodes);
var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId };
var pagedItems = ApplyPaging(previousEpisodes, request.StartIndex, request.Limit);
var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(Episode).Name },
SortBy = new[] { "PremiereDate", "AirTime", "SortName" },
SortOrder = SortOrder.Ascending,
MinPremiereDate = minPremiereDate,
StartIndex = request.StartIndex,
Limit = request.Limit
}, parentIds);
var options = GetDtoOptions(request);
var returnItems = _dtoService.GetBaseItemDtos(pagedItems, options, user).ToArray();
var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, options, user).ToArray();
var result = new ItemsResult
{
TotalRecordCount = itemsList.Count,
TotalRecordCount = itemsResult.TotalRecordCount,
Items = returnItems
};
@ -440,7 +439,7 @@ namespace MediaBrowser.Api
}
episodes = season.GetEpisodes(user);
}
}
else if (request.Season.HasValue)
{
var series = _libraryManager.GetItemById(request.Id) as Series;
@ -495,7 +494,7 @@ namespace MediaBrowser.Api
.ToList();
var pagedItems = ApplyPaging(returnList, request.StartIndex, request.Limit);
var dtoOptions = GetDtoOptions(request);
var dtos = _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user)

@ -206,6 +206,8 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "Genres", Description = "Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Genres { get; set; }
public string GenreIds { get; set; }
[ApiMember(Name = "OfficialRatings", Description = "Optional. If specified, results will be filtered based on OfficialRating. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string OfficialRatings { get; set; }
@ -385,6 +387,11 @@ namespace MediaBrowser.Api.UserLibrary
return (StudioIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
}
public string[] GetGenreIds()
{
return (GenreIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
}
public string[] GetPersonTypes()
{
return (PersonTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

@ -112,6 +112,15 @@ namespace MediaBrowser.Api.UserLibrary
user == null ? _libraryManager.RootFolder : user.RootFolder :
parentItem;
if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase))
{
item = user == null ? _libraryManager.RootFolder : user.RootFolder;
}
else if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase))
{
item = user == null ? _libraryManager.RootFolder : user.RootFolder;
}
// Default list type = children
if (!string.IsNullOrEmpty(request.Ids))
@ -211,6 +220,7 @@ namespace MediaBrowser.Api.UserLibrary
Tags = request.GetTags(),
OfficialRatings = request.GetOfficialRatings(),
Genres = request.GetGenres(),
GenreIds = request.GetGenreIds(),
Studios = request.GetStudios(),
StudioIds = request.GetStudioIds(),
Person = request.Person,
@ -423,15 +433,6 @@ namespace MediaBrowser.Api.UserLibrary
return false;
}
// Min index number
if (request.MinIndexNumber.HasValue)
{
if (!(i.IndexNumber.HasValue && i.IndexNumber.Value >= request.MinIndexNumber.Value))
{
return false;
}
}
// Min official rating
if (!string.IsNullOrEmpty(request.MinOfficialRating))
{

@ -26,6 +26,8 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "IncludeExternalContent", Description = "Whether or not to include external views such as channels or live tv", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "POST")]
public bool? IncludeExternalContent { get; set; }
public string PresetViews { get; set; }
}
[Route("/Users/{UserId}/SpecialViewOptions", "GET")]
@ -75,9 +77,24 @@ namespace MediaBrowser.Api.UserLibrary
query.IncludeExternalContent = request.IncludeExternalContent.Value;
}
if (!string.IsNullOrWhiteSpace(request.PresetViews))
{
query.PresetViews = request.PresetViews.Split(',');
}
var app = AuthorizationContext.GetAuthorizationInfo(Request).Client ?? string.Empty;
if (app.IndexOf("emby rt", StringComparison.OrdinalIgnoreCase) != -1)
{
query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows };
}
//query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows };
var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false);
var dtoOptions = GetDtoOptions(request);
dtoOptions.Fields = new List<ItemFields>();
dtoOptions.Fields.Add(ItemFields.PrimaryImageAspectRatio);
dtoOptions.Fields.Add(ItemFields.DisplayPreferencesId);
var user = _userManager.GetUserById(request.UserId);
@ -123,7 +140,7 @@ namespace MediaBrowser.Api.UserLibrary
var views = user.RootFolder
.GetChildren(user, true)
.OfType<Folder>()
.Where(i => !UserView.IsExcludedFromGrouping(i))
.Where(UserView.IsEligibleForGrouping)
.ToList();
var list = views
@ -141,9 +158,7 @@ namespace MediaBrowser.Api.UserLibrary
private bool IsEligibleForSpecialView(ICollectionFolder view)
{
var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music, CollectionType.Photos };
return types.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
return UserView.IsEligibleForEnhancedView(view.CollectionType);
}
}

@ -453,7 +453,7 @@ namespace MediaBrowser.Common.Implementations
RegisterSingleInstance<IApplicationPaths>(ApplicationPaths);
TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, Logger, FileSystemManager);
TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LogManager.GetLogger("TaskManager"), FileSystemManager);
RegisterSingleInstance(JsonSerializer);
RegisterSingleInstance(XmlSerializer);
@ -465,7 +465,7 @@ namespace MediaBrowser.Common.Implementations
RegisterSingleInstance(FileSystemManager);
HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, Logger, FileSystemManager);
HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager);
RegisterSingleInstance(HttpClient);
NetworkManager = CreateNetworkManager(LogManager.GetLogger("NetworkManager"));
@ -474,7 +474,7 @@ namespace MediaBrowser.Common.Implementations
SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager);
RegisterSingleInstance(SecurityManager);
InstallationManager = new InstallationManager(Logger, this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager);
InstallationManager = new InstallationManager(LogManager.GetLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager);
RegisterSingleInstance(InstallationManager);
ZipClient = new ZipClient(FileSystemManager);

@ -55,6 +55,25 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
private ILogger Logger { get; set; }
private readonly IFileSystem _fileSystem;
private bool _suspendTriggers;
public bool SuspendTriggers
{
get { return _suspendTriggers; }
set
{
Logger.Info("Setting SuspendTriggers to {0}", value);
var executeQueued = _suspendTriggers && !value;
_suspendTriggers = value;
if (executeQueued)
{
ExecuteQueuedTasks();
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="TaskManager" /> class.
/// </summary>
@ -151,6 +170,31 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
QueueScheduledTask<T>(new TaskExecutionOptions());
}
public void Execute<T>()
where T : IScheduledTask
{
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == typeof(T));
if (scheduledTask == null)
{
Logger.Error("Unable to find scheduled task of type {0} in Execute.", typeof(T).Name);
}
else
{
var type = scheduledTask.ScheduledTask.GetType();
Logger.Info("Queueing task {0}", type.Name);
lock (_taskQueue)
{
if (scheduledTask.State == TaskState.Idle)
{
Execute(scheduledTask, new TaskExecutionOptions());
}
}
}
}
/// <summary>
/// Queues the scheduled task.
/// </summary>
@ -183,7 +227,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
lock (_taskQueue)
{
if (task.State == TaskState.Idle)
if (task.State == TaskState.Idle && !SuspendTriggers)
{
Execute(task, options);
return;
@ -273,6 +317,13 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// </summary>
private void ExecuteQueuedTasks()
{
if (SuspendTriggers)
{
return;
}
Logger.Info("ExecuteQueuedTasks");
// Execute queued tasks
lock (_taskQueue)
{

@ -66,7 +66,12 @@ namespace MediaBrowser.Common.ScheduledTasks
void Cancel(IScheduledTaskWorker task);
Task Execute(IScheduledTaskWorker task, TaskExecutionOptions options = null);
void Execute<T>()
where T : IScheduledTask;
event EventHandler<GenericEventArgs<IScheduledTaskWorker>> TaskExecuting;
event EventHandler<TaskCompletionEventArgs> TaskCompleted;
bool SuspendTriggers { get; set; }
}
}

@ -18,9 +18,9 @@ namespace MediaBrowser.Controller.Channels
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
protected override bool GetBlockUnratedValue(UserPolicy config)
public override UnratedItem GetBlockUnratedType()
{
return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
return UnratedItem.ChannelContent;
}
protected override string CreateUserDataKey()

@ -6,6 +6,7 @@ using System;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.Channels
@ -20,6 +21,11 @@ namespace MediaBrowser.Controller.Channels
return false;
}
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.ChannelContent;
}
[IgnoreDataMember]
public override bool SupportsLocalMetadata
{

@ -42,9 +42,9 @@ namespace MediaBrowser.Controller.Channels
return ExternalId;
}
protected override bool GetBlockUnratedValue(UserPolicy config)
public override UnratedItem GetBlockUnratedType()
{
return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
return UnratedItem.ChannelContent;
}
[IgnoreDataMember]

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Entities;
using System;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using System.Collections.Generic;
@ -44,10 +45,10 @@ namespace MediaBrowser.Controller.Dto
/// <summary>
/// Fills the synchronize information.
/// </summary>
/// <param name="dtos">The dtos.</param>
/// <param name="tuples">The tuples.</param>
/// <param name="options">The options.</param>
/// <param name="user">The user.</param>
void FillSyncInfo(IEnumerable<IHasSyncInfo> dtos, DtoOptions options, User user);
void FillSyncInfo(IEnumerable<Tuple<BaseItem, BaseItemDto>> tuples, DtoOptions options, User user);
/// <summary>
/// Gets the base item dto.

@ -27,9 +27,22 @@ namespace MediaBrowser.Controller.Entities.Audio
public long? Size { get; set; }
public string Container { get; set; }
public int? TotalBitrate { get; set; }
public List<string> Tags { get; set; }
public ExtraType? ExtraType { get; set; }
/// <summary>
/// Gets or sets the artist.
/// </summary>
/// <value>The artist.</value>
public List<string> Artists { get; set; }
public List<string> AlbumArtists { get; set; }
/// <summary>
/// Gets or sets the album.
/// </summary>
/// <value>The album.</value>
public string Album { get; set; }
[IgnoreDataMember]
public bool IsThemeMedia
{
@ -43,7 +56,6 @@ namespace MediaBrowser.Controller.Entities.Audio
{
Artists = new List<string>();
AlbumArtists = new List<string>();
Tags = new List<string>();
}
[IgnoreDataMember]
@ -92,14 +104,6 @@ namespace MediaBrowser.Controller.Entities.Audio
locationType != LocationType.Virtual;
}
/// <summary>
/// Gets or sets the artist.
/// </summary>
/// <value>The artist.</value>
public List<string> Artists { get; set; }
public List<string> AlbumArtists { get; set; }
[IgnoreDataMember]
public List<string> AllArtists
{
@ -114,12 +118,6 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
/// <summary>
/// Gets or sets the album.
/// </summary>
/// <value>The album.</value>
public string Album { get; set; }
[IgnoreDataMember]
public MusicAlbum AlbumEntity
{
@ -173,9 +171,9 @@ namespace MediaBrowser.Controller.Entities.Audio
return base.CreateUserDataKey();
}
protected override bool GetBlockUnratedValue(UserPolicy config)
public override UnratedItem GetBlockUnratedType()
{
return config.BlockUnratedItems.Contains(UnratedItem.Music);
return UnratedItem.Music;
}
public SongInfo GetLookupInfo()

@ -34,7 +34,7 @@ namespace MediaBrowser.Controller.Entities.Audio
{
get
{
return Parents.OfType<MusicArtist>().FirstOrDefault();
return GetParents().OfType<MusicArtist>().FirstOrDefault();
}
}
@ -114,13 +114,18 @@ namespace MediaBrowser.Controller.Entities.Audio
return config.BlockUnratedItems.Contains(UnratedItem.Music);
}
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.Music;
}
public AlbumInfo GetLookupInfo()
{
var id = GetItemLookupInfo<AlbumInfo>();
id.AlbumArtists = AlbumArtists;
var artist = Parents.OfType<MusicArtist>().FirstOrDefault();
var artist = GetParents().OfType<MusicArtist>().FirstOrDefault();
if (artist != null)
{

@ -138,6 +138,11 @@ namespace MediaBrowser.Controller.Entities.Audio
return config.BlockUnratedItems.Contains(UnratedItem.Music);
}
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.Music;
}
public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
{
var items = GetRecursiveChildren().ToList();

@ -1,5 +1,4 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Configuration;
@ -24,6 +23,7 @@ using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
using MediaBrowser.Model.LiveTv;
namespace MediaBrowser.Controller.Entities
{
@ -34,6 +34,7 @@ namespace MediaBrowser.Controller.Entities
{
protected BaseItem()
{
Tags = new List<string>();
Genres = new List<string>();
Studios = new List<string>();
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@ -44,7 +45,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// The supported image extensions
/// </summary>
public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg" };
public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".tbn", ".gif" };
public static readonly List<string> SupportedImageExtensionsList = SupportedImageExtensions.ToList();
@ -103,7 +104,8 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name
[IgnoreDataMember]
public virtual string Name
{
get
{
@ -122,14 +124,23 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[IgnoreDataMember]
public Guid Id { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is hd.
/// </summary>
/// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value>
[IgnoreDataMember]
public bool? IsHD { get; set; }
/// <summary>
/// Gets or sets the audio.
/// </summary>
/// <value>The audio.</value>
[IgnoreDataMember]
public ProgramAudio? Audio { get; set; }
/// <summary>
/// Return the id that should be used to key display prefs for this item.
/// Default is based on the type for everything except actual generic folders.
@ -149,6 +160,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the path.
/// </summary>
/// <value>The path.</value>
[IgnoreDataMember]
public virtual string Path { get; set; }
[IgnoreDataMember]
@ -173,7 +185,7 @@ namespace MediaBrowser.Controller.Entities
}
/// <summary>
/// Id of the program.
/// If this content came from an external service, the id of the content on that service
/// </summary>
[IgnoreDataMember]
public string ExternalId
@ -201,11 +213,6 @@ namespace MediaBrowser.Controller.Entities
}
}
public virtual bool IsHiddenFromUser(User user)
{
return false;
}
[IgnoreDataMember]
public virtual bool IsOwnedItem
{
@ -325,12 +332,14 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the date created.
/// </summary>
/// <value>The date created.</value>
[IgnoreDataMember]
public DateTime DateCreated { get; set; }
/// <summary>
/// Gets or sets the date modified.
/// </summary>
/// <value>The date modified.</value>
[IgnoreDataMember]
public DateTime DateModified { get; set; }
public DateTime DateLastSaved { get; set; }
@ -407,6 +416,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the name of the forced sort.
/// </summary>
/// <value>The name of the forced sort.</value>
[IgnoreDataMember]
public string ForcedSortName
{
get { return _forcedSortName; }
@ -447,10 +457,7 @@ namespace MediaBrowser.Controller.Entities
{
var idString = Id.ToString("N");
if (ConfigurationManager.Configuration.EnableLibraryMetadataSubFolder)
{
basePath = System.IO.Path.Combine(basePath, "library");
}
basePath = System.IO.Path.Combine(basePath, "library");
return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString);
}
@ -493,6 +500,7 @@ namespace MediaBrowser.Controller.Entities
return sortable;
}
[IgnoreDataMember]
public Guid ParentId { get; set; }
/// <summary>
@ -502,15 +510,7 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public Folder Parent
{
get
{
if (ParentId != Guid.Empty)
{
return LibraryManager.GetItemById(ParentId) as Folder;
}
return null;
}
get { return GetParent() as Folder; }
set
{
@ -525,16 +525,28 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public IEnumerable<Folder> Parents
{
get
get { return GetParents().OfType<Folder>(); }
}
public BaseItem GetParent()
{
if (ParentId != Guid.Empty)
{
var parent = Parent;
return LibraryManager.GetItemById(ParentId);
}
while (parent != null)
{
yield return parent;
return null;
}
parent = parent.Parent;
}
public IEnumerable<BaseItem> GetParents()
{
var parent = GetParent();
while (parent != null)
{
yield return parent;
parent = parent.GetParent();
}
}
@ -546,19 +558,20 @@ namespace MediaBrowser.Controller.Entities
public T FindParent<T>()
where T : Folder
{
return Parents.OfType<T>().FirstOrDefault();
return GetParents().OfType<T>().FirstOrDefault();
}
[IgnoreDataMember]
public virtual BaseItem DisplayParent
{
get { return Parent; }
get { return GetParent(); }
}
/// <summary>
/// When the item first debuted. For movies this could be premiere date, episodes would be first aired
/// </summary>
/// <value>The premiere date.</value>
[IgnoreDataMember]
public DateTime? PremiereDate { get; set; }
/// <summary>
@ -572,31 +585,35 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the display type of the media.
/// </summary>
/// <value>The display type of the media.</value>
[IgnoreDataMember]
public string DisplayMediaType { get; set; }
/// <summary>
/// Gets or sets the official rating.
/// </summary>
/// <value>The official rating.</value>
[IgnoreDataMember]
public string OfficialRating { get; set; }
/// <summary>
/// Gets or sets the official rating description.
/// </summary>
/// <value>The official rating description.</value>
[IgnoreDataMember]
public string OfficialRatingDescription { get; set; }
/// <summary>
/// Gets or sets the custom rating.
/// </summary>
/// <value>The custom rating.</value>
//[IgnoreDataMember]
[IgnoreDataMember]
public string CustomRating { get; set; }
/// <summary>
/// Gets or sets the overview.
/// </summary>
/// <value>The overview.</value>
[IgnoreDataMember]
public string Overview { get; set; }
/// <summary>
@ -609,37 +626,48 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the genres.
/// </summary>
/// <value>The genres.</value>
[IgnoreDataMember]
public List<string> Genres { get; set; }
/// <summary>
/// Gets or sets the tags.
/// </summary>
/// <value>The tags.</value>
public List<string> Tags { get; set; }
/// <summary>
/// Gets or sets the home page URL.
/// </summary>
/// <value>The home page URL.</value>
[IgnoreDataMember]
public string HomePageUrl { get; set; }
/// <summary>
/// Gets or sets the community rating.
/// </summary>
/// <value>The community rating.</value>
//[IgnoreDataMember]
[IgnoreDataMember]
public float? CommunityRating { get; set; }
/// <summary>
/// Gets or sets the community rating vote count.
/// </summary>
/// <value>The community rating vote count.</value>
[IgnoreDataMember]
public int? VoteCount { get; set; }
/// <summary>
/// Gets or sets the run time ticks.
/// </summary>
/// <value>The run time ticks.</value>
[IgnoreDataMember]
public long? RunTimeTicks { get; set; }
/// <summary>
/// Gets or sets the production year.
/// </summary>
/// <value>The production year.</value>
[IgnoreDataMember]
public int? ProductionYear { get; set; }
/// <summary>
@ -647,19 +675,34 @@ namespace MediaBrowser.Controller.Entities
/// This could be episode number, album track number, etc.
/// </summary>
/// <value>The index number.</value>
//[IgnoreDataMember]
[IgnoreDataMember]
public int? IndexNumber { get; set; }
/// <summary>
/// For an episode this could be the season number, or for a song this could be the disc number.
/// </summary>
/// <value>The parent index number.</value>
[IgnoreDataMember]
public int? ParentIndexNumber { get; set; }
[IgnoreDataMember]
public virtual string OfficialRatingForComparison
public string OfficialRatingForComparison
{
get { return OfficialRating; }
get
{
if (!string.IsNullOrWhiteSpace(OfficialRating))
{
return OfficialRating;
}
var parent = DisplayParent;
if (parent != null)
{
return parent.OfficialRatingForComparison;
}
return null;
}
}
[IgnoreDataMember]
@ -721,21 +764,21 @@ namespace MediaBrowser.Controller.Entities
return LibraryManager.ResolvePaths(files, directoryService, null)
.OfType<Audio.Audio>()
.Select(audio =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio;
if (dbItem != null)
{
audio = dbItem;
}
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio;
audio.ExtraType = ExtraType.ThemeSong;
if (dbItem != null)
{
audio = dbItem;
}
audio.ExtraType = ExtraType.ThemeSong;
return audio;
return audio;
// Sort them so that the list can be easily compared for changes
}).OrderBy(i => i.Path).ToList();
// Sort them so that the list can be easily compared for changes
}).OrderBy(i => i.Path).ToList();
}
/// <summary>
@ -751,21 +794,21 @@ namespace MediaBrowser.Controller.Entities
return LibraryManager.ResolvePaths(files, directoryService, null)
.OfType<Video>()
.Select(item =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(item.Id) as Video;
if (dbItem != null)
{
item = dbItem;
}
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(item.Id) as Video;
item.ExtraType = ExtraType.ThemeVideo;
if (dbItem != null)
{
item = dbItem;
}
return item;
item.ExtraType = ExtraType.ThemeVideo;
// Sort them so that the list can be easily compared for changes
}).OrderBy(i => i.Path).ToList();
return item;
// Sort them so that the list can be easily compared for changes
}).OrderBy(i => i.Path).ToList();
}
public Task RefreshMetadata(CancellationToken cancellationToken)
@ -821,7 +864,7 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
protected virtual bool SupportsOwnedItems
{
get { return IsFolder || Parent != null; }
get { return IsFolder || GetParent() != null; }
}
[IgnoreDataMember]
@ -846,7 +889,7 @@ namespace MediaBrowser.Controller.Entities
var localTrailersChanged = false;
if (LocationType == LocationType.FileSystem && Parent != null)
if (LocationType == LocationType.FileSystem && GetParent() != null)
{
var hasThemeMedia = this as IHasThemeMedia;
if (hasThemeMedia != null)
@ -1008,7 +1051,7 @@ namespace MediaBrowser.Controller.Entities
if (string.IsNullOrWhiteSpace(lang))
{
lang = Parents
lang = GetParents()
.Select(i => i.PreferredMetadataLanguage)
.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
}
@ -1038,7 +1081,7 @@ namespace MediaBrowser.Controller.Entities
if (string.IsNullOrWhiteSpace(lang))
{
lang = Parents
lang = GetParents()
.Select(i => i.PreferredMetadataCountryCode)
.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
}
@ -1119,6 +1162,23 @@ namespace MediaBrowser.Controller.Entities
}
public int? GetParentalRatingValue()
{
var rating = CustomRating;
if (string.IsNullOrWhiteSpace(rating))
{
rating = OfficialRating;
}
if (string.IsNullOrWhiteSpace(rating))
{
return null;
}
return LocalizationManager.GetRatingLevel(rating);
}
public int? GetInheritedParentalRatingValue()
{
var rating = CustomRatingForComparison;
@ -1156,6 +1216,11 @@ namespace MediaBrowser.Controller.Entities
return true;
}
public virtual UnratedItem GetBlockUnratedType()
{
return UnratedItem.Other;
}
/// <summary>
/// Gets the block unrated value.
/// </summary>
@ -1174,7 +1239,7 @@ namespace MediaBrowser.Controller.Entities
return false;
}
return config.BlockUnratedItems.Contains(UnratedItem.Other);
return config.BlockUnratedItems.Contains(GetBlockUnratedType());
}
/// <summary>
@ -1206,14 +1271,14 @@ namespace MediaBrowser.Controller.Entities
return false;
}
if (Parents.Any(i => !i.IsVisible(user)))
if (GetParents().Any(i => !i.IsVisible(user)))
{
return false;
}
if (checkFolders)
{
var topParent = Parents.LastOrDefault() ?? this;
var topParent = GetParents().LastOrDefault() ?? this;
if (string.IsNullOrWhiteSpace(topParent.Path))
{
@ -1307,15 +1372,6 @@ namespace MediaBrowser.Controller.Entities
return null;
}
/// <summary>
/// Adds a person to the item
/// </summary>
/// <param name="person">The person.</param>
/// <exception cref="System.ArgumentNullException"></exception>
public void AddPerson(PersonInfo person)
{
}
/// <summary>
/// Adds a studio to the item
/// </summary>
@ -1779,8 +1835,8 @@ namespace MediaBrowser.Controller.Entities
ProviderIds = ProviderIds,
IndexNumber = IndexNumber,
ParentIndexNumber = ParentIndexNumber,
Year = ProductionYear,
PremiereDate = PremiereDate
Year = ProductionYear,
PremiereDate = PremiereDate
};
}
@ -1875,5 +1931,59 @@ namespace MediaBrowser.Controller.Entities
DateLastSaved.Ticks.ToString(CultureInfo.InvariantCulture)
};
}
public virtual IEnumerable<Guid> GetAncestorIds()
{
return GetParents().Select(i => i.Id).Concat(LibraryManager.GetCollectionFolders(this).Select(i => i.Id));
}
public BaseItem GetTopParent()
{
if (IsTopParent)
{
return this;
}
return GetParents().FirstOrDefault(i => i.IsTopParent);
}
[IgnoreDataMember]
public virtual bool IsTopParent
{
get
{
if (GetParent() is AggregateFolder || this is Channel || this is BasePluginFolder)
{
return true;
}
var view = this as UserView;
if (view != null && string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
{
return true;
}
return false;
}
}
[IgnoreDataMember]
public virtual bool SupportsAncestors
{
get
{
return true;
}
}
public virtual IEnumerable<Guid> GetIdsForAncestorQuery()
{
return new[] { Id };
}
public virtual Task Delete(DeleteOptions options)
{
return LibraryManager.DeleteItem(this, options);
}
}
}
}

@ -17,19 +17,8 @@ namespace MediaBrowser.Controller.Entities
}
}
/// <summary>
/// Gets or sets the tags.
/// </summary>
/// <value>The tags.</value>
public List<string> Tags { get; set; }
public string SeriesName { get; set; }
public Book()
{
Tags = new List<string>();
}
public override bool CanDownload()
{
var locationType = LocationType;
@ -37,9 +26,9 @@ namespace MediaBrowser.Controller.Entities
locationType != LocationType.Virtual;
}
protected override bool GetBlockUnratedValue(UserPolicy config)
public override UnratedItem GetBlockUnratedType()
{
return config.BlockUnratedItems.Contains(UnratedItem.Book);
return UnratedItem.Book;
}
public BookInfo GetLookupInfo()
@ -48,7 +37,7 @@ namespace MediaBrowser.Controller.Entities
if (string.IsNullOrEmpty(SeriesName))
{
info.SeriesName = Parents.Select(i => i.Name).FirstOrDefault();
info.SeriesName = GetParents().Select(i => i.Name).FirstOrDefault();
}
else
{

@ -181,9 +181,7 @@ namespace MediaBrowser.Controller.Entities
}
private List<LinkedChild> GetLinkedChildrenInternal()
{
return LibraryManager.RootFolder.Children
.OfType<Folder>()
.Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase))
return GetPhysicalParents()
.SelectMany(c => c.LinkedChildren)
.ToList();
}
@ -199,11 +197,14 @@ namespace MediaBrowser.Controller.Entities
private IEnumerable<BaseItem> GetActualChildren()
{
return
LibraryManager.RootFolder.Children
return GetPhysicalParents().SelectMany(c => c.Children);
}
public IEnumerable<Folder> GetPhysicalParents()
{
return LibraryManager.RootFolder.Children
.OfType<Folder>()
.Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase))
.SelectMany(c => c.Children);
.Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase));
}
[IgnoreDataMember]

@ -28,7 +28,6 @@ namespace MediaBrowser.Controller.Entities
public List<Guid> ThemeSongIds { get; set; }
public List<Guid> ThemeVideoIds { get; set; }
public List<string> Tags { get; set; }
public Folder()
{
@ -36,7 +35,6 @@ namespace MediaBrowser.Controller.Entities
ThemeSongIds = new List<Guid>();
ThemeVideoIds = new List<Guid>();
Tags = new List<string>();
}
[IgnoreDataMember]
@ -151,7 +149,15 @@ namespace MediaBrowser.Controller.Entities
await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
if (!EnableNewFolderQuerying())
{
await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
}
}
private static bool EnableNewFolderQuerying()
{
return ConfigurationManager.Configuration.MigrationVersion >= 1;
}
protected void AddChildrenInternal(IEnumerable<BaseItem> children)
@ -196,21 +202,6 @@ namespace MediaBrowser.Controller.Entities
}
}
[IgnoreDataMember]
public override string OfficialRatingForComparison
{
get
{
// Never want folders to be blocked by "BlockNotRated"
if (this is Series)
{
return base.OfficialRatingForComparison;
}
return !string.IsNullOrWhiteSpace(base.OfficialRatingForComparison) ? base.OfficialRatingForComparison : "None";
}
}
/// <summary>
/// Removes the child.
/// </summary>
@ -224,7 +215,12 @@ namespace MediaBrowser.Controller.Entities
item.SetParent(null);
return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken);
if (!EnableNewFolderQuerying())
{
return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken);
}
return Task.FromResult(true);
}
/// <summary>
@ -457,32 +453,25 @@ namespace MediaBrowser.Controller.Entities
{
BaseItem currentChild;
if (currentChildren.TryGetValue(child.Id, out currentChild))
if (currentChildren.TryGetValue(child.Id, out currentChild) && IsValidFromResolver(currentChild, child))
{
if (IsValidFromResolver(currentChild, child))
var currentChildLocationType = currentChild.LocationType;
if (currentChildLocationType != LocationType.Remote &&
currentChildLocationType != LocationType.Virtual)
{
var currentChildLocationType = currentChild.LocationType;
if (currentChildLocationType != LocationType.Remote &&
currentChildLocationType != LocationType.Virtual)
{
currentChild.DateModified = child.DateModified;
}
await UpdateIsOffline(currentChild, false).ConfigureAwait(false);
validChildren.Add(currentChild);
}
else
{
newItems.Add(child);
validChildren.Add(child);
currentChild.DateModified = child.DateModified;
}
await UpdateIsOffline(currentChild, false).ConfigureAwait(false);
validChildren.Add(currentChild);
continue;
}
else
{
// Brand new item - needs to be added
newItems.Add(child);
validChildren.Add(child);
}
// Brand new item - needs to be added
child.SetParent(this);
newItems.Add(child);
validChildren.Add(child);
}
// If any items were added or removed....
@ -508,7 +497,6 @@ namespace MediaBrowser.Controller.Entities
}
else
{
await UpdateIsOffline(item, false).ConfigureAwait(false);
actualRemovals.Add(item);
}
}
@ -519,6 +507,11 @@ namespace MediaBrowser.Controller.Entities
foreach (var item in actualRemovals)
{
Logger.Debug("Removed item: " + item.Path);
item.SetParent(null);
item.IsOffline = false;
await LibraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = false }).ConfigureAwait(false);
LibraryManager.ReportItemRemoved(item);
}
}
@ -527,7 +520,10 @@ namespace MediaBrowser.Controller.Entities
AddChildrenInternal(newItems);
await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
if (!EnableNewFolderQuerying())
{
await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
}
}
}
@ -721,7 +717,7 @@ namespace MediaBrowser.Controller.Entities
return true;
}
return ContainsPath(LibraryManager.GetVirtualFolders(), originalPath);
return false;
}
/// <summary>
@ -757,19 +753,16 @@ namespace MediaBrowser.Controller.Entities
/// <returns>IEnumerable{BaseItem}.</returns>
protected IEnumerable<BaseItem> GetCachedChildren()
{
if (ConfigurationManager.Configuration.DisableStartupScan)
if (EnableNewFolderQuerying())
{
return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
//return ItemRepository.GetItems(new InternalItemsQuery
//{
// ParentId = Id
return ItemRepository.GetItemList(new InternalItemsQuery
{
ParentId = Id
//}).Items.Select(RetrieveChild).Where(i => i != null);
}
else
{
return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
}).Select(RetrieveChild).Where(i => i != null);
}
return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
}
private BaseItem RetrieveChild(BaseItem child)
@ -832,19 +825,7 @@ namespace MediaBrowser.Controller.Entities
return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager);
}
/// <summary>
/// Gets allowed children of an item
/// </summary>
/// <param name="user">The user.</param>
/// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
/// <returns>IEnumerable{BaseItem}.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
public virtual IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
{
return GetChildren(user, includeLinkedChildren, false);
}
internal IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren, bool includeHidden)
{
if (user == null)
{
@ -856,7 +837,7 @@ namespace MediaBrowser.Controller.Entities
var result = new Dictionary<Guid, BaseItem>();
AddChildren(user, includeLinkedChildren, result, includeHidden, false, null);
AddChildren(user, includeLinkedChildren, result, false, null);
return result.Values;
}
@ -872,29 +853,25 @@ namespace MediaBrowser.Controller.Entities
/// <param name="user">The user.</param>
/// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
/// <param name="result">The result.</param>
/// <param name="includeHidden">if set to <c>true</c> [include hidden].</param>
/// <param name="recursive">if set to <c>true</c> [recursive].</param>
/// <param name="filter">The filter.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool includeHidden, bool recursive, Func<BaseItem, bool> filter)
private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool recursive, Func<BaseItem, bool> filter)
{
foreach (var child in GetEligibleChildrenForRecursiveChildren(user))
{
if (child.IsVisible(user))
{
if (includeHidden || !child.IsHiddenFromUser(user))
if (filter == null || filter(child))
{
if (filter == null || filter(child))
{
result[child.Id] = child;
}
result[child.Id] = child;
}
if (recursive && child.IsFolder)
{
var folder = (Folder)child;
folder.AddChildren(user, includeLinkedChildren, result, includeHidden, true, filter);
folder.AddChildren(user, includeLinkedChildren, result, true, filter);
}
}
}
@ -935,7 +912,7 @@ namespace MediaBrowser.Controller.Entities
var result = new Dictionary<Guid, BaseItem>();
AddChildren(user, true, result, false, true, filter);
AddChildren(user, true, result, true, filter);
return result.Values;
}
@ -1184,6 +1161,7 @@ namespace MediaBrowser.Controller.Entities
Recursive = true,
IsFolder = false,
IsUnaired = false
};
if (!user.Configuration.DisplayMissingEpisodes)
@ -1322,4 +1300,4 @@ namespace MediaBrowser.Controller.Entities
}
}
}
}
}

@ -21,7 +21,6 @@ namespace MediaBrowser.Controller.Entities
RemoteTrailerIds = new List<Guid>();
ThemeSongIds = new List<Guid>();
ThemeVideoIds = new List<Guid>();
Tags = new List<string>();
}
public List<Guid> LocalTrailerIds { get; set; }
@ -34,12 +33,6 @@ namespace MediaBrowser.Controller.Entities
locationType != LocationType.Virtual;
}
/// <summary>
/// Gets or sets the tags.
/// </summary>
/// <value>The tags.</value>
public List<string> Tags { get; set; }
/// <summary>
/// Gets or sets the remote trailers.
/// </summary>
@ -105,9 +98,9 @@ namespace MediaBrowser.Controller.Entities
return base.GetDeletePaths();
}
protected override bool GetBlockUnratedValue(UserPolicy config)
public override UnratedItem GetBlockUnratedType()
{
return config.BlockUnratedItems.Contains(UnratedItem.Game);
return UnratedItem.Game;
}
public GameInfo GetLookupInfo()

@ -50,6 +50,11 @@ namespace MediaBrowser.Controller.Entities
return false;
}
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.Game;
}
public GameSystemInfo GetLookupInfo()
{
var id = GetItemLookupInfo<GameSystemInfo>();

@ -16,6 +16,11 @@ namespace MediaBrowser.Controller.Entities
IEnumerable<string> PhysicalLocations { get; }
}
public interface ISupportsUserSpecificView
{
bool EnableUserSpecificView { get; }
}
public static class CollectionFolderExtensions
{
public static string GetViewType(this ICollectionFolder folder, User user)

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Entities
{
public interface IHiddenFromDisplay
{
/// <summary>
/// Determines whether the specified user is hidden.
/// </summary>
/// <param name="user">The user.</param>
/// <returns><c>true</c> if the specified user is hidden; otherwise, <c>false</c>.</returns>
bool IsHiddenFromUser(User user);
}
}

@ -1,6 +1,7 @@
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Controller.Entities
{
@ -26,10 +27,12 @@ namespace MediaBrowser.Controller.Entities
public bool? IsLiked { get; set; }
public bool? IsPlayed { get; set; }
public bool? IsResumable { get; set; }
public bool? IncludeItemsByName { get; set; }
public string[] MediaTypes { get; set; }
public string[] IncludeItemTypes { get; set; }
public string[] ExcludeItemTypes { get; set; }
public string[] ExcludeTags { get; set; }
public string[] Genres { get; set; }
public bool? IsMissing { get; set; }
@ -69,12 +72,15 @@ namespace MediaBrowser.Controller.Entities
public string[] Studios { get; set; }
public string[] StudioIds { get; set; }
public string[] GenreIds { get; set; }
public ImageType[] ImageTypes { get; set; }
public VideoType[] VideoTypes { get; set; }
public UnratedItem[] BlockUnratedItems { get; set; }
public int[] Years { get; set; }
public string[] Tags { get; set; }
public string[] OfficialRatings { get; set; }
public DateTime? MinPremiereDate { get; set; }
public DateTime? MinStartDate { get; set; }
public DateTime? MaxStartDate { get; set; }
public DateTime? MinEndDate { get; set; }
@ -87,6 +93,7 @@ namespace MediaBrowser.Controller.Entities
public int? MinPlayers { get; set; }
public int? MaxPlayers { get; set; }
public int? MinIndexNumber { get; set; }
public double? MinCriticRating { get; set; }
public double? MinCommunityRating { get; set; }
@ -101,9 +108,14 @@ namespace MediaBrowser.Controller.Entities
public LocationType? LocationType { get; set; }
public Guid? ParentId { get; set; }
public string[] AncestorIds { get; set; }
public string[] TopParentIds { get; set; }
public LocationType[] ExcludeLocationTypes { get; set; }
public InternalItemsQuery()
{
BlockUnratedItems = new UnratedItem[] { };
Tags = new string[] { };
OfficialRatings = new string[] { };
SortBy = new string[] { };
@ -113,6 +125,7 @@ namespace MediaBrowser.Controller.Entities
Genres = new string[] { };
Studios = new string[] { };
StudioIds = new string[] { };
GenreIds = new string[] { };
ImageTypes = new ImageType[] { };
VideoTypes = new VideoType[] { };
Years = new int[] { };
@ -120,6 +133,29 @@ namespace MediaBrowser.Controller.Entities
PersonIds = new string[] { };
ChannelIds = new string[] { };
ItemIds = new string[] { };
AncestorIds = new string[] { };
TopParentIds = new string[] { };
ExcludeTags = new string[] { };
ExcludeLocationTypes = new LocationType[] { };
}
public InternalItemsQuery(User user)
: this()
{
if (user != null)
{
var policy = user.Policy;
MaxParentalRating = policy.MaxParentalRating;
if (policy.MaxParentalRating.HasValue)
{
BlockUnratedItems = policy.BlockUnratedItems;
}
ExcludeTags = policy.BlockedTags;
User = user;
}
}
}
}

@ -8,15 +8,13 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Entities.Movies
{
/// <summary>
/// Class BoxSet
/// </summary>
public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer, IHasShares
public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IHasShares
{
public List<Share> Shares { get; set; }
@ -65,6 +63,11 @@ namespace MediaBrowser.Controller.Entities.Movies
return config.BlockUnratedItems.Contains(UnratedItem.Movie);
}
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.Movie;
}
[IgnoreDataMember]
public override bool IsPreSorted
{
@ -154,34 +157,6 @@ namespace MediaBrowser.Controller.Entities.Movies
return GetItemLookupInfo<BoxSetInfo>();
}
public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
{
// Refresh bottom up, children first, then the boxset
// By then hopefully the movies within will have Tmdb collection values
var items = GetRecursiveChildren().ToList();
var totalItems = items.Count;
var numComplete = 0;
// Refresh songs
foreach (var item in items)
{
cancellationToken.ThrowIfCancellationRequested();
await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
numComplete++;
double percent = numComplete;
percent /= totalItems;
progress.Report(percent * 100);
}
// Refresh current item
await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
progress.Report(100);
}
public override bool IsVisible(User user)
{
var userId = user.Id.ToString("N");

@ -130,7 +130,7 @@ namespace MediaBrowser.Controller.Entities.Movies
// Must have a parent to have special features
// In other words, it must be part of the Parent/Child tree
if (LocationType == LocationType.FileSystem && Parent != null && !IsInMixedFolder)
if (LocationType == LocationType.FileSystem && GetParent() != null && !IsInMixedFolder)
{
var specialFeaturesChanged = await RefreshSpecialFeatures(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
@ -159,9 +159,9 @@ namespace MediaBrowser.Controller.Entities.Movies
return itemsChanged;
}
protected override bool GetBlockUnratedValue(UserPolicy config)
public override UnratedItem GetBlockUnratedType()
{
return config.BlockUnratedItems.Contains(UnratedItem.Movie);
return UnratedItem.Movie;
}
public MovieInfo GetLookupInfo()

@ -56,9 +56,9 @@ namespace MediaBrowser.Controller.Entities
return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.CreateUserDataKey();
}
protected override bool GetBlockUnratedValue(UserPolicy config)
public override UnratedItem GetBlockUnratedType()
{
return config.BlockUnratedItems.Contains(UnratedItem.Music);
return UnratedItem.Music;
}
public MusicVideoInfo GetLookupInfo()

@ -102,6 +102,15 @@ namespace MediaBrowser.Controller.Entities
return false;
}
}
[IgnoreDataMember]
public override bool SupportsAncestors
{
get
{
return false;
}
}
}
/// <summary>

@ -9,12 +9,10 @@ namespace MediaBrowser.Controller.Entities
{
public class Photo : BaseItem, IHasTags, IHasTaglines
{
public List<string> Tags { get; set; }
public List<string> Taglines { get; set; }
public Photo()
{
Tags = new List<string>();
Taglines = new List<string>();
}
@ -51,10 +49,15 @@ namespace MediaBrowser.Controller.Entities
{
get
{
return Parents.OfType<PhotoAlbum>().FirstOrDefault();
return GetParents().OfType<PhotoAlbum>().FirstOrDefault();
}
}
public override bool CanDownload()
{
return true;
}
public int? Width { get; set; }
public int? Height { get; set; }
public string CameraMake { get; set; }
@ -70,10 +73,5 @@ namespace MediaBrowser.Controller.Entities
public double? Longitude { get; set; }
public double? Altitude { get; set; }
public int? IsoSpeedRating { get; set; }
protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Other);
}
}
}

@ -9,8 +9,9 @@ using System.Threading.Tasks;
namespace MediaBrowser.Controller.Entities
{
public class PhotoAlbum : Folder, IMetadataContainer
public class PhotoAlbum : Folder
{
[IgnoreDataMember]
public override bool SupportsLocalMetadata
{
get
@ -32,31 +33,5 @@ namespace MediaBrowser.Controller.Entities
{
return config.BlockUnratedItems.Contains(UnratedItem.Other);
}
public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
{
var items = GetRecursiveChildren().ToList();
var totalItems = items.Count;
var numComplete = 0;
// Refresh songs
foreach (var item in items)
{
cancellationToken.ThrowIfCancellationRequested();
await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
numComplete++;
double percent = numComplete;
percent /= totalItems;
progress.Report(percent * 100);
}
// Refresh current item
await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
progress.Report(100);
}
}
}

@ -10,13 +10,6 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public class Studio : BaseItem, IItemByName, IHasTags
{
public List<string> Tags { get; set; }
public Studio()
{
Tags = new List<string>();
}
/// <summary>
/// Gets the user data key.
/// </summary>

@ -95,7 +95,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
get
{
return Season ?? Parent;
return Season ?? GetParent();
}
}
@ -115,19 +115,6 @@ namespace MediaBrowser.Controller.Entities.TV
return base.CreateUserDataKey();
}
/// <summary>
/// Our rating comes from our series
/// </summary>
[IgnoreDataMember]
public override string OfficialRatingForComparison
{
get
{
var series = Series;
return series != null ? series.OfficialRatingForComparison : base.OfficialRatingForComparison;
}
}
/// <summary>
/// This Episode's Series Instance
/// </summary>
@ -265,14 +252,28 @@ namespace MediaBrowser.Controller.Entities.TV
}
}
public override IEnumerable<Guid> GetAncestorIds()
{
var list = base.GetAncestorIds().ToList();
var seasonId = SeasonId;
if (seasonId.HasValue && !list.Contains(seasonId.Value))
{
list.Add(seasonId.Value);
}
return list;
}
public override IEnumerable<string> GetDeletePaths()
{
return new[] { Path };
}
protected override bool GetBlockUnratedValue(UserPolicy config)
public override UnratedItem GetBlockUnratedType()
{
return config.BlockUnratedItems.Contains(UnratedItem.Series);
return UnratedItem.Series;
}
public EpisodeInfo GetLookupInfo()

@ -6,6 +6,7 @@ using MoreLinq;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Controller.Entities.TV
{
@ -32,7 +33,7 @@ namespace MediaBrowser.Controller.Entities.TV
[IgnoreDataMember]
public override BaseItem DisplayParent
{
get { return Series ?? Parent; }
get { return Series ?? GetParent(); }
}
// Genre, Rating and Stuido will all be the same
@ -87,19 +88,6 @@ namespace MediaBrowser.Controller.Entities.TV
}
}
/// <summary>
/// Our rating comes from our series
/// </summary>
[IgnoreDataMember]
public override string OfficialRatingForComparison
{
get
{
var series = Series;
return series != null ? series.OfficialRatingForComparison : base.OfficialRatingForComparison;
}
}
/// <summary>
/// Creates the name of the sort.
/// </summary>
@ -234,6 +222,11 @@ namespace MediaBrowser.Controller.Entities.TV
return false;
}
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.Series;
}
[IgnoreDataMember]
public string SeriesName
{

@ -333,6 +333,11 @@ namespace MediaBrowser.Controller.Entities.TV
return config.BlockUnratedItems.Contains(UnratedItem.Series);
}
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.Series;
}
public SeriesInfo GetLookupInfo()
{
var info = GetItemLookupInfo<SeriesInfo>();

@ -73,7 +73,7 @@ namespace MediaBrowser.Controller.Entities
get
{
// Local trailers are not part of children
return Parent == null;
return GetParent() == null;
}
}
@ -97,9 +97,9 @@ namespace MediaBrowser.Controller.Entities
return base.CreateUserDataKey();
}
protected override bool GetBlockUnratedValue(UserPolicy config)
public override UnratedItem GetBlockUnratedType()
{
return config.BlockUnratedItems.Contains(UnratedItem.Trailer);
return UnratedItem.Trailer;
}
public TrailerInfo GetLookupInfo()

@ -20,7 +20,6 @@ namespace MediaBrowser.Controller.Entities
{
public static IUserManager UserManager { get; set; }
public static IXmlSerializer XmlSerializer { get; set; }
public bool EnableUserViews { get; set; }
/// <summary>
/// From now on all user paths will be Id-based.
@ -58,6 +57,26 @@ namespace MediaBrowser.Controller.Entities
}
}
private string _name;
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public override string Name
{
get
{
return _name;
}
set
{
_name = value;
// lazy load this again
SortName = null;
}
}
/// <summary>
/// Returns the folder containing the item.
/// If the item is a folder, it returns the folder itself

@ -55,13 +55,21 @@ namespace MediaBrowser.Controller.Entities
}
}
protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
{
var list = base.GetEligibleChildrenForRecursiveChildren(user).ToList();
list.AddRange(LibraryManager.RootFolder.VirtualChildren);
return list;
}
/// <summary>
/// Get the children of this folder from the actual file system
/// </summary>
/// <returns>IEnumerable{BaseItem}.</returns>
protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
{
return base.GetNonCachedChildren(directoryService).Concat(LibraryManager.RootFolder.VirtualChildren);
return base.GetNonCachedChildren(directoryService);
}
public override bool BeforeMetadataRefresh()

@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using System.Linq;
namespace MediaBrowser.Controller.Entities
{
@ -16,7 +17,7 @@ namespace MediaBrowser.Controller.Entities
public Guid DisplayParentId { get; set; }
public Guid? UserId { get; set; }
public static ITVSeriesManager TVSeriesManager;
public static IPlaylistManager PlaylistManager;
@ -24,7 +25,26 @@ namespace MediaBrowser.Controller.Entities
{
return true;
}
public override IEnumerable<Guid> GetIdsForAncestorQuery()
{
var list = new List<Guid>();
if (DisplayParentId != Guid.Empty)
{
list.Add(DisplayParentId);
}
else if (ParentId != Guid.Empty)
{
list.Add(ParentId);
}
else
{
list.Add(Id);
}
return list;
}
public override Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query)
{
var parent = this as Folder;
@ -81,16 +101,11 @@ namespace MediaBrowser.Controller.Entities
return GetChildren(user, false);
}
public static bool IsExcludedFromGrouping(Folder folder)
public static bool IsUserSpecific(Folder folder)
{
var standaloneTypes = new List<string>
{
CollectionType.Books,
CollectionType.HomeVideos,
CollectionType.Photos,
CollectionType.Playlists,
CollectionType.BoxSets,
CollectionType.MusicVideos
CollectionType.Playlists
};
var collectionFolder = folder as ICollectionFolder;
@ -100,25 +115,63 @@ namespace MediaBrowser.Controller.Entities
return false;
}
var supportsUserSpecific = folder as ISupportsUserSpecificView;
if (supportsUserSpecific != null && supportsUserSpecific.EnableUserSpecificView)
{
return true;
}
return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty);
}
public static bool IsUserSpecific(Folder folder)
public static bool IsEligibleForGrouping(Folder folder)
{
var standaloneTypes = new List<string>
{
CollectionType.Playlists,
CollectionType.BoxSets
var collectionFolder = folder as ICollectionFolder;
return collectionFolder != null && IsEligibleForGrouping(collectionFolder.CollectionType);
}
public static bool IsEligibleForGrouping(string viewType)
{
var types = new[]
{
CollectionType.Movies,
CollectionType.TvShows,
string.Empty
};
var collectionFolder = folder as ICollectionFolder;
return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
}
if (collectionFolder == null)
{
return false;
}
public static bool IsEligibleForEnhancedView(string viewType)
{
var types = new[]
{
CollectionType.Movies,
CollectionType.TvShows
};
return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty);
return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
}
public static bool EnableOriginalFolder(string viewType)
{
var types = new[]
{
CollectionType.Games,
CollectionType.Books,
CollectionType.MusicVideos,
CollectionType.HomeVideos,
CollectionType.Photos,
CollectionType.Music,
CollectionType.BoxSets
};
return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
}
protected override Task ValidateChildrenInternal(IProgress<double> progress, System.Threading.CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, Providers.MetadataRefreshOptions refreshOptions, Providers.IDirectoryService directoryService)
{
return Task.FromResult(true);
}
[IgnoreDataMember]

@ -120,59 +120,34 @@ namespace MediaBrowser.Controller.Entities
return await GetLiveTvView(queryParent, user, query).ConfigureAwait(false);
}
case CollectionType.Photos:
case CollectionType.Books:
case CollectionType.HomeVideos:
case CollectionType.Games:
case CollectionType.MusicVideos:
{
if (query.Recursive)
{
return GetResult(queryParent.GetRecursiveChildren(user, true), queryParent, query);
}
return GetResult(queryParent.GetChildren(user, true), queryParent, query);
}
case CollectionType.Folders:
return GetResult(user.RootFolder.GetChildren(user, true), queryParent, query);
case CollectionType.Games:
return await GetGameView(user, queryParent, query).ConfigureAwait(false);
case CollectionType.Playlists:
return await GetPlaylistsView(queryParent, user, query).ConfigureAwait(false);
case CollectionType.BoxSets:
return await GetBoxsetView(queryParent, user, query).ConfigureAwait(false);
case CollectionType.Photos:
return await GetPhotosView(queryParent, user, query).ConfigureAwait(false);
case CollectionType.TvShows:
return await GetTvView(queryParent, user, query).ConfigureAwait(false);
case CollectionType.Music:
return await GetMusicFolders(queryParent, user, query).ConfigureAwait(false);
case CollectionType.Movies:
return await GetMovieFolders(queryParent, user, query).ConfigureAwait(false);
case SpecialFolder.MusicGenres:
return await GetMusicGenres(queryParent, user, query).ConfigureAwait(false);
case SpecialFolder.MusicGenre:
return await GetMusicGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false);
case SpecialFolder.GameGenres:
return await GetGameGenres(queryParent, user, query).ConfigureAwait(false);
case SpecialFolder.GameGenre:
return await GetGameGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false);
case SpecialFolder.GameSystems:
return GetGameSystems(queryParent, user, query);
case SpecialFolder.LatestGames:
return GetLatestGames(queryParent, user, query);
case SpecialFolder.RecentlyPlayedGames:
return GetRecentlyPlayedGames(queryParent, user, query);
case SpecialFolder.GameFavorites:
return GetFavoriteGames(queryParent, user, query);
case SpecialFolder.TvShowSeries:
return GetTvSeries(queryParent, user, query);
@ -212,6 +187,21 @@ namespace MediaBrowser.Controller.Entities
case SpecialFolder.MovieCollections:
return GetMovieCollections(queryParent, user, query);
case SpecialFolder.TvFavoriteEpisodes:
return GetFavoriteEpisodes(queryParent, user, query);
case SpecialFolder.TvFavoriteSeries:
return GetFavoriteSeries(queryParent, user, query);
case CollectionType.Music:
return await GetMusicFolders(queryParent, user, query).ConfigureAwait(false);
case SpecialFolder.MusicGenres:
return await GetMusicGenres(queryParent, user, query).ConfigureAwait(false);
case SpecialFolder.MusicGenre:
return await GetMusicGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false);
case SpecialFolder.MusicLatest:
return GetMusicLatest(queryParent, user, query);
@ -230,12 +220,6 @@ namespace MediaBrowser.Controller.Entities
case SpecialFolder.MusicSongs:
return GetMusicSongs(queryParent, user, query);
case SpecialFolder.TvFavoriteEpisodes:
return GetFavoriteEpisodes(queryParent, user, query);
case SpecialFolder.TvFavoriteSeries:
return GetFavoriteSeries(queryParent, user, query);
case SpecialFolder.MusicFavorites:
return await GetMusicFavorites(queryParent, user, query).ConfigureAwait(false);
@ -262,18 +246,6 @@ namespace MediaBrowser.Controller.Entities
}
}
private async Task<QueryResult<BaseItem>> FindPlaylists(Folder parent, User user, InternalItemsQuery query)
{
var list = _playlistManager.GetPlaylists(user.Id.ToString("N"));
return GetResult(list, parent, query);
}
private int GetSpecialItemsLimit()
{
return 50;
}
private async Task<QueryResult<BaseItem>> GetMusicFolders(Folder parent, User user, InternalItemsQuery query)
{
if (query.Recursive)
@ -289,7 +261,7 @@ namespace MediaBrowser.Controller.Entities
list.Add(await GetUserView(SpecialFolder.MusicPlaylists, "1", parent).ConfigureAwait(false));
list.Add(await GetUserView(SpecialFolder.MusicAlbums, "2", parent).ConfigureAwait(false));
list.Add(await GetUserView(SpecialFolder.MusicAlbumArtists, "3", parent).ConfigureAwait(false));
//list.Add(await GetUserView(SpecialFolder.MusicArtists, user, "4", parent).ConfigureAwait(false));
list.Add(await GetUserView(SpecialFolder.MusicArtists, "4", parent).ConfigureAwait(false));
list.Add(await GetUserView(SpecialFolder.MusicSongs, "5", parent).ConfigureAwait(false));
list.Add(await GetUserView(SpecialFolder.MusicGenres, "6", parent).ConfigureAwait(false));
list.Add(await GetUserView(SpecialFolder.MusicFavorites, "7", parent).ConfigureAwait(false));
@ -422,6 +394,36 @@ namespace MediaBrowser.Controller.Entities
return PostFilterAndSort(items, parent, null, query);
}
private QueryResult<BaseItem> GetFavoriteSongs(Folder parent, User user, InternalItemsQuery query)
{
query.IsFavorite = true;
var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is Audio.Audio) && FilterItem(i, query));
return PostFilterAndSort(items, parent, null, query);
}
private QueryResult<BaseItem> GetFavoriteAlbums(Folder parent, User user, InternalItemsQuery query)
{
query.IsFavorite = true;
var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is MusicAlbum) && FilterItem(i, query));
return PostFilterAndSort(items, parent, null, query);
}
private async Task<QueryResult<BaseItem>> FindPlaylists(Folder parent, User user, InternalItemsQuery query)
{
var list = _playlistManager.GetPlaylists(user.Id.ToString("N"));
return GetResult(list, parent, query);
}
private int GetSpecialItemsLimit()
{
return 50;
}
private async Task<QueryResult<BaseItem>> GetMovieFolders(Folder parent, User user, InternalItemsQuery query)
{
if (query.Recursive)
@ -480,24 +482,6 @@ namespace MediaBrowser.Controller.Entities
return PostFilterAndSort(items, parent, null, query);
}
private QueryResult<BaseItem> GetFavoriteSongs(Folder parent, User user, InternalItemsQuery query)
{
query.IsFavorite = true;
var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is Audio.Audio) && FilterItem(i, query));
return PostFilterAndSort(items, parent, null, query);
}
private QueryResult<BaseItem> GetFavoriteAlbums(Folder parent, User user, InternalItemsQuery query)
{
query.IsFavorite = true;
var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is MusicAlbum) && FilterItem(i, query));
return PostFilterAndSort(items, parent, null, query);
}
private QueryResult<BaseItem> GetMovieMovies(Folder parent, User user, InternalItemsQuery query)
{
var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => (i is Movie) && FilterItem(i, query));
@ -582,19 +566,6 @@ namespace MediaBrowser.Controller.Entities
return GetResult(collections, parent, query);
}
private async Task<QueryResult<BaseItem>> GetPhotosView(Folder queryParent, User user, InternalItemsQuery query)
{
if (query.Recursive)
{
var mediaTypes = new[] { MediaType.Video, MediaType.Photo };
var items = GetRecursiveChildren(queryParent, user, new[] { CollectionType.Photos, string.Empty }, i => (i is PhotoAlbum || mediaTypes.Contains(i.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) && FilterItem(i, query));
return PostFilterAndSort(items, queryParent, null, query);
}
return GetResult(queryParent.GetChildren(user, true), queryParent, query);
}
private async Task<QueryResult<BaseItem>> GetTvView(Folder parent, User user, InternalItemsQuery query)
{
if (query.Recursive)
@ -617,54 +588,6 @@ namespace MediaBrowser.Controller.Entities
return GetResult(list, parent, query);
}
private async Task<QueryResult<BaseItem>> GetGameView(User user, Folder parent, InternalItemsQuery query)
{
if (query.Recursive)
{
var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => FilterItem(i, query));
return PostFilterAndSort(items, parent, null, query);
}
var list = new List<BaseItem>();
list.Add(await GetUserView(SpecialFolder.LatestGames, "0", parent).ConfigureAwait(false));
list.Add(await GetUserView(SpecialFolder.RecentlyPlayedGames, "1", parent).ConfigureAwait(false));
list.Add(await GetUserView(SpecialFolder.GameFavorites, "2", parent).ConfigureAwait(false));
list.Add(await GetUserView(SpecialFolder.GameSystems, "3", parent).ConfigureAwait(false));
list.Add(await GetUserView(SpecialFolder.GameGenres, "4", parent).ConfigureAwait(false));
return GetResult(list, parent, query);
}
private QueryResult<BaseItem> GetLatestGames(Folder parent, User user, InternalItemsQuery query)
{
query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName };
query.SortOrder = SortOrder.Descending;
var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query));
return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
}
private QueryResult<BaseItem> GetRecentlyPlayedGames(Folder parent, User user, InternalItemsQuery query)
{
query.IsPlayed = true;
query.SortBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName };
query.SortOrder = SortOrder.Descending;
var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query));
return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
}
private QueryResult<BaseItem> GetFavoriteGames(Folder parent, User user, InternalItemsQuery query)
{
query.IsFavorite = true;
var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query));
return PostFilterAndSort(items, parent, null, query);
}
private QueryResult<BaseItem> GetTvLatest(Folder parent, User user, InternalItemsQuery query)
{
query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName };
@ -745,49 +668,6 @@ namespace MediaBrowser.Controller.Entities
return GetResult(items, queryParent, query);
}
private QueryResult<BaseItem> GetGameSystems(Folder parent, User user, InternalItemsQuery query)
{
var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is GameSystem && FilterItem(i, query));
return PostFilterAndSort(items, parent, null, query);
}
private async Task<QueryResult<BaseItem>> GetGameGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query)
{
var items = GetRecursiveChildren(queryParent, user, new[] { CollectionType.Games },
i => i is Game && i.Genres.Contains(displayParent.Name, StringComparer.OrdinalIgnoreCase));
return GetResult(items, queryParent, query);
}
private async Task<QueryResult<BaseItem>> GetGameGenres(Folder parent, User user, InternalItemsQuery query)
{
var tasks = GetRecursiveChildren(parent, user, new[] { CollectionType.Games })
.OfType<Game>()
.SelectMany(i => i.Genres)
.DistinctNames()
.Select(i =>
{
try
{
return _libraryManager.GetGameGenre(i);
}
catch
{
// Full exception logged at lower levels
_logger.Error("Error getting game genre");
return null;
}
})
.Where(i => i != null)
.Select(i => GetUserView(i.Name, SpecialFolder.GameGenre, i.SortName, parent));
var genres = await Task.WhenAll(tasks).ConfigureAwait(false);
return GetResult(genres, parent, query);
}
private QueryResult<BaseItem> GetResult<T>(QueryResult<T> result)
where T : BaseItem
{
@ -1061,6 +941,11 @@ namespace MediaBrowser.Controller.Entities
return false;
}
if (request.GenreIds.Length > 0)
{
return false;
}
if (request.VideoTypes.Length > 0)
{
return false;
@ -1101,10 +986,15 @@ namespace MediaBrowser.Controller.Entities
return false;
}
if (request.MinIndexNumber.HasValue)
{
return false;
}
return true;
}
public static IEnumerable<BaseItem> FilterVirtualEpisodes(
private static IEnumerable<BaseItem> FilterVirtualEpisodes(
IEnumerable<BaseItem> items,
bool? isMissing,
bool? isVirtualUnaired,
@ -1374,7 +1264,7 @@ namespace MediaBrowser.Controller.Entities
if (query.IsInBoxSet.HasValue)
{
var val = query.IsInBoxSet.Value;
if (item.Parents.OfType<BoxSet>().Any() != val)
if (item.GetParents().OfType<BoxSet>().Any() != val)
{
return false;
}
@ -1657,6 +1547,16 @@ namespace MediaBrowser.Controller.Entities
return false;
}
// Apply genre filter
if (query.GenreIds.Length > 0 && !query.GenreIds.Any(id =>
{
var genreItem = libraryManager.GetItemById(id);
return genreItem != null && item.Genres.Contains(genreItem.Name, StringComparer.OrdinalIgnoreCase);
}))
{
return false;
}
// Apply year filter
if (query.Years.Length > 0)
{
@ -1779,6 +1679,16 @@ namespace MediaBrowser.Controller.Entities
}
}
if (query.MinIndexNumber.HasValue)
{
var val = query.MinIndexNumber.Value;
if (!(item.IndexNumber.HasValue && item.IndexNumber.Value >= val))
{
return false;
}
}
return true;
}
@ -1789,12 +1699,12 @@ namespace MediaBrowser.Controller.Entities
return _libraryManager.RootFolder
.Children
.OfType<Folder>()
.Where(i => !UserView.IsExcludedFromGrouping(i));
.Where(UserView.IsEligibleForGrouping);
}
return user.RootFolder
.GetChildren(user, true, true)
.GetChildren(user, true)
.OfType<Folder>()
.Where(i => user.IsFolderGrouped(i.Id) && !UserView.IsExcludedFromGrouping(i));
.Where(i => user.IsFolderGrouped(i.Id) && UserView.IsEligibleForGrouping(i));
}
private IEnumerable<Folder> GetMediaFolders(User user, IEnumerable<string> viewTypes)

@ -185,12 +185,6 @@ namespace MediaBrowser.Controller.Entities
public bool IsShortcut { get; set; }
public string ShortcutPath { get; set; }
/// <summary>
/// Gets or sets the tags.
/// </summary>
/// <value>The tags.</value>
public List<string> Tags { get; set; }
/// <summary>
/// Gets or sets the video bit rate.
/// </summary>
@ -356,7 +350,7 @@ namespace MediaBrowser.Controller.Entities
// Must have a parent to have additional parts or alternate versions
// In other words, it must be part of the Parent/Child tree
// The additional parts won't have additional parts themselves
if (LocationType == LocationType.FileSystem && Parent != null)
if (LocationType == LocationType.FileSystem && GetParent() != null)
{
if (!IsStacked)
{

@ -67,5 +67,19 @@ namespace MediaBrowser.Controller.FileOrganization
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SaveResult(FileOrganizationResult result, CancellationToken cancellationToken);
/// <summary>
/// Returns a list of smart match entries
/// </summary>
/// <param name="query">The query.</param>
/// <returns>IEnumerable{SmartMatchInfo}.</returns>
QueryResult<SmartMatchInfo> GetSmartMatchInfos(FileOrganizationResultQuery query);
/// <summary>
/// Deletes a smart match entry.
/// </summary>
/// <param name="ItemName">Item name.</param>
/// <param name="matchString">The match string to delete.</param>
void DeleteSmartMatchEntry(string ItemName, string matchString);
}
}

@ -329,7 +329,6 @@ namespace MediaBrowser.Controller.Library
/// <param name="parentId">The parent identifier.</param>
/// <param name="viewType">Type of the view.</param>
/// <param name="sortName">Name of the sort.</param>
/// <param name="uniqueId">The unique identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;UserView&gt;.</returns>
Task<UserView> GetNamedView(User user,
@ -337,7 +336,6 @@ namespace MediaBrowser.Controller.Library
string parentId,
string viewType,
string sortName,
string uniqueId,
CancellationToken cancellationToken);
/// <summary>
@ -391,13 +389,11 @@ namespace MediaBrowser.Controller.Library
/// <param name="parent">The parent.</param>
/// <param name="viewType">Type of the view.</param>
/// <param name="sortName">Name of the sort.</param>
/// <param name="uniqueId">The unique identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;UserView&gt;.</returns>
Task<UserView> GetShadowView(BaseItem parent,
string viewType,
string sortName,
string uniqueId,
CancellationToken cancellationToken);
/// <summary>
@ -543,5 +539,29 @@ namespace MediaBrowser.Controller.Library
/// <param name="imageIndex">Index of the image.</param>
/// <returns>Task.</returns>
Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex);
/// <summary>
/// Gets the items.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="parentIds">The parent ids.</param>
/// <returns>List&lt;BaseItem&gt;.</returns>
IEnumerable<BaseItem> GetItems(InternalItemsQuery query, IEnumerable<string> parentIds);
/// <summary>
/// Gets the items result.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="parentIds">The parent ids.</param>
/// <returns>QueryResult&lt;BaseItem&gt;.</returns>
QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query, IEnumerable<string> parentIds);
/// <summary>
/// Ignores the file.
/// </summary>
/// <param name="file">The file.</param>
/// <param name="parent">The parent.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
bool IgnoreFile(FileSystemMetadata file, BaseItem parent);
}
}

@ -155,7 +155,7 @@ namespace MediaBrowser.Controller.Library
// Not officially supported but in some cases we can handle it.
if (item == null)
{
item = parent.Parents.OfType<T>().FirstOrDefault();
item = parent.GetParents().OfType<T>().FirstOrDefault();
}
return item != null;

@ -6,14 +6,6 @@ namespace MediaBrowser.Controller.Library
{
public static class LibraryManagerExtensions
{
public static Task DeleteItem(this ILibraryManager manager, BaseItem item)
{
return manager.DeleteItem(item, new DeleteOptions
{
DeleteFileLocation = true
});
}
public static BaseItem GetItemById(this ILibraryManager manager, string id)
{
return manager.GetItemById(new Guid(id));

@ -44,6 +44,13 @@ namespace MediaBrowser.Controller.LiveTv
/// <returns>Task.</returns>
Task DeleteRecording(string id);
/// <summary>
/// Deletes the recording.
/// </summary>
/// <param name="recording">The recording.</param>
/// <returns>Task.</returns>
Task DeleteRecording(ILiveTvRecording recording);
/// <summary>
/// Cancels the timer.
/// </summary>
@ -338,9 +345,9 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <param name="item">The item.</param>
/// <param name="dto">The dto.</param>
/// <param name="addChannelInfo">if set to <c>true</c> [add channel information].</param>
/// <param name="fields">The fields.</param>
/// <param name="user">The user.</param>
void AddInfoToProgramDto(BaseItem item, BaseItemDto dto, bool addChannelInfo, User user = null);
void AddInfoToProgramDto(BaseItem item, BaseItemDto dto, List<ItemFields> fields, User user = null);
/// <summary>
/// Saves the tuner host.
/// </summary>

@ -9,6 +9,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
namespace MediaBrowser.Controller.LiveTv
{
@ -36,7 +38,6 @@ namespace MediaBrowser.Controller.LiveTv
public bool IsLive { get; set; }
[IgnoreDataMember]
public bool IsPremiere { get; set; }
public ProgramAudio? Audio { get; set; }
/// <summary>
/// Gets the user data key.
@ -106,9 +107,9 @@ namespace MediaBrowser.Controller.LiveTv
}
}
protected override bool GetBlockUnratedValue(UserPolicy config)
public override UnratedItem GetBlockUnratedType()
{
return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
return UnratedItem.LiveTvProgram;
}
protected override string GetInternalMetadataPath(string basePath)
@ -140,5 +141,15 @@ namespace MediaBrowser.Controller.LiveTv
return list;
}
public override bool IsVisibleStandalone(User user)
{
return IsVisible(user);
}
public override Task Delete(DeleteOptions options)
{
return LiveTvManager.DeleteRecording(this);
}
}
}

@ -22,9 +22,9 @@ namespace MediaBrowser.Controller.LiveTv
return GetClientTypeName() + "-" + Name;
}
protected override bool GetBlockUnratedValue(UserPolicy config)
public override UnratedItem GetBlockUnratedType()
{
return config.BlockUnratedItems.Contains(UnratedItem.LiveTvChannel);
return UnratedItem.LiveTvChannel;
}
/// <summary>

@ -40,10 +40,11 @@ namespace MediaBrowser.Controller.LiveTv
}
/// <summary>
/// Gets or sets the type of the channel.
/// Gets or sets the name.
/// </summary>
/// <value>The type of the channel.</value>
public ChannelType ChannelType { get; set; }
/// <value>The name.</value>
[IgnoreDataMember]
public string ServiceName { get; set; }
/// <summary>
/// The start date of the program, in UTC.
@ -51,12 +52,6 @@ namespace MediaBrowser.Controller.LiveTv
[IgnoreDataMember]
public DateTime StartDate { get; set; }
/// <summary>
/// Gets or sets the audio.
/// </summary>
/// <value>The audio.</value>
public ProgramAudio? Audio { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is repeat.
/// </summary>
@ -71,12 +66,6 @@ namespace MediaBrowser.Controller.LiveTv
[IgnoreDataMember]
public string EpisodeTitle { get; set; }
/// <summary>
/// Gets or sets the name of the service.
/// </summary>
/// <value>The name of the service.</value>
public string ServiceName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is movie.
/// </summary>
@ -153,14 +142,14 @@ namespace MediaBrowser.Controller.LiveTv
}
}
[IgnoreDataMember]
public override string MediaType
{
get
{
return ChannelType == ChannelType.TV ? Model.Entities.MediaType.Video : Model.Entities.MediaType.Audio;
}
}
//[IgnoreDataMember]
//public override string MediaType
//{
// get
// {
// return ChannelType == ChannelType.TV ? Model.Entities.MediaType.Video : Model.Entities.MediaType.Audio;
// }
//}
[IgnoreDataMember]
public bool IsAiring
@ -189,9 +178,9 @@ namespace MediaBrowser.Controller.LiveTv
return "Program";
}
protected override bool GetBlockUnratedValue(UserPolicy config)
public override UnratedItem GetBlockUnratedType()
{
return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
return UnratedItem.LiveTvProgram;
}
protected override string GetInternalMetadataPath(string basePath)
@ -236,5 +225,14 @@ namespace MediaBrowser.Controller.LiveTv
return base.SupportsPeople;
}
}
[IgnoreDataMember]
public override bool SupportsAncestors
{
get
{
return false;
}
}
}
}

@ -9,6 +9,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
namespace MediaBrowser.Controller.LiveTv
{
@ -36,7 +38,6 @@ namespace MediaBrowser.Controller.LiveTv
public bool IsLive { get; set; }
[IgnoreDataMember]
public bool IsPremiere { get; set; }
public ProgramAudio? Audio { get; set; }
/// <summary>
/// Gets the user data key.
@ -121,9 +122,9 @@ namespace MediaBrowser.Controller.LiveTv
}
}
protected override bool GetBlockUnratedValue(UserPolicy config)
public override UnratedItem GetBlockUnratedType()
{
return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
return UnratedItem.LiveTvProgram;
}
protected override string GetInternalMetadataPath(string basePath)
@ -155,5 +156,15 @@ namespace MediaBrowser.Controller.LiveTv
return list;
}
public override bool IsVisibleStandalone(User user)
{
return IsVisible(user);
}
public override Task Delete(DeleteOptions options)
{
return LiveTvManager.DeleteRecording(this);
}
}
}

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.LiveTv
@ -11,6 +12,11 @@ namespace MediaBrowser.Controller.LiveTv
return false;
}
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.LiveTvProgram;
}
public override bool SupportsLocalMetadata
{
get

@ -162,6 +162,7 @@
<Compile Include="Entities\IHasThemeMedia.cs" />
<Compile Include="Entities\IHasTrailers.cs" />
<Compile Include="Entities\IHasUserData.cs" />
<Compile Include="Entities\IHiddenFromDisplay.cs" />
<Compile Include="Entities\IItemByName.cs" />
<Compile Include="Entities\ILibraryItem.cs" />
<Compile Include="Entities\ImageSourceInfo.cs" />

@ -176,6 +176,20 @@ namespace MediaBrowser.Controller.Persistence
/// <param name="query">The query.</param>
/// <returns>QueryResult&lt;Tuple&lt;Guid, System.String&gt;&gt;.</returns>
QueryResult<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query);
/// <summary>
/// Gets the item list.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>List&lt;BaseItem&gt;.</returns>
IEnumerable<BaseItem> GetItemList(InternalItemsQuery query);
/// <summary>
/// Updates the inherited values.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task UpdateInheritedValues(CancellationToken cancellationToken);
}
}

@ -144,6 +144,7 @@ namespace MediaBrowser.Controller.Playlists
public string PlaylistMediaType { get; set; }
[IgnoreDataMember]
public override string MediaType
{
get

@ -889,6 +889,10 @@ namespace MediaBrowser.Controller.Providers
{
video.Video3DFormat = Video3DFormat.FullSideBySide;
}
else if (string.Equals("MVC", val, StringComparison.OrdinalIgnoreCase))
{
video.Video3DFormat = Video3DFormat.MVC;
}
}
break;
}

@ -10,24 +10,6 @@ namespace MediaBrowser.Controller.Providers
/// <value>The item identifier.</value>
public Guid ItemId { get; set; }
/// <summary>
/// Gets or sets the name of the item.
/// </summary>
/// <value>The name of the item.</value>
public string ItemName { get; set; }
/// <summary>
/// Gets or sets the type of the item.
/// </summary>
/// <value>The type of the item.</value>
public string ItemType { get; set; }
/// <summary>
/// Gets or sets the name of the series.
/// </summary>
/// <value>The name of the series.</value>
public string SeriesName { get; set; }
/// <summary>
/// Gets or sets the date last metadata refresh.
/// </summary>
@ -40,22 +22,8 @@ namespace MediaBrowser.Controller.Providers
/// <value>The date last images refresh.</value>
public DateTime? DateLastImagesRefresh { get; set; }
/// <summary>
/// Gets or sets the last result error message.
/// </summary>
/// <value>The last result error message.</value>
public string LastErrorMessage { get; set; }
public DateTime? ItemDateModified { get; set; }
public void AddStatus(string errorMessage)
{
if (string.IsNullOrEmpty(LastErrorMessage))
{
LastErrorMessage = errorMessage;
}
}
public bool IsDirty { get; private set; }
public void SetDateLastMetadataRefresh(DateTime? date)

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Library;
using CommonIO;
using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Resolvers
{
@ -7,6 +8,6 @@ namespace MediaBrowser.Controller.Resolvers
/// </summary>
public interface IResolverIgnoreRule
{
bool ShouldIgnore(ItemResolveArgs args);
bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent);
}
}

@ -26,6 +26,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
private readonly ILocalizationManager _localization;
private readonly IChannelManager _channelManager;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IUserViewManager _userViewManager;
public ContentDirectory(IDlnaManager dlna,
IUserDataManager userDataManager,
@ -34,7 +35,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
IServerConfigurationManager config,
IUserManager userManager,
ILogger logger,
IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager)
IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager)
: base(logger, httpClient)
{
_dlna = dlna;
@ -46,6 +47,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
_localization = localization;
_channelManager = channelManager;
_mediaSourceManager = mediaSourceManager;
_userViewManager = userViewManager;
}
private int SystemUpdateId
@ -86,7 +88,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
_config,
_localization,
_channelManager,
_mediaSourceManager)
_mediaSourceManager,
_userViewManager)
.ProcessControlRequest(request);
}

@ -24,6 +24,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using MediaBrowser.Model.Library;
namespace MediaBrowser.Dlna.ContentDirectory
{
@ -34,6 +35,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
private readonly IUserDataManager _userDataManager;
private readonly IServerConfigurationManager _config;
private readonly User _user;
private readonly IUserViewManager _userViewManager;
private const string NS_DC = "http://purl.org/dc/elements/1.1/";
private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
@ -47,7 +49,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
private readonly DeviceProfile _profile;
public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager)
public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager)
: base(config, logger)
{
_libraryManager = libraryManager;
@ -55,6 +57,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
_user = user;
_systemUpdateId = systemUpdateId;
_channelManager = channelManager;
_userViewManager = userViewManager;
_profile = profile;
_config = config;
@ -450,16 +453,32 @@ namespace MediaBrowser.Dlna.ContentDirectory
sortOrders.Add(ItemSortBy.SortName);
}
var queryResult = await folder.GetItems(new InternalItemsQuery
QueryResult<BaseItem> queryResult;
if (folder is UserRootFolder)
{
Limit = limit,
StartIndex = startIndex,
SortBy = sortOrders.ToArray(),
SortOrder = sort.SortOrder,
User = user,
Filter = FilterUnsupportedContent
var views = await _userViewManager.GetUserViews(new UserViewQuery { UserId = user.Id.ToString("N"), PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Music } }, CancellationToken.None)
.ConfigureAwait(false);
queryResult = new QueryResult<BaseItem>
{
Items = views.Cast<BaseItem>().ToArray()
};
queryResult.TotalRecordCount = queryResult.Items.Length;
}
else
{
queryResult = await folder.GetItems(new InternalItemsQuery
{
Limit = limit,
StartIndex = startIndex,
SortBy = sortOrders.ToArray(),
SortOrder = sort.SortOrder,
User = user,
Filter = FilterUnsupportedContent
}).ConfigureAwait(false);
}).ConfigureAwait(false);
}
var options = _config.GetDlnaConfiguration();
@ -481,23 +500,17 @@ namespace MediaBrowser.Dlna.ContentDirectory
private QueryResult<ServerItem> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
{
var itemsWithPerson = _libraryManager.GetItems(new InternalItemsQuery
var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
Person = person.Name
}).Items;
var items = itemsWithPerson
.Where(i => i is Movie || i is Series || i is IChannelItem)
.Where(i => i.IsVisibleStandalone(user))
.ToList();
Person = person.Name,
IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name, typeof(ChannelVideoItem).Name },
SortBy = new[] { ItemSortBy.SortName },
Limit = limit,
StartIndex = startIndex
items = _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending)
.Skip(startIndex ?? 0)
.Take(limit ?? int.MaxValue)
.ToList();
}, new string[] { });
var serverItems = items.Select(i => new ServerItem
var serverItems = itemsResult.Items.Select(i => new ServerItem
{
Item = i,
StubType = null
@ -506,7 +519,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
return new QueryResult<ServerItem>
{
TotalRecordCount = serverItems.Length,
TotalRecordCount = itemsResult.TotalRecordCount,
Items = serverItems
};
}

@ -966,7 +966,7 @@ namespace MediaBrowser.Dlna.Didl
}
}
item = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Primary));
item = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Primary));
if (item != null)
{

@ -210,6 +210,10 @@ namespace MediaBrowser.Dlna
throw new ArgumentNullException("headers");
}
//_logger.Debug("GetProfile. Headers: " + _jsonSerializer.SerializeToString(headers));
// Convert to case insensitive
headers = new Dictionary<string, string>(headers, StringComparer.OrdinalIgnoreCase);
var profile = GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification));
if (profile != null)
@ -221,7 +225,7 @@ namespace MediaBrowser.Dlna
string userAgent = null;
headers.TryGetValue("User-Agent", out userAgent);
var msg = "No matching device profile found. The default will be used. ";
var msg = "No matching device profile via headers found. The default will be used. ";
if (!string.IsNullOrEmpty(userAgent))
{
msg += "User-agent: " + userAgent + ". ";
@ -249,7 +253,9 @@ namespace MediaBrowser.Dlna
case HeaderMatchType.Equals:
return string.Equals(value, header.Value, StringComparison.OrdinalIgnoreCase);
case HeaderMatchType.Substring:
return value.IndexOf(header.Value, StringComparison.OrdinalIgnoreCase) != -1;
var isMatch = value.IndexOf(header.Value, StringComparison.OrdinalIgnoreCase) != -1;
//_logger.Debug("IsMatch-Substring value: {0} testValue: {1} isMatch: {2}", value, header.Value, isMatch);
return isMatch;
case HeaderMatchType.Regex:
// Reports of IgnoreCase not working on linux so try it a couple different ways.
return Regex.IsMatch(value, header.Value, RegexOptions.IgnoreCase) || Regex.IsMatch(value.ToUpper(), header.Value.ToUpper(), RegexOptions.IgnoreCase);

@ -114,7 +114,7 @@ namespace MediaBrowser.Dlna.PlayTo
{
Url = url,
UserAgent = USERAGENT,
LogRequest = logRequest || _config.GetDlnaConfiguration().EnableDebugLogging,
LogRequest = logRequest || _config.GetDlnaConfiguration().EnableDebugLog,
LogErrorResponseBody = true
};

@ -122,7 +122,7 @@ namespace MediaBrowser.Dlna.Server
builder.Append("<serialNumber>" + SecurityElement.Escape(_profile.SerialNumber) + "</serialNumber>");
}
builder.Append("<UDN>uuid:" + SecurityElement.Escape(_serverUdn) + "</UDN>");
builder.Append("<UDN>uuid:" + SecurityElement.Escape(_serverId) + "</UDN>");
builder.Append("<presentationURL>" + SecurityElement.Escape(_serverAddress) + "</presentationURL>");
if (!EnableAbsoluteUrls)

@ -27,7 +27,7 @@ namespace MediaBrowser.Dlna.Service
{
try
{
var enableDebugLogging = Config.GetDlnaConfiguration().EnableDebugLogging;
var enableDebugLogging = Config.GetDlnaConfiguration().EnableDebugLog;
if (enableDebugLogging)
{

@ -217,7 +217,7 @@ namespace MediaBrowser.Dlna.Ssdp
return;
}
if (_config.GetDlnaConfiguration().EnableDebugLogging)
if (_config.GetDlnaConfiguration().EnableDebugLog)
{
var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value));
var headerText = string.Join(",", headerTexts.ToArray());

@ -21,7 +21,7 @@ namespace MediaBrowser.Dlna.Ssdp
{
public class SsdpHandler : IDisposable, ISsdpHandler
{
private Socket _socket;
private Socket _multicastSocket;
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
@ -40,6 +40,9 @@ namespace MediaBrowser.Dlna.Ssdp
private readonly IApplicationHost _appHost;
private readonly int _unicastPort = 1901;
private UdpClient _unicastClient;
public SsdpHandler(ILogger logger, IServerConfigurationManager config, IApplicationHost appHost)
{
_logger = logger;
@ -92,7 +95,7 @@ namespace MediaBrowser.Dlna.Ssdp
{
TimeSpan delay = GetSearchDelay(headers);
if (_config.GetDlnaConfiguration().EnableDebugLogging)
if (_config.GetDlnaConfiguration().EnableDebugLog)
{
_logger.Debug("Delaying search response by {0} seconds", delay.TotalSeconds);
}
@ -123,6 +126,8 @@ namespace MediaBrowser.Dlna.Ssdp
RestartSocketListener();
ReloadAliveNotifier();
//CreateUnicastClient();
SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
}
@ -150,26 +155,29 @@ namespace MediaBrowser.Dlna.Ssdp
// Seconds to delay response
values["MX"] = "3";
var header = "M-SEARCH * HTTP/1.1";
var msg = new SsdpMessageBuilder().BuildMessage(header, values);
// UDP is unreliable, so send 3 requests at a time (per Upnp spec, sec 1.1.2)
SendDatagram("M-SEARCH * HTTP/1.1", values, _ssdpEndp, localIp, true, 2);
SendDatagram(msg, _ssdpEndp, localIp, true);
//SendUnicastRequest(msg);
}
public async void SendDatagram(string header,
Dictionary<string, string> values,
public async void SendDatagram(string msg,
EndPoint endpoint,
EndPoint localAddress,
bool isBroadcast,
int sendCount)
int sendCount = 3)
{
var msg = new SsdpMessageBuilder().BuildMessage(header, values);
var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLogging;
var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLog;
for (var i = 0; i < sendCount; i++)
{
if (i > 0)
{
await Task.Delay(500).ConfigureAwait(false);
await Task.Delay(200).ConfigureAwait(false);
}
var dgram = new Datagram(endpoint, localAddress, _logger, msg, isBroadcast, enableDebugLogging);
@ -202,7 +210,7 @@ namespace MediaBrowser.Dlna.Ssdp
private void RespondToSearch(EndPoint endpoint, string deviceType)
{
var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLogging;
var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLog;
var isLogged = false;
@ -232,8 +240,10 @@ namespace MediaBrowser.Dlna.Ssdp
values["ST"] = d.Type;
values["USN"] = d.USN;
SendDatagram(header, values, endpoint, null, false, 1);
SendDatagram(header, values, endpoint, new IPEndPoint(d.Address, 0), false, 1);
var msg = new SsdpMessageBuilder().BuildMessage(header, values);
SendDatagram(msg, endpoint, null, false, 1);
SendDatagram(msg, endpoint, new IPEndPoint(d.Address, 0), false, 1);
//SendDatagram(header, values, endpoint, null, true);
if (enableDebugLogging)
@ -253,7 +263,7 @@ namespace MediaBrowser.Dlna.Ssdp
try
{
_socket = CreateMulticastSocket();
_multicastSocket = CreateMulticastSocket();
_logger.Info("MultiCast socket created");
@ -274,8 +284,7 @@ namespace MediaBrowser.Dlna.Ssdp
EndPoint endpoint = new IPEndPoint(IPAddress.Any, SSDPPort);
_socket.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref endpoint, ReceiveCallback,
buffer);
_multicastSocket.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref endpoint, ReceiveCallback, buffer);
}
catch (ObjectDisposedException)
{
@ -301,11 +310,11 @@ namespace MediaBrowser.Dlna.Ssdp
{
EndPoint endpoint = new IPEndPoint(IPAddress.Any, SSDPPort);
var length = _socket.EndReceiveFrom(result, ref endpoint);
var length = _multicastSocket.EndReceiveFrom(result, ref endpoint);
var received = (byte[])result.AsyncState;
var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLogging;
var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLog;
if (enableDebugLogging)
{
@ -325,7 +334,7 @@ namespace MediaBrowser.Dlna.Ssdp
var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value));
var headerText = string.Join(",", headerTexts.ToArray());
_logger.Debug("{0} message received from {1} on {3}. Headers: {2}", args.Method, args.EndPoint, headerText, _socket.LocalEndPoint);
_logger.Debug("{0} message received from {1} on {3}. Headers: {2}", args.Method, args.EndPoint, headerText, _multicastSocket.LocalEndPoint);
}
OnMessageReceived(args);
@ -342,7 +351,7 @@ namespace MediaBrowser.Dlna.Ssdp
_logger.ErrorException("Failed to read SSDP message", ex);
}
if (_socket != null)
if (_multicastSocket != null)
{
Receive();
}
@ -375,17 +384,18 @@ namespace MediaBrowser.Dlna.Ssdp
_isDisposed = true;
DisposeUnicastClient();
DisposeSocket();
StopAliveNotifier();
}
private void DisposeSocket()
{
if (_socket != null)
if (_multicastSocket != null)
{
_socket.Close();
_socket.Dispose();
_socket = null;
_multicastSocket.Close();
_multicastSocket.Dispose();
_multicastSocket = null;
}
}
@ -404,7 +414,7 @@ namespace MediaBrowser.Dlna.Ssdp
private void NotifyAll()
{
var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLogging;
var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLog;
if (enableDebugLogging)
{
@ -412,11 +422,11 @@ namespace MediaBrowser.Dlna.Ssdp
}
foreach (var d in RegisteredDevices)
{
NotifyDevice(d, "alive", 1, enableDebugLogging);
NotifyDevice(d, "alive", enableDebugLogging);
}
}
private void NotifyDevice(UpnpDevice dev, string type, int sendCount, bool logMessage)
private void NotifyDevice(UpnpDevice dev, string type, bool logMessage)
{
const string header = "NOTIFY * HTTP/1.1";
@ -436,7 +446,9 @@ namespace MediaBrowser.Dlna.Ssdp
_logger.Debug("{0} said {1}", dev.USN, type);
}
SendDatagram(header, values, _ssdpEndp, new IPEndPoint(dev.Address, 0), true, sendCount);
var msg = new SsdpMessageBuilder().BuildMessage(header, values);
SendDatagram(msg, _ssdpEndp, new IPEndPoint(dev.Address, 0), true);
}
public void RegisterNotification(Guid uuid, Uri descriptionUri, IPAddress address, IEnumerable<string> services)
@ -457,13 +469,111 @@ namespace MediaBrowser.Dlna.Ssdp
foreach (var d in dl.ToList())
{
NotifyDevice(d, "byebye", 2, true);
NotifyDevice(d, "byebye", true);
}
_logger.Debug("Unregistered mount {0}", uuid);
}
}
private void CreateUnicastClient()
{
if (_unicastClient == null)
{
try
{
_unicastClient = new UdpClient(_unicastPort);
}
catch (Exception ex)
{
_logger.ErrorException("Error creating unicast client", ex);
}
try
{
UnicastSetBeginReceive();
}
catch (Exception ex)
{
_logger.ErrorException("Error in UnicastSetBeginReceive", ex);
}
}
}
private void DisposeUnicastClient()
{
if (_unicastClient != null)
{
try
{
_unicastClient.Close();
}
catch (Exception ex)
{
_logger.ErrorException("Error closing unicast client", ex);
}
_unicastClient = null;
}
}
/// <summary>
/// Listen for Unicast SSDP Responses
/// </summary>
private void UnicastSetBeginReceive()
{
var ipRxEnd = new IPEndPoint(IPAddress.Any, _unicastPort);
var udpListener = new UdpState { EndPoint = ipRxEnd };
udpListener.UdpClient = _unicastClient;
_unicastClient.BeginReceive(UnicastReceiveCallback, udpListener);
}
/// <summary>
/// The UnicastReceiveCallback receives Http Responses
/// and Fired the SatIpDeviceFound Event for adding the SatIpDevice
/// </summary>
/// <param name="ar"></param>
private void UnicastReceiveCallback(IAsyncResult ar)
{
var udpClient = ((UdpState)(ar.AsyncState)).UdpClient;
var endpoint = ((UdpState)(ar.AsyncState)).EndPoint;
if (udpClient.Client != null)
{
var responseBytes = udpClient.EndReceive(ar, ref endpoint);
var args = SsdpHelper.ParseSsdpResponse(responseBytes);
args.EndPoint = endpoint;
OnMessageReceived(args);
UnicastSetBeginReceive();
}
}
private async void SendUnicastRequest(string request)
{
if (_unicastClient == null)
{
return;
}
_logger.Debug("Sending unicast search request");
byte[] req = Encoding.ASCII.GetBytes(request);
var ipSsdp = IPAddress.Parse(SSDPAddr);
var ipTxEnd = new IPEndPoint(ipSsdp, SSDPPort);
for (var i = 0; i < 3; i++)
{
if (i > 0)
{
await Task.Delay(50).ConfigureAwait(false);
}
_unicastClient.Send(req, req.Length, ipTxEnd);
}
}
private readonly object _notificationTimerSyncLock = new object();
private int _aliveNotifierIntervalMs;
private void ReloadAliveNotifier()
@ -511,5 +621,11 @@ namespace MediaBrowser.Dlna.Ssdp
}
}
}
public class UdpState
{
public UdpClient UdpClient;
public IPEndPoint EndPoint;
}
}
}

@ -176,7 +176,7 @@ namespace MediaBrowser.LocalMetadata.Images
"default"
};
if (item is MusicAlbum || item is MusicArtist)
if (item is MusicAlbum || item is MusicArtist || item is Photo)
{
// these prefer folder
names.Insert(0, "poster");

@ -736,6 +736,9 @@ namespace MediaBrowser.LocalMetadata.Savers
case Video3DFormat.HalfTopAndBottom:
builder.Append("<Format3D>HTAB</Format3D>");
break;
case Video3DFormat.MVC:
builder.Append("<Format3D>MVC</Format3D>");
break;
}
}
}

@ -19,38 +19,40 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
}
protected override string GetCommandLineArguments(EncodingJob job)
protected override string GetCommandLineArguments(EncodingJob state)
{
var audioTranscodeParams = new List<string>();
var bitrate = job.OutputAudioBitrate;
var bitrate = state.OutputAudioBitrate;
if (bitrate.HasValue)
{
audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(UsCulture));
}
if (job.OutputAudioChannels.HasValue)
if (state.OutputAudioChannels.HasValue)
{
audioTranscodeParams.Add("-ac " + job.OutputAudioChannels.Value.ToString(UsCulture));
audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(UsCulture));
}
if (job.OutputAudioSampleRate.HasValue)
if (state.OutputAudioSampleRate.HasValue)
{
audioTranscodeParams.Add("-ar " + job.OutputAudioSampleRate.Value.ToString(UsCulture));
audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture));
}
var threads = GetNumberOfThreads(job, false);
const string vn = " -vn";
var inputModifier = GetInputModifier(job);
var threads = GetNumberOfThreads(state, false);
var inputModifier = GetInputModifier(state);
return string.Format("{0} {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"",
inputModifier,
GetInputArgument(job),
GetInputArgument(state),
threads,
" -vn",
vn,
string.Join(" ", audioTranscodeParams.ToArray()),
job.OutputFilePath).Trim();
state.OutputFilePath).Trim();
}
protected override string GetOutputFileExtension(EncodingJob state)

@ -303,15 +303,15 @@ namespace MediaBrowser.MediaEncoding.Encoder
return job.Options.CpuCoreLimit ?? 0;
}
protected string GetInputModifier(EncodingJob job, bool genPts = true)
protected string GetInputModifier(EncodingJob state, bool genPts = true)
{
var inputModifier = string.Empty;
var probeSize = GetProbeSizeArgument(job);
var probeSize = GetProbeSizeArgument(state);
inputModifier += " " + probeSize;
inputModifier = inputModifier.Trim();
var userAgentParam = GetUserAgentParam(job);
var userAgentParam = GetUserAgentParam(state);
if (!string.IsNullOrWhiteSpace(userAgentParam))
{
@ -320,35 +320,43 @@ namespace MediaBrowser.MediaEncoding.Encoder
inputModifier = inputModifier.Trim();
inputModifier += " " + GetFastSeekCommandLineParameter(job.Options);
inputModifier += " " + GetFastSeekCommandLineParameter(state.Options);
inputModifier = inputModifier.Trim();
if (job.IsVideoRequest && genPts)
if (state.IsVideoRequest && genPts)
{
inputModifier += " -fflags +genpts";
}
if (!string.IsNullOrEmpty(job.InputAudioSync))
if (!string.IsNullOrEmpty(state.InputAudioSync))
{
inputModifier += " -async " + job.InputAudioSync;
inputModifier += " -async " + state.InputAudioSync;
}
if (!string.IsNullOrEmpty(job.InputVideoSync))
if (!string.IsNullOrEmpty(state.InputVideoSync))
{
inputModifier += " -vsync " + job.InputVideoSync;
inputModifier += " -vsync " + state.InputVideoSync;
}
if (job.ReadInputAtNativeFramerate)
if (state.ReadInputAtNativeFramerate)
{
inputModifier += " -re";
}
var videoDecoder = GetVideoDecoder(job);
var videoDecoder = GetVideoDecoder(state);
if (!string.IsNullOrWhiteSpace(videoDecoder))
{
inputModifier += " " + videoDecoder;
}
//if (state.IsVideoRequest)
//{
// if (string.Equals(state.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase))
// {
// //inputModifier += " -noaccurate_seek";
// }
//}
return inputModifier;
}
@ -392,11 +400,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
return null;
}
private string GetUserAgentParam(EncodingJob job)
private string GetUserAgentParam(EncodingJob state)
{
string useragent = null;
job.RemoteHttpHeaders.TryGetValue("User-Agent", out useragent);
state.RemoteHttpHeaders.TryGetValue("User-Agent", out useragent);
if (!string.IsNullOrWhiteSpace(useragent))
{
@ -409,31 +417,31 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary>
/// Gets the probe size argument.
/// </summary>
/// <param name="job">The job.</param>
/// <param name="state">The state.</param>
/// <returns>System.String.</returns>
private string GetProbeSizeArgument(EncodingJob job)
private string GetProbeSizeArgument(EncodingJob state)
{
if (job.PlayableStreamFileNames.Count > 0)
if (state.PlayableStreamFileNames.Count > 0)
{
return MediaEncoder.GetProbeSizeArgument(job.PlayableStreamFileNames.ToArray(), job.InputProtocol);
return MediaEncoder.GetProbeSizeArgument(state.PlayableStreamFileNames.ToArray(), state.InputProtocol);
}
return MediaEncoder.GetProbeSizeArgument(new[] { job.MediaPath }, job.InputProtocol);
return MediaEncoder.GetProbeSizeArgument(new[] { state.MediaPath }, state.InputProtocol);
}
/// <summary>
/// Gets the fast seek command line parameter.
/// </summary>
/// <param name="options">The options.</param>
/// <param name="request">The request.</param>
/// <returns>System.String.</returns>
/// <value>The fast seek command line parameter.</value>
protected string GetFastSeekCommandLineParameter(EncodingJobOptions options)
protected string GetFastSeekCommandLineParameter(EncodingJobOptions request)
{
var time = options.StartTimeTicks;
var time = request.StartTimeTicks ?? 0;
if (time.HasValue && time.Value > 0)
if (time > 0)
{
return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(time.Value));
return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(time));
}
return string.Empty;
@ -442,34 +450,35 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary>
/// Gets the input argument.
/// </summary>
/// <param name="job">The job.</param>
/// <param name="state">The state.</param>
/// <returns>System.String.</returns>
protected string GetInputArgument(EncodingJob job)
protected string GetInputArgument(EncodingJob state)
{
var arg = "-i " + GetInputPathArgument(job);
var arg = string.Format("-i {0}", GetInputPathArgument(state));
if (job.SubtitleStream != null)
if (state.SubtitleStream != null)
{
if (job.SubtitleStream.IsExternal && !job.SubtitleStream.IsTextSubtitleStream)
if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)
{
arg += " -i \"" + job.SubtitleStream.Path + "\"";
arg += " -i \"" + state.SubtitleStream.Path + "\"";
}
}
return arg;
return arg.Trim();
}
private string GetInputPathArgument(EncodingJob job)
private string GetInputPathArgument(EncodingJob state)
{
var protocol = job.InputProtocol;
var protocol = state.InputProtocol;
var mediaPath = state.MediaPath ?? string.Empty;
var inputPath = new[] { job.MediaPath };
var inputPath = new[] { mediaPath };
if (job.IsInputVideo)
if (state.IsInputVideo)
{
if (!(job.VideoType == VideoType.Iso && job.IsoMount == null))
if (!(state.VideoType == VideoType.Iso && state.IsoMount == null))
{
inputPath = MediaEncoderHelpers.GetInputArgument(FileSystem, job.MediaPath, job.InputProtocol, job.IsoMount, job.PlayableStreamFileNames);
inputPath = MediaEncoderHelpers.GetInputArgument(FileSystem, mediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames);
}
}
@ -491,7 +500,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}, false, cancellationToken).ConfigureAwait(false);
AttachMediaStreamInfo(state, liveStreamResponse.MediaSource, state.Options);
AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, state.Options);
if (state.IsVideoRequest)
{
@ -505,11 +514,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
private void AttachMediaStreamInfo(EncodingJob state,
private void AttachMediaSourceInfo(EncodingJob state,
MediaSourceInfo mediaSource,
EncodingJobOptions videoRequest)
{
EncodingJobFactory.AttachMediaStreamInfo(state, mediaSource, videoRequest);
EncodingJobFactory.AttachMediaSourceInfo(state, mediaSource, videoRequest);
}
/// <summary>
@ -559,9 +568,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// </summary>
/// <param name="state">The state.</param>
/// <param name="videoCodec">The video codec.</param>
/// <param name="isHls">if set to <c>true</c> [is HLS].</param>
/// <returns>System.String.</returns>
protected string GetVideoQualityParam(EncodingJob state, string videoCodec, bool isHls)
protected string GetVideoQualityParam(EncodingJob state, string videoCodec)
{
var param = string.Empty;
@ -572,7 +580,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
param = "-preset superfast";
param += " -crf 28";
param += " -crf 23";
}
else if (string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase))
@ -582,6 +590,19 @@ namespace MediaBrowser.MediaEncoding.Encoder
param += " -crf 28";
}
// h264 (h264_qsv)
else if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
{
param = "-preset 7 -look_ahead 0";
}
// h264 (libnvenc)
else if (string.Equals(videoCodec, "libnvenc", StringComparison.OrdinalIgnoreCase))
{
param = "-preset high-performance";
}
// webm
else if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
{
@ -626,7 +647,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
param = "-mbd 2";
}
param += GetVideoBitrateParam(state, videoCodec, isHls);
param += GetVideoBitrateParam(state, videoCodec);
var framerate = GetFramerateParam(state);
if (framerate.HasValue)
@ -644,29 +665,66 @@ namespace MediaBrowser.MediaEncoding.Encoder
param += " -profile:v " + state.Options.Profile;
}
if (state.Options.Level.HasValue)
var levelString = state.Options.Level.HasValue ? state.Options.Level.Value.ToString(CultureInfo.InvariantCulture) : null;
if (!string.IsNullOrEmpty(levelString))
{
param += " -level " + state.Options.Level.Value.ToString(UsCulture);
var h264Encoder = EncodingJobFactory.GetH264Encoder(state, GetEncodingOptions());
// h264_qsv and libnvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format
if (String.Equals(h264Encoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) || String.Equals(h264Encoder, "libnvenc", StringComparison.OrdinalIgnoreCase))
{
switch (levelString)
{
case "30":
param += " -level 3";
break;
case "31":
param += " -level 3.1";
break;
case "32":
param += " -level 3.2";
break;
case "40":
param += " -level 4";
break;
case "41":
param += " -level 4.1";
break;
case "42":
param += " -level 4.2";
break;
case "50":
param += " -level 5";
break;
case "51":
param += " -level 5.1";
break;
case "52":
param += " -level 5.2";
break;
default:
param += " -level " + levelString;
break;
}
}
else
{
param += " -level " + levelString;
}
}
return "-pix_fmt yuv420p " + param;
}
protected string GetVideoBitrateParam(EncodingJob state, string videoCodec, bool isHls)
protected string GetVideoBitrateParam(EncodingJob state, string videoCodec)
{
var bitrate = state.OutputVideoBitrate;
if (bitrate.HasValue)
{
var hasFixedResolution = state.Options.HasFixedResolution;
if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
{
if (hasFixedResolution)
{
return string.Format(" -minrate:v ({0}*.90) -maxrate:v ({0}*1.10) -bufsize:v {0} -b:v {0}", bitrate.Value.ToString(UsCulture));
}
// With vpx when crf is used, b:v becomes a max rate
// https://trac.ffmpeg.org/wiki/vpxEncodingGuide. But higher bitrate source files -b:v causes judder so limite the bitrate but dont allow it to "saturate" the bitrate. So dont contrain it down just up.
return string.Format(" -maxrate:v {0} -bufsize:v ({0}*2) -b:v {0}", bitrate.Value.ToString(UsCulture));
@ -677,18 +735,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
}
// H264
if (hasFixedResolution)
{
if (isHls)
{
return string.Format(" -b:v {0} -maxrate ({0}*.80) -bufsize {0}", bitrate.Value.ToString(UsCulture));
}
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
}
return string.Format(" -maxrate {0} -bufsize {1}",
// h264
return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}",
bitrate.Value.ToString(UsCulture),
(bitrate.Value * 2).ToString(UsCulture));
}
@ -698,20 +746,23 @@ namespace MediaBrowser.MediaEncoding.Encoder
protected double? GetFramerateParam(EncodingJob state)
{
if (state.Options.Framerate.HasValue)
if (state.Options != null)
{
return state.Options.Framerate.Value;
}
if (state.Options.Framerate.HasValue)
{
return state.Options.Framerate.Value;
}
var maxrate = state.Options.MaxFramerate;
var maxrate = state.Options.MaxFramerate;
if (maxrate.HasValue && state.VideoStream != null)
{
var contentRate = state.VideoStream.AverageFrameRate ?? state.VideoStream.RealFrameRate;
if (contentRate.HasValue && contentRate.Value > maxrate.Value)
if (maxrate.HasValue && state.VideoStream != null)
{
return maxrate;
var contentRate = state.VideoStream.AverageFrameRate ?? state.VideoStream.RealFrameRate;
if (contentRate.HasValue && contentRate.Value > maxrate.Value)
{
return maxrate;
}
}
}
@ -852,7 +903,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
filters.Add(string.Format("scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam));
filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
}
// If a max height was requested
@ -863,6 +914,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
}
if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
{
if (filters.Count > 1)
{
//filters[filters.Count - 1] += ":flags=fast_bilinear";
}
}
var output = string.Empty;
if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream)
@ -917,8 +976,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
seconds.ToString(UsCulture));
}
var mediaPath = state.MediaPath ?? string.Empty;
return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB",
MediaEncoder.EscapeSubtitleFilterPath(state.MediaPath),
MediaEncoder.EscapeSubtitleFilterPath(mediaPath),
state.InternalSubtitleStreamOffset.ToString(UsCulture),
seconds.ToString(UsCulture));
}

@ -10,6 +10,7 @@ using MediaBrowser.Model.MediaInfo;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -66,38 +67,56 @@ namespace MediaBrowser.MediaEncoding.Encoder
? mediaSources.First()
: mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
AttachMediaStreamInfo(state, mediaSource, options);
var videoRequest = state.Options;
state.OutputAudioBitrate = GetAudioBitrateParam(request, state.AudioStream);
AttachMediaSourceInfo(state, mediaSource, videoRequest);
//var container = Path.GetExtension(state.RequestedUrl);
//if (string.IsNullOrEmpty(container))
//{
// container = request.Static ?
// state.InputContainer :
// (Path.GetExtension(GetOutputFilePath(state)) ?? string.Empty).TrimStart('.');
//}
//state.OutputContainer = (container ?? string.Empty).TrimStart('.');
state.OutputAudioBitrate = GetAudioBitrateParam(state.Options, state.AudioStream);
state.OutputAudioSampleRate = request.AudioSampleRate;
state.OutputAudioCodec = GetAudioCodec(request);
state.OutputAudioCodec = state.Options.AudioCodec;
state.OutputAudioChannels = GetNumAudioChannelsParam(request, state.AudioStream, state.OutputAudioCodec);
state.OutputAudioChannels = GetNumAudioChannelsParam(state.Options, state.AudioStream, state.OutputAudioCodec);
if (isVideoRequest)
if (videoRequest != null)
{
state.OutputVideoCodec = GetVideoCodec(request);
state.OutputVideoBitrate = GetVideoBitrateParamValue(request, state.VideoStream);
state.OutputVideoCodec = state.Options.VideoCodec;
state.OutputVideoBitrate = GetVideoBitrateParamValue(state.Options, state.VideoStream);
if (state.OutputVideoBitrate.HasValue)
{
var resolution = ResolutionNormalizer.Normalize(
state.VideoStream == null ? (int?)null : state.VideoStream.BitRate,
state.OutputVideoBitrate.Value,
state.VideoStream == null ? null : state.VideoStream.Codec,
state.VideoStream == null ? (int?)null : state.VideoStream.BitRate,
state.OutputVideoBitrate.Value,
state.VideoStream == null ? null : state.VideoStream.Codec,
state.OutputVideoCodec,
request.MaxWidth,
request.MaxHeight);
videoRequest.MaxWidth,
videoRequest.MaxHeight);
request.MaxWidth = resolution.MaxWidth;
request.MaxHeight = resolution.MaxHeight;
videoRequest.MaxWidth = resolution.MaxWidth;
videoRequest.MaxHeight = resolution.MaxHeight;
}
}
ApplyDeviceProfileSettings(state);
TryStreamCopy(state, request);
if (videoRequest != null)
{
TryStreamCopy(state, videoRequest);
}
//state.OutputFilePath = GetOutputFilePath(state);
return state;
}
@ -119,7 +138,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
internal static void AttachMediaStreamInfo(EncodingJob state,
internal static void AttachMediaSourceInfo(EncodingJob state,
MediaSourceInfo mediaSource,
EncodingJobOptions videoRequest)
{
@ -131,11 +150,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
state.RunTimeTicks = mediaSource.RunTimeTicks;
state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
if (mediaSource.ReadAtNativeFramerate)
{
state.ReadInputAtNativeFramerate = true;
}
if (mediaSource.VideoType.HasValue)
{
state.VideoType = mediaSource.VideoType.Value;
@ -156,6 +170,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
state.InputBitrate = mediaSource.Bitrate;
state.InputFileSize = mediaSource.Size;
state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;
if (state.ReadInputAtNativeFramerate ||
mediaSource.Protocol == MediaProtocol.File && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase))
@ -165,6 +180,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
state.InputAudioSync = "1";
}
if (string.Equals(mediaSource.Container, "wma", StringComparison.OrdinalIgnoreCase))
{
// Seeing some stuttering when transcoding wma to audio-only HLS
state.InputAudioSync = "1";
}
var mediaStreams = mediaSource.MediaStreams;
if (videoRequest != null)
@ -210,19 +231,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <returns>System.Nullable{VideoCodecs}.</returns>
private static string InferVideoCodec(string container)
{
if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase))
var ext = "." + (container ?? string.Empty);
if (string.Equals(ext, ".asf", StringComparison.OrdinalIgnoreCase))
{
return "wmv";
}
if (string.Equals(container, "webm", StringComparison.OrdinalIgnoreCase))
if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
{
return "vpx";
}
if (string.Equals(container, "ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(container, "ogv", StringComparison.OrdinalIgnoreCase))
if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
{
return "theora";
}
if (string.Equals(container, "m3u8", StringComparison.OrdinalIgnoreCase) || string.Equals(container, "ts", StringComparison.OrdinalIgnoreCase))
if (string.Equals(ext, ".m3u8", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ts", StringComparison.OrdinalIgnoreCase))
{
return "h264";
}
@ -232,35 +255,37 @@ namespace MediaBrowser.MediaEncoding.Encoder
private string InferAudioCodec(string container)
{
if (string.Equals(container, "mp3", StringComparison.OrdinalIgnoreCase))
var ext = "." + (container ?? string.Empty);
if (string.Equals(ext, ".mp3", StringComparison.OrdinalIgnoreCase))
{
return "mp3";
}
if (string.Equals(container, "aac", StringComparison.OrdinalIgnoreCase))
if (string.Equals(ext, ".aac", StringComparison.OrdinalIgnoreCase))
{
return "aac";
}
if (string.Equals(container, "wma", StringComparison.OrdinalIgnoreCase))
if (string.Equals(ext, ".wma", StringComparison.OrdinalIgnoreCase))
{
return "wma";
}
if (string.Equals(container, "ogg", StringComparison.OrdinalIgnoreCase))
if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase))
{
return "vorbis";
}
if (string.Equals(container, "oga", StringComparison.OrdinalIgnoreCase))
if (string.Equals(ext, ".oga", StringComparison.OrdinalIgnoreCase))
{
return "vorbis";
}
if (string.Equals(container, "ogv", StringComparison.OrdinalIgnoreCase))
if (string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
{
return "vorbis";
}
if (string.Equals(container, "webm", StringComparison.OrdinalIgnoreCase))
if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
{
return "vorbis";
}
if (string.Equals(container, "webma", StringComparison.OrdinalIgnoreCase))
if (string.Equals(ext, ".webma", StringComparison.OrdinalIgnoreCase))
{
return "vorbis";
}
@ -330,8 +355,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
private int? GetNumAudioChannelsParam(EncodingJobOptions request, MediaStream audioStream, string outputAudioCodec)
{
var inputChannels = audioStream == null
? null
: audioStream.Channels;
? null
: audioStream.Channels;
if (inputChannels <= 0)
{
@ -348,15 +373,15 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (request.MaxAudioChannels.HasValue)
{
var channelLimit = codec.IndexOf("mp3", StringComparison.OrdinalIgnoreCase) != -1
? 2
: 6;
if (inputChannels.HasValue)
{
return Math.Min(request.MaxAudioChannels.Value, inputChannels.Value);
channelLimit = Math.Min(channelLimit, inputChannels.Value);
}
var channelLimit = codec.IndexOf("mp3", StringComparison.OrdinalIgnoreCase) != -1
? 2
: 6;
// If we don't have any media info then limit it to 5 to prevent encoding errors due to asking for too many channels
return Math.Min(request.MaxAudioChannels.Value, channelLimit);
}
@ -398,15 +423,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (bitrate.HasValue)
{
var hasFixedResolution = state.Options.HasFixedResolution;
if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
{
if (hasFixedResolution)
{
return string.Format(" -minrate:v ({0}*.90) -maxrate:v ({0}*1.10) -bufsize:v {0} -b:v {0}", bitrate.Value.ToString(UsCulture));
}
// With vpx when crf is used, b:v becomes a max rate
// https://trac.ffmpeg.org/wiki/vpxEncodingGuide. But higher bitrate source files -b:v causes judder so limite the bitrate but dont allow it to "saturate" the bitrate. So dont contrain it down just up.
return string.Format(" -maxrate:v {0} -bufsize:v ({0}*2) -b:v {0}", bitrate.Value.ToString(UsCulture));
@ -417,18 +435,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
}
// H264
if (hasFixedResolution)
{
if (isHls)
{
return string.Format(" -b:v {0} -maxrate ({0}*.80) -bufsize {0}", bitrate.Value.ToString(UsCulture));
}
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
}
return string.Format(" -maxrate {0} -bufsize {1}",
// h264
return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}",
bitrate.Value.ToString(UsCulture),
(bitrate.Value * 2).ToString(UsCulture));
}
@ -466,11 +474,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary>
/// Gets the name of the output audio codec
/// </summary>
/// <param name="request">The request.</param>
/// <param name="state">The state.</param>
/// <returns>System.String.</returns>
private string GetAudioCodec(EncodingJobOptions request)
internal static string GetAudioEncoder(EncodingJob state)
{
var codec = request.AudioCodec;
var codec = state.OutputAudioCodec;
if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase))
{
@ -489,40 +497,56 @@ namespace MediaBrowser.MediaEncoding.Encoder
return "wmav2";
}
return (codec ?? string.Empty).ToLower();
return codec.ToLower();
}
/// <summary>
/// Gets the name of the output video codec
/// </summary>
/// <param name="request">The request.</param>
/// <param name="state">The state.</param>
/// <param name="options">The options.</param>
/// <returns>System.String.</returns>
private string GetVideoCodec(EncodingJobOptions request)
internal static string GetVideoEncoder(EncodingJob state, EncodingOptions options)
{
var codec = request.VideoCodec;
var codec = state.OutputVideoCodec;
if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase))
if (!string.IsNullOrEmpty(codec))
{
return "libx264";
}
if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
{
return "libx265";
}
if (string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase))
{
return "libvpx";
}
if (string.Equals(codec, "wmv", StringComparison.OrdinalIgnoreCase))
{
return "wmv2";
if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase))
{
return GetH264Encoder(state, options);
}
if (string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase))
{
return "libvpx";
}
if (string.Equals(codec, "wmv", StringComparison.OrdinalIgnoreCase))
{
return "wmv2";
}
if (string.Equals(codec, "theora", StringComparison.OrdinalIgnoreCase))
{
return "libtheora";
}
return codec.ToLower();
}
if (string.Equals(codec, "theora", StringComparison.OrdinalIgnoreCase))
return "copy";
}
internal static string GetH264Encoder(EncodingJob state, EncodingOptions options)
{
if (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
{
return "libtheora";
// It's currently failing on live tv
if (state.RunTimeTicks.HasValue)
{
return "h264_qsv";
}
}
return (codec ?? string.Empty).ToLower();
return "libx264";
}
internal static bool CanStreamCopyVideo(EncodingJobOptions request, MediaStream videoStream)

@ -557,6 +557,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
vf = "crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2";
// ftab crop heigt in half, set the display aspect,crop out any black bars we may have made the scale width to 600
break;
default:
break;
}
}

@ -21,7 +21,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
protected override string GetCommandLineArguments(EncodingJob state)
{
// Get the output codec name
var videoCodec = state.OutputVideoCodec;
var videoCodec = EncodingJobFactory.GetVideoEncoder(state, GetEncodingOptions());
var format = string.Empty;
var keyFrame = string.Empty;
@ -29,6 +29,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (string.Equals(Path.GetExtension(state.OutputFilePath), ".mp4", StringComparison.OrdinalIgnoreCase) &&
state.Options.Context == EncodingContext.Streaming)
{
// Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
format = " -f mp4 -movflags frag_keyframe+empty_moov";
}
@ -53,42 +54,49 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// Gets video arguments to pass to ffmpeg
/// </summary>
/// <param name="state">The state.</param>
/// <param name="codec">The video codec.</param>
/// <param name="videoCodec">The video codec.</param>
/// <returns>System.String.</returns>
private string GetVideoArguments(EncodingJob state, string codec)
private string GetVideoArguments(EncodingJob state, string videoCodec)
{
var args = "-codec:v:0 " + codec;
var args = "-codec:v:0 " + videoCodec;
if (state.EnableMpegtsM2TsMode)
{
args += " -mpegts_m2ts_mode 1";
}
// See if we can save come cpu cycles by avoiding encoding
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
var isOutputMkv = string.Equals(state.Options.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase);
if (state.RunTimeTicks.HasValue)
{
return state.VideoStream != null && IsH264(state.VideoStream) && string.Equals(state.Options.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) ?
args + " -bsf:v h264_mp4toannexb" :
args;
//args += " -copyts -avoid_negative_ts disabled -start_at_zero";
}
if (state.Options.Context == EncodingContext.Streaming)
if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})",
5.ToString(UsCulture));
if (state.VideoStream != null && IsH264(state.VideoStream) &&
(string.Equals(state.Options.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) || isOutputMkv))
{
args += " -bsf:v h264_mp4toannexb";
}
args += keyFrameArg;
return args;
}
var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})",
5.ToString(UsCulture));
args += keyFrameArg;
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
// Add resolution params, if specified
if (!hasGraphicalSubs)
{
args += GetOutputSizeParam(state, codec);
args += GetOutputSizeParam(state, videoCodec);
}
var qualityParam = GetVideoQualityParam(state, codec, false);
var qualityParam = GetVideoQualityParam(state, videoCodec);
if (!string.IsNullOrEmpty(qualityParam))
{
@ -98,7 +106,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
// This is for internal graphical subs
if (hasGraphicalSubs)
{
args += GetGraphicalSubtitleParam(state, codec);
args += GetGraphicalSubtitleParam(state, videoCodec);
}
return args;
@ -118,11 +126,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
// Get the output codec name
var codec = state.OutputAudioCodec;
var codec = EncodingJobFactory.GetAudioEncoder(state);
var args = "-codec:a:0 " + codec;
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
{
return args;
}

@ -122,10 +122,15 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var subtitle = await GetSubtitleStream(itemId, mediaSourceId, subtitleStreamIndex, cancellationToken)
.ConfigureAwait(false);
using (var stream = subtitle.Item1)
var inputFormat = subtitle.Item2;
if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase) && TryGetWriter(outputFormat) == null)
{
var inputFormat = subtitle.Item2;
return subtitle.Item1;
}
using (var stream = subtitle.Item1)
{
return await ConvertSubtitles(stream, inputFormat, outputFormat, startTimeTicks, endTimeTicks, cancellationToken).ConfigureAwait(false);
}
}
@ -288,7 +293,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return null;
}
private ISubtitleWriter GetWriter(string format)
private ISubtitleWriter TryGetWriter(string format)
{
if (string.IsNullOrEmpty(format))
{
@ -312,6 +317,18 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return new TtmlWriter();
}
return null;
}
private ISubtitleWriter GetWriter(string format)
{
var writer = TryGetWriter(format);
if (writer != null)
{
return writer;
}
throw new ArgumentException("Unsupported format: " + format);
}

@ -668,6 +668,9 @@
<Compile Include="..\MediaBrowser.Model\FileOrganization\FileSortingStatus.cs">
<Link>FileOrganization\FileSortingStatus.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\FileOrganization\SmartMatchInfo.cs">
<Link>FileOrganization\SmartMatchInfo.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\FileOrganization\TvFileOrganizationOptions.cs">
<Link>FileOrganization\TvFileOrganizationOptions.cs</Link>
</Compile>

@ -633,6 +633,9 @@
<Compile Include="..\MediaBrowser.Model\FileOrganization\FileSortingStatus.cs">
<Link>FileOrganization\FileSortingStatus.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\FileOrganization\SmartMatchInfo.cs">
<Link>FileOrganization\SmartMatchInfo.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\FileOrganization\TvFileOrganizationOptions.cs">
<Link>FileOrganization\TvFileOrganizationOptions.cs</Link>
</Compile>

@ -1413,6 +1413,13 @@ namespace MediaBrowser.Model.ApiClient
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;System.Int32&gt;.</returns>
Task<int> GetSupportedBitrate(CancellationToken cancellationToken);
Task<int> DetectMaxBitrate(CancellationToken cancellationToken);
/// <summary>
/// Gets the end point information.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>System.Threading.Tasks.Task&lt;MediaBrowser.Model.Net.EndPointInfo&gt;.</returns>
Task<EndPointInfo> GetEndPointInfo(CancellationToken cancellationToken);
}
}

@ -11,6 +11,7 @@ namespace MediaBrowser.Model.Configuration
public bool EnableIntrosParentalControl { get; set; }
public bool EnableIntrosFromSimilarMovies { get; set; }
public string CustomIntroPath { get; set; }
public string MediaInfoIntroPath { get; set; }
public bool EnableIntrosFromUpcomingDvdMovies { get; set; }
public bool EnableIntrosFromUpcomingStreamingMovies { get; set; }

@ -5,7 +5,7 @@ namespace MediaBrowser.Model.Configuration
{
public bool EnablePlayTo { get; set; }
public bool EnableServer { get; set; }
public bool EnableDebugLogging { get; set; }
public bool EnableDebugLog { get; set; }
public bool BlastAliveMessages { get; set; }
public int ClientDiscoveryIntervalSeconds { get; set; }
public int BlastAliveMessageIntervalSeconds { get; set; }

@ -92,24 +92,6 @@ namespace MediaBrowser.Model.Configuration
/// <value><c>true</c> if [enable localized guids]; otherwise, <c>false</c>.</value>
public bool EnableLocalizedGuids { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [disable startup scan].
/// </summary>
/// <value><c>true</c> if [disable startup scan]; otherwise, <c>false</c>.</value>
public bool DisableStartupScan { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [enable user views].
/// </summary>
/// <value><c>true</c> if [enable user views]; otherwise, <c>false</c>.</value>
public bool EnableUserViews { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [enable library metadata sub folder].
/// </summary>
/// <value><c>true</c> if [enable library metadata sub folder]; otherwise, <c>false</c>.</value>
public bool EnableLibraryMetadataSubFolder { get; set; }
/// <summary>
/// Gets or sets the preferred metadata language.
/// </summary>
@ -179,9 +161,7 @@ namespace MediaBrowser.Model.Configuration
/// </summary>
/// <value>The dashboard source path.</value>
public string DashboardSourcePath { get; set; }
public bool MergeMetadataAndImagesByName { get; set; }
/// <summary>
/// Gets or sets the image saving convention.
/// </summary>
@ -220,18 +200,18 @@ namespace MediaBrowser.Model.Configuration
public bool EnableWindowsShortcuts { get; set; }
public bool EnableVideoFrameByFrameAnalysis { get; set; }
public bool EnableDateLastRefresh { get; set; }
public string[] Migrations { get; set; }
public int MigrationVersion { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
/// </summary>
public ServerConfiguration()
{
Migrations = new string[] {};
Migrations = new string[] { };
ImageSavingConvention = ImageSavingConvention.Compatible;
PublicPort = 8096;
@ -576,7 +556,7 @@ namespace MediaBrowser.Model.Configuration
Type = ImageType.Thumb
}
},
DisabledMetadataFetchers = new []{ "TheMovieDb" }
DisabledMetadataFetchers = new []{ "The Open Movie Database", "TheMovieDb" }
},
new MetadataOptions(0, 1280)
@ -597,6 +577,7 @@ namespace MediaBrowser.Model.Configuration
Type = ImageType.Primary
}
},
DisabledMetadataFetchers = new []{ "The Open Movie Database" },
DisabledImageFetchers = new []{ "TheMovieDb" }
}
};

@ -192,6 +192,7 @@ namespace MediaBrowser.Model.Dto
/// <value>The channel identifier.</value>
public string ChannelId { get; set; }
public string ChannelName { get; set; }
public string ServiceName { get; set; }
/// <summary>
/// Gets or sets the overview.

@ -51,7 +51,7 @@ namespace MediaBrowser.Model.Dto
public string TranscodingUrl { get; set; }
public string TranscodingSubProtocol { get; set; }
public string TranscodingContainer { get; set; }
public MediaSourceInfo()
{
Formats = new List<string>();

@ -6,6 +6,7 @@ namespace MediaBrowser.Model.Entities
HalfSideBySide,
FullSideBySide,
FullTopAndBottom,
HalfTopAndBottom
HalfTopAndBottom,
MVC
}
}

@ -9,9 +9,16 @@ namespace MediaBrowser.Model.FileOrganization
/// <value>The tv options.</value>
public TvFileOrganizationOptions TvOptions { get; set; }
/// <summary>
/// Gets or sets a list of smart match entries.
/// </summary>
/// <value>The smart match entries.</value>
public SmartMatchInfo[] SmartMatchInfos { get; set; }
public AutoOrganizeOptions()
{
TvOptions = new TvFileOrganizationOptions();
SmartMatchInfos = new SmartMatchInfo[]{};
}
}
}

@ -0,0 +1,16 @@

namespace MediaBrowser.Model.FileOrganization
{
public class SmartMatchInfo
{
public string ItemName { get; set; }
public string DisplayName { get; set; }
public FileOrganizerType OrganizerType { get; set; }
public string[] MatchStrings { get; set; }
public SmartMatchInfo()
{
MatchStrings = new string[] { };
}
}
}

@ -8,6 +8,7 @@ namespace MediaBrowser.Model.LiveTv
public bool EnableMovieProviders { get; set; }
public string RecordingPath { get; set; }
public bool EnableAutoOrganize { get; set; }
public bool EnableRecordingEncoding { get; set; }
public List<TunerHostInfo> TunerHosts { get; set; }
public List<ListingsProviderInfo> ListingProviders { get; set; }

@ -137,6 +137,7 @@
<Compile Include="Dto\MetadataEditorInfo.cs" />
<Compile Include="Dto\NameIdPair.cs" />
<Compile Include="Dto\NameValuePair.cs" />
<Compile Include="FileOrganization\SmartMatchInfo.cs" />
<Compile Include="MediaInfo\LiveStreamRequest.cs" />
<Compile Include="MediaInfo\LiveStreamResponse.cs" />
<Compile Include="MediaInfo\PlaybackInfoRequest.cs" />

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

Loading…
Cancel
Save