Merge branch 'beta'
This commit is contained in:
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)
|
||||
|
|
202
MediaBrowser.Api/PinLoginService.cs
Normal file
202
MediaBrowser.Api/PinLoginService.cs
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
return audio;
|
||||
audio.ExtraType = ExtraType.ThemeSong;
|
||||
|
||||
// Sort them so that the list can be easily compared for changes
|
||||
}).OrderBy(i => i.Path).ToList();
|
||||
return audio;
|
||||
|
||||
// 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;
|
||||
}
|
||||
currentChild.DateModified = child.DateModified;
|
||||
}
|
||||
|
||||
await UpdateIsOffline(currentChild, false).ConfigureAwait(false);
|
||||
validChildren.Add(currentChild);
|
||||
}
|
||||
else
|
||||
{
|
||||
newItems.Add(child);
|
||||
validChildren.Add(child);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Brand new item - needs to be added
|
||||
newItems.Add(child);
|
||||
validChildren.Add(child);
|
||||
await UpdateIsOffline(currentChild, false).ConfigureAwait(false);
|
||||
validChildren.Add(currentChild);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
|
18
MediaBrowser.Controller/Entities/IHiddenFromDisplay.cs
Normal file
18
MediaBrowser.Controller/Entities/IHiddenFromDisplay.cs
Normal file
|
@ -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,
|
||||
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
|
||||
};
|
||||
|
||||
return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsEligibleForEnhancedView(string viewType)
|
||||
{
|
||||
var types = new[]
|
||||
{
|
||||
CollectionType.Movies,
|
||||
CollectionType.TvShows
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
var collectionFolder = folder as ICollectionFolder;
|
||||
return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (collectionFolder == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty);
|
||||
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<UserView>.</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<UserView>.</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<BaseItem>.</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<BaseItem>.</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<Tuple<Guid, System.String>>.</returns>
|
||||
QueryResult<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item list.
|
||||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>List<BaseItem>.</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
|
||||
{
|
||||
Limit = limit,
|
||||
StartIndex = startIndex,
|
||||
SortBy = sortOrders.ToArray(),
|
||||
SortOrder = sort.SortOrder,
|
||||
User = user,
|
||||
Filter = FilterUnsupportedContent
|
||||
QueryResult<BaseItem> queryResult;
|
||||
|
||||
}).ConfigureAwait(false);
|
||||
if (folder is UserRootFolder)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
Person = person.Name,
|
||||
IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name, typeof(ChannelVideoItem).Name },
|
||||
SortBy = new[] { ItemSortBy.SortName },
|
||||
Limit = limit,
|
||||
StartIndex = startIndex
|
||||
|
||||
}).Items;
|
||||
}, new string[] { });
|
||||
|
||||
var items = itemsWithPerson
|
||||
.Where(i => i is Movie || i is Series || i is IChannelItem)
|
||||
.Where(i => i.IsVisibleStandalone(user))
|
||||
.ToList();
|
||||
|
||||
items = _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending)
|
||||
.Skip(startIndex ?? 0)
|
||||
.Take(limit ?? int.MaxValue)
|
||||
.ToList();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 (state.Options.Framerate.HasValue)
|
||||
{
|
||||
return maxrate;
|
||||
return state.Options.Framerate.Value;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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, "theora", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return "libtheora";
|
||||
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();
|
||||
}
|
||||
|
||||
return (codec ?? string.Empty).ToLower();
|
||||
return "copy";
|
||||
}
|
||||
|
||||
internal static string GetH264Encoder(EncodingJob state, EncodingOptions options)
|
||||
{
|
||||
if (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// It's currently failing on live tv
|
||||
if (state.RunTimeTicks.HasValue)
|
||||
{
|
||||
return "h264_qsv";
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
var inputFormat = subtitle.Item2;
|
||||
|
||||
if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase) && TryGetWriter(outputFormat) == null)
|
||||
{
|
||||
return subtitle.Item1;
|
||||
}
|
||||
|
||||
using (var stream = subtitle.Item1)
|
||||
{
|
||||
var inputFormat = subtitle.Item2;
|
||||
|
||||
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<System.Int32>.</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<MediaBrowser.Model.Net.EndPointInfo>.</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[]{};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
16
MediaBrowser.Model/FileOrganization/SmartMatchInfo.cs
Normal file
16
MediaBrowser.Model/FileOrganization/SmartMatchInfo.cs
Normal file
|
@ -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…
Reference in New Issue
Block a user