Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
9e33966ee1
|
@ -164,7 +164,10 @@ namespace MediaBrowser.Api
|
|||
return name;
|
||||
}
|
||||
|
||||
return libraryManager.GetAllArtists()
|
||||
return libraryManager.RootFolder.RecursiveChildren
|
||||
.OfType<Audio>()
|
||||
.SelectMany(i => i.AllArtists)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.FirstOrDefault(i =>
|
||||
{
|
||||
i = _dashReplaceChars.Aggregate(i, (current, c) => current.Replace(c, SlugChar));
|
||||
|
|
|
@ -194,8 +194,10 @@ namespace MediaBrowser.Api.DefaultTheme
|
|||
.Select(i => _dtoService.GetBaseItemDto(i, fields, user))
|
||||
.ToList();
|
||||
|
||||
var artists = _libraryManager.GetAllArtists(allItems)
|
||||
.Randomize()
|
||||
var artists = allItems.OfType<Audio>()
|
||||
.SelectMany(i => i.AllArtists)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.Randomize()
|
||||
.Select(i =>
|
||||
{
|
||||
try
|
||||
|
|
|
@ -155,7 +155,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
var games = items.OfType<Game>().ToList();
|
||||
|
||||
summary.ClientInstalledGameCount = games.Count(i => !i.IsPlaceHolder);
|
||||
summary.ClientInstalledGameCount = games.Count(i => i.IsPlaceHolder);
|
||||
|
||||
summary.GameCount = games.Count;
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
UpdateItem(request, item);
|
||||
|
||||
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
if (dontFetchMetaChanged && item.IsFolder)
|
||||
{
|
||||
|
@ -107,7 +107,7 @@ namespace MediaBrowser.Api
|
|||
foreach (var child in folder.RecursiveChildren.ToList())
|
||||
{
|
||||
child.DontFetchMeta = newLockData;
|
||||
await _libraryManager.UpdateItem(child, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
await child.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
UpdateItem(request, item);
|
||||
|
||||
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Post(UpdateArtist request)
|
||||
|
@ -141,7 +141,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
UpdateItem(request, item);
|
||||
|
||||
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Post(UpdateStudio request)
|
||||
|
@ -157,7 +157,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
UpdateItem(request, item);
|
||||
|
||||
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Post(UpdateMusicGenre request)
|
||||
|
@ -173,7 +173,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
UpdateItem(request, item);
|
||||
|
||||
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Post(UpdateGameGenre request)
|
||||
|
@ -189,7 +189,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
UpdateItem(request, item);
|
||||
|
||||
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Post(UpdateGenre request)
|
||||
|
@ -205,7 +205,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
UpdateItem(request, item);
|
||||
|
||||
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void UpdateItem(BaseItemDto request, BaseItem item)
|
||||
|
|
|
@ -66,7 +66,8 @@
|
|||
<Compile Include="..\SharedVersion.cs">
|
||||
<Link>Properties\SharedVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AlbumsService.cs" />
|
||||
<Compile Include="Movies\CollectionService.cs" />
|
||||
<Compile Include="Music\AlbumsService.cs" />
|
||||
<Compile Include="AppThemeService.cs" />
|
||||
<Compile Include="BaseApiService.cs" />
|
||||
<Compile Include="ConfigurationService.cs" />
|
||||
|
@ -81,7 +82,7 @@
|
|||
<Compile Include="Images\ImageRequest.cs" />
|
||||
<Compile Include="Images\ImageService.cs" />
|
||||
<Compile Include="Images\ImageWriter.cs" />
|
||||
<Compile Include="InstantMixService.cs" />
|
||||
<Compile Include="Music\InstantMixService.cs" />
|
||||
<Compile Include="ItemLookupService.cs" />
|
||||
<Compile Include="ItemRefreshService.cs" />
|
||||
<Compile Include="ItemUpdateService.cs" />
|
||||
|
@ -91,7 +92,7 @@
|
|||
<Compile Include="Library\LibraryStructureService.cs" />
|
||||
<Compile Include="LiveTv\LiveTvService.cs" />
|
||||
<Compile Include="LocalizationService.cs" />
|
||||
<Compile Include="MoviesService.cs" />
|
||||
<Compile Include="Movies\MoviesService.cs" />
|
||||
<Compile Include="NewsService.cs" />
|
||||
<Compile Include="NotificationsService.cs" />
|
||||
<Compile Include="PackageReviewService.cs" />
|
||||
|
@ -118,7 +119,7 @@
|
|||
<Compile Include="SessionsService.cs" />
|
||||
<Compile Include="SimilarItemsHelper.cs" />
|
||||
<Compile Include="SystemService.cs" />
|
||||
<Compile Include="TrailersService.cs" />
|
||||
<Compile Include="Movies\TrailersService.cs" />
|
||||
<Compile Include="TvShowsService.cs" />
|
||||
<Compile Include="UserLibrary\ArtistsService.cs" />
|
||||
<Compile Include="UserLibrary\BaseItemsByNameService.cs" />
|
||||
|
|
80
MediaBrowser.Api/Movies/CollectionService.cs
Normal file
80
MediaBrowser.Api/Movies/CollectionService.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
using MediaBrowser.Controller.Collections;
|
||||
using ServiceStack;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.Movies
|
||||
{
|
||||
[Route("/Collections", "POST")]
|
||||
[Api(Description = "Creates a new collection")]
|
||||
public class CreateCollection : IReturnVoid
|
||||
{
|
||||
[ApiMember(Name = "IsLocked", Description = "Whether or not to lock the new collection.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
|
||||
public bool IsLocked { get; set; }
|
||||
|
||||
[ApiMember(Name = "Name", Description = "The name of the new collection.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[ApiMember(Name = "ParentId", Description = "Optional - create the collection within a specific folder", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public Guid? ParentId { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Collections/{Id}/Items", "POST")]
|
||||
[Api(Description = "Adds items to a collection")]
|
||||
public class AddToCollection : IReturnVoid
|
||||
{
|
||||
[ApiMember(Name = "Ids", Description = "Item id, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string Ids { get; set; }
|
||||
|
||||
[ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Collections/{Id}/Items", "DELETE")]
|
||||
[Api(Description = "Removes items from a collection")]
|
||||
public class RemoveFromCollection : IReturnVoid
|
||||
{
|
||||
[ApiMember(Name = "Ids", Description = "Item id, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string Ids { get; set; }
|
||||
|
||||
[ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
public class CollectionService : BaseApiService
|
||||
{
|
||||
private readonly ICollectionManager _collectionManager;
|
||||
|
||||
public CollectionService(ICollectionManager collectionManager)
|
||||
{
|
||||
_collectionManager = collectionManager;
|
||||
}
|
||||
|
||||
public void Post(CreateCollection request)
|
||||
{
|
||||
var task = _collectionManager.CreateCollection(new CollectionCreationOptions
|
||||
{
|
||||
IsLocked = request.IsLocked,
|
||||
Name = request.Name,
|
||||
ParentId = request.ParentId
|
||||
});
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
public void Post(AddToCollection request)
|
||||
{
|
||||
var task = _collectionManager.AddToCollection(request.Id, request.Ids.Split(',').Select(i => new Guid(i)));
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
public void Delete(RemoveFromCollection request)
|
||||
{
|
||||
var task = _collectionManager.RemoveFromCollection(request.Id, request.Ids.Split(',').Select(i => new Guid(i)));
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
}
|
||||
}
|
324
MediaBrowser.Api/Movies/MoviesService.cs
Normal file
324
MediaBrowser.Api/Movies/MoviesService.cs
Normal file
|
@ -0,0 +1,324 @@
|
|||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using ServiceStack;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Api.Movies
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetSimilarMovies
|
||||
/// </summary>
|
||||
[Route("/Movies/{Id}/Similar", "GET")]
|
||||
[Api(Description = "Finds movies and trailers similar to a given movie.")]
|
||||
public class GetSimilarMovies : BaseGetSimilarItemsFromItem
|
||||
{
|
||||
[ApiMember(Name = "IncludeTrailers", Description = "Whether or not to include trailers within the results. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool IncludeTrailers { get; set; }
|
||||
|
||||
public GetSimilarMovies()
|
||||
{
|
||||
IncludeTrailers = true;
|
||||
}
|
||||
}
|
||||
|
||||
[Route("/Movies/Recommendations", "GET")]
|
||||
[Api(Description = "Gets movie recommendations")]
|
||||
public class GetMovieRecommendations : IReturn<RecommendationDto[]>, IHasItemFields
|
||||
{
|
||||
[ApiMember(Name = "CategoryLimit", Description = "The max number of categories to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int CategoryLimit { get; set; }
|
||||
|
||||
[ApiMember(Name = "ItemLimit", Description = "The max number of items to return per category", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int ItemLimit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public Guid? UserId { get; set; }
|
||||
|
||||
public GetMovieRecommendations()
|
||||
{
|
||||
CategoryLimit = 5;
|
||||
ItemLimit = 8;
|
||||
}
|
||||
|
||||
public string Fields { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class MoviesService
|
||||
/// </summary>
|
||||
public class MoviesService : BaseApiService
|
||||
{
|
||||
/// <summary>
|
||||
/// The _user manager
|
||||
/// </summary>
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
/// <summary>
|
||||
/// The _user data repository
|
||||
/// </summary>
|
||||
private readonly IUserDataManager _userDataRepository;
|
||||
/// <summary>
|
||||
/// The _library manager
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
private readonly IItemRepository _itemRepo;
|
||||
private readonly IDtoService _dtoService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MoviesService"/> class.
|
||||
/// </summary>
|
||||
/// <param name="userManager">The user manager.</param>
|
||||
/// <param name="userDataRepository">The user data repository.</param>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_userDataRepository = userDataRepository;
|
||||
_libraryManager = libraryManager;
|
||||
_itemRepo = itemRepo;
|
||||
_dtoService = dtoService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetSimilarMovies request)
|
||||
{
|
||||
var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager,
|
||||
_itemRepo,
|
||||
_libraryManager,
|
||||
_userDataRepository,
|
||||
_dtoService,
|
||||
Logger,
|
||||
request, item => item is Movie || (item is Trailer && request.IncludeTrailers),
|
||||
SimilarItemsHelper.GetSimiliarityScore);
|
||||
|
||||
return ToOptimizedSerializedResultUsingCache(result);
|
||||
}
|
||||
|
||||
public object Get(GetMovieRecommendations request)
|
||||
{
|
||||
var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
|
||||
|
||||
var folder = user.RootFolder;
|
||||
var movies = folder.RecursiveChildren.OfType<Movie>().ToList();
|
||||
|
||||
var result = GetRecommendationCategories(user, movies, request.CategoryLimit, request.ItemLimit, request.GetItemFields().ToList());
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
private IEnumerable<RecommendationDto> GetRecommendationCategories(User user, List<Movie> allMovies, int categoryLimit, int itemLimit, List<ItemFields> fields)
|
||||
{
|
||||
var categories = new List<RecommendationDto>();
|
||||
|
||||
var recentlyPlayedMovies = allMovies
|
||||
.Select(i =>
|
||||
{
|
||||
var userdata = _userDataRepository.GetUserData(user.Id, i.GetUserDataKey());
|
||||
return new Tuple<Movie, bool, DateTime>(i, userdata.Played, userdata.LastPlayedDate ?? DateTime.MinValue);
|
||||
})
|
||||
.Where(i => i.Item2)
|
||||
.OrderByDescending(i => i.Item3)
|
||||
.Select(i => i.Item1)
|
||||
.ToList();
|
||||
|
||||
var excludeFromLiked = recentlyPlayedMovies.Take(10);
|
||||
var likedMovies = allMovies
|
||||
.Select(i =>
|
||||
{
|
||||
var score = 0;
|
||||
var userData = _userDataRepository.GetUserData(user.Id, i.GetUserDataKey());
|
||||
|
||||
if (userData.IsFavorite)
|
||||
{
|
||||
score = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
score = userData.Likes.HasValue ? userData.Likes.Value ? 1 : -1 : 0;
|
||||
}
|
||||
|
||||
return new Tuple<Movie, int>(i, score);
|
||||
})
|
||||
.OrderByDescending(i => i.Item2)
|
||||
.ThenBy(i => Guid.NewGuid())
|
||||
.Where(i => i.Item2 > 0)
|
||||
.Select(i => i.Item1)
|
||||
.Where(i => !excludeFromLiked.Contains(i));
|
||||
|
||||
var mostRecentMovies = recentlyPlayedMovies.Take(6).ToList();
|
||||
// Get recently played directors
|
||||
var recentDirectors = GetDirectors(mostRecentMovies)
|
||||
.OrderBy(i => Guid.NewGuid())
|
||||
.ToList();
|
||||
|
||||
// Get recently played actors
|
||||
var recentActors = GetActors(mostRecentMovies)
|
||||
.OrderBy(i => Guid.NewGuid())
|
||||
.ToList();
|
||||
|
||||
var similarToRecentlyPlayed = GetSimilarTo(user, allMovies, recentlyPlayedMovies.Take(7).OrderBy(i => Guid.NewGuid()), itemLimit, fields, RecommendationType.SimilarToRecentlyPlayed).GetEnumerator();
|
||||
var similarToLiked = GetSimilarTo(user, allMovies, likedMovies, itemLimit, fields, RecommendationType.SimilarToLikedItem).GetEnumerator();
|
||||
|
||||
var hasDirectorFromRecentlyPlayed = GetWithDirector(user, allMovies, recentDirectors, itemLimit, fields, RecommendationType.HasDirectorFromRecentlyPlayed).GetEnumerator();
|
||||
var hasActorFromRecentlyPlayed = GetWithActor(user, allMovies, recentActors, itemLimit, fields, RecommendationType.HasActorFromRecentlyPlayed).GetEnumerator();
|
||||
|
||||
var categoryTypes = new List<IEnumerator<RecommendationDto>>
|
||||
{
|
||||
// Give this extra weight
|
||||
similarToRecentlyPlayed,
|
||||
similarToRecentlyPlayed,
|
||||
|
||||
// Give this extra weight
|
||||
similarToLiked,
|
||||
similarToLiked,
|
||||
|
||||
hasDirectorFromRecentlyPlayed,
|
||||
hasActorFromRecentlyPlayed
|
||||
};
|
||||
|
||||
while (categories.Count < categoryLimit)
|
||||
{
|
||||
var allEmpty = true;
|
||||
|
||||
foreach (var category in categoryTypes)
|
||||
{
|
||||
if (category.MoveNext())
|
||||
{
|
||||
categories.Add(category.Current);
|
||||
allEmpty = false;
|
||||
|
||||
if (categories.Count >= categoryLimit)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allEmpty)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//// Get the lead actor for all movies
|
||||
//var allActors = GetActors(allMovies)
|
||||
// .ToList();
|
||||
|
||||
//foreach (var actor in recentActors)
|
||||
//{
|
||||
|
||||
//}
|
||||
|
||||
return categories.OrderBy(i => i.RecommendationType).ThenBy(i => Guid.NewGuid());
|
||||
}
|
||||
|
||||
private IEnumerable<RecommendationDto> GetWithDirector(User user, List<Movie> allMovies, IEnumerable<string> directors, int itemLimit, List<ItemFields> fields, RecommendationType type)
|
||||
{
|
||||
var userId = user.Id;
|
||||
|
||||
foreach (var director in directors)
|
||||
{
|
||||
var items = allMovies
|
||||
.Where(i => !_userDataRepository.GetUserData(userId, i.GetUserDataKey()).Played && i.People.Any(p => string.Equals(p.Type, PersonType.Director, StringComparison.OrdinalIgnoreCase) && string.Equals(p.Name, director, StringComparison.OrdinalIgnoreCase)))
|
||||
.Take(itemLimit)
|
||||
.ToList();
|
||||
|
||||
if (items.Count > 0)
|
||||
{
|
||||
yield return new RecommendationDto
|
||||
{
|
||||
BaselineItemName = director,
|
||||
CategoryId = director.GetMD5().ToString("N"),
|
||||
RecommendationType = type,
|
||||
Items = items.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<RecommendationDto> GetWithActor(User user, List<Movie> allMovies, IEnumerable<string> names, int itemLimit, List<ItemFields> fields, RecommendationType type)
|
||||
{
|
||||
var userId = user.Id;
|
||||
|
||||
foreach (var name in names)
|
||||
{
|
||||
var items = allMovies
|
||||
.Where(i => !_userDataRepository.GetUserData(userId, i.GetUserDataKey()).Played && i.People.Any(p => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase)))
|
||||
.Take(itemLimit)
|
||||
.ToList();
|
||||
|
||||
if (items.Count > 0)
|
||||
{
|
||||
yield return new RecommendationDto
|
||||
{
|
||||
BaselineItemName = name,
|
||||
CategoryId = name.GetMD5().ToString("N"),
|
||||
RecommendationType = type,
|
||||
Items = items.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<RecommendationDto> GetSimilarTo(User user, List<Movie> allMovies, IEnumerable<Movie> baselineItems, int itemLimit, List<ItemFields> fields, RecommendationType type)
|
||||
{
|
||||
var userId = user.Id;
|
||||
|
||||
foreach (var item in baselineItems)
|
||||
{
|
||||
var similar = SimilarItemsHelper
|
||||
.GetSimilaritems(item, allMovies, SimilarItemsHelper.GetSimiliarityScore)
|
||||
.Where(i => !_userDataRepository.GetUserData(userId, i.GetUserDataKey()).Played)
|
||||
.Take(itemLimit)
|
||||
.ToList();
|
||||
|
||||
if (similar.Count > 0)
|
||||
{
|
||||
yield return new RecommendationDto
|
||||
{
|
||||
BaselineItemName = item.Name,
|
||||
CategoryId = item.Id.ToString("N"),
|
||||
RecommendationType = type,
|
||||
Items = similar.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetActors(IEnumerable<BaseItem> items)
|
||||
{
|
||||
// Get the two leading actors for all movies
|
||||
return items
|
||||
.SelectMany(i => i.People.Where(p => !string.Equals(PersonType.Director, p.Type, StringComparison.OrdinalIgnoreCase)).Take(2))
|
||||
.Select(i => i.Name)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetDirectors(IEnumerable<BaseItem> items)
|
||||
{
|
||||
return items
|
||||
.Select(i => i.People.FirstOrDefault(p => string.Equals(PersonType.Director, p.Type, StringComparison.OrdinalIgnoreCase)))
|
||||
.Where(i => i != null)
|
||||
.Select(i => i.Name)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ using MediaBrowser.Controller.Library;
|
|||
using MediaBrowser.Controller.Persistence;
|
||||
using ServiceStack;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
namespace MediaBrowser.Api.Movies
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetSimilarTrailers
|
|
@ -1,82 +0,0 @@
|
|||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using ServiceStack;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetSimilarMovies
|
||||
/// </summary>
|
||||
[Route("/Movies/{Id}/Similar", "GET")]
|
||||
[Api(Description = "Finds movies and trailers similar to a given movie.")]
|
||||
public class GetSimilarMovies : BaseGetSimilarItemsFromItem
|
||||
{
|
||||
[ApiMember(Name = "IncludeTrailers", Description = "Whether or not to include trailers within the results. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool IncludeTrailers { get; set; }
|
||||
|
||||
public GetSimilarMovies()
|
||||
{
|
||||
IncludeTrailers = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class MoviesService
|
||||
/// </summary>
|
||||
public class MoviesService : BaseApiService
|
||||
{
|
||||
/// <summary>
|
||||
/// The _user manager
|
||||
/// </summary>
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
/// <summary>
|
||||
/// The _user data repository
|
||||
/// </summary>
|
||||
private readonly IUserDataManager _userDataRepository;
|
||||
/// <summary>
|
||||
/// The _library manager
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
private readonly IItemRepository _itemRepo;
|
||||
private readonly IDtoService _dtoService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MoviesService"/> class.
|
||||
/// </summary>
|
||||
/// <param name="userManager">The user manager.</param>
|
||||
/// <param name="userDataRepository">The user data repository.</param>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_userDataRepository = userDataRepository;
|
||||
_libraryManager = libraryManager;
|
||||
_itemRepo = itemRepo;
|
||||
_dtoService = dtoService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetSimilarMovies request)
|
||||
{
|
||||
var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager,
|
||||
_itemRepo,
|
||||
_libraryManager,
|
||||
_userDataRepository,
|
||||
_dtoService,
|
||||
Logger,
|
||||
request, item => item is Movie || (item is Trailer && request.IncludeTrailers),
|
||||
SimilarItemsHelper.GetSimiliarityScore);
|
||||
|
||||
return ToOptimizedSerializedResultUsingCache(result);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
namespace MediaBrowser.Api.Music
|
||||
{
|
||||
[Route("/Albums/{Id}/Similar", "GET")]
|
||||
[Api(Description = "Finds albums similar to a given album.")]
|
|
@ -7,7 +7,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
namespace MediaBrowser.Api.Music
|
||||
{
|
||||
[Route("/Songs/{Id}/InstantMix", "GET")]
|
||||
[Api(Description = "Creates an instant playlist based on a given song")]
|
|
@ -279,8 +279,19 @@ namespace MediaBrowser.Api.Playback
|
|||
/// </summary>
|
||||
/// <returns>System.Int32.</returns>
|
||||
/// <exception cref="System.Exception">Unrecognized MediaEncodingQuality value.</exception>
|
||||
protected int GetNumberOfThreads(bool isWebm)
|
||||
protected int GetNumberOfThreads(StreamState state, bool isWebm)
|
||||
{
|
||||
// Use more when this is true. -re will keep cpu usage under control
|
||||
if (state.ReadInputAtNativeFramerate)
|
||||
{
|
||||
if (isWebm)
|
||||
{
|
||||
return Math.Max(Environment.ProcessorCount - 1, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Webm: http://www.webmproject.org/docs/encoder-parameters/
|
||||
// The decoder will usually automatically use an appropriate number of threads according to how many cores are available but it can only use multiple threads
|
||||
// for the coefficient data if the encoder selected --token-parts > 0 at encode time.
|
||||
|
@ -491,16 +502,16 @@ namespace MediaBrowser.Api.Playback
|
|||
|
||||
return string.Format("{4} -vf \"{0}scale=trunc({1}/2)*2:trunc({2}/2)*2{3}\"", yadifParam, widthParam, heightParam, assSubtitleParam, copyTsParam);
|
||||
}
|
||||
|
||||
// If Max dimensions were supplied
|
||||
//this makes my brain hurt. For width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
|
||||
if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
|
||||
{
|
||||
var MaxwidthParam = request.MaxWidth.Value.ToString(UsCulture);
|
||||
var MaxheightParam = request.MaxHeight.Value.ToString(UsCulture);
|
||||
|
||||
return string.Format("{4} -vf \"{0}scale=trunc(min(iw\\,{1})/2)*2:trunc(min((iw/dar)\\,{2})/2)*2{3}\"", yadifParam, MaxwidthParam, MaxheightParam, assSubtitleParam, copyTsParam);
|
||||
}
|
||||
|
||||
// If Max dimensions were supplied
|
||||
//this makes my brain hurt. For width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
|
||||
if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
|
||||
{
|
||||
var MaxwidthParam = request.MaxWidth.Value.ToString(UsCulture);
|
||||
var MaxheightParam = request.MaxHeight.Value.ToString(UsCulture);
|
||||
|
||||
return string.Format("{4} -vf \"{0}scale=trunc(min(iw\\,{1})/2)*2:trunc(min((iw/dar)\\,{2})/2)*2{3}\"", yadifParam, MaxwidthParam, MaxheightParam, assSubtitleParam, copyTsParam);
|
||||
}
|
||||
|
||||
var isH264Output = outputVideoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
|
@ -603,7 +614,7 @@ namespace MediaBrowser.Api.Playback
|
|||
private string GetExtractedAssPath(StreamState state, bool performConversion)
|
||||
{
|
||||
var path = EncodingManager.GetSubtitleCachePath(state.MediaPath, state.SubtitleStream.Index, ".ass");
|
||||
|
||||
|
||||
if (performConversion)
|
||||
{
|
||||
InputType type;
|
||||
|
@ -987,20 +998,15 @@ namespace MediaBrowser.Api.Playback
|
|||
|
||||
if (state.VideoStream != null)
|
||||
{
|
||||
var isUpscaling = false;
|
||||
|
||||
if (state.VideoRequest.Height.HasValue && state.VideoStream.Height.HasValue &&
|
||||
state.VideoRequest.Height.Value > state.VideoStream.Height.Value)
|
||||
{
|
||||
isUpscaling = true;
|
||||
}
|
||||
var isUpscaling = state.VideoRequest.Height.HasValue && state.VideoStream.Height.HasValue &&
|
||||
state.VideoRequest.Height.Value > state.VideoStream.Height.Value;
|
||||
|
||||
if (state.VideoRequest.Width.HasValue && state.VideoStream.Width.HasValue &&
|
||||
state.VideoRequest.Width.Value > state.VideoStream.Width.Value)
|
||||
{
|
||||
isUpscaling = true;
|
||||
}
|
||||
|
||||
|
||||
// Don't allow bitrate increases unless upscaling
|
||||
if (!isUpscaling)
|
||||
{
|
||||
|
@ -1198,66 +1204,74 @@ namespace MediaBrowser.Api.Playback
|
|||
request.DeviceId = val;
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
else if (i == 2)
|
||||
{
|
||||
if (videoRequest != null)
|
||||
{
|
||||
videoRequest.VideoCodec = (VideoCodecs)Enum.Parse(typeof(VideoCodecs), val, true);
|
||||
}
|
||||
}
|
||||
else if (i == 2)
|
||||
else if (i == 3)
|
||||
{
|
||||
request.AudioCodec = (AudioCodecs)Enum.Parse(typeof(AudioCodecs), val, true);
|
||||
}
|
||||
else if (i == 3)
|
||||
else if (i == 4)
|
||||
{
|
||||
if (videoRequest != null)
|
||||
{
|
||||
videoRequest.AudioStreamIndex = int.Parse(val, UsCulture);
|
||||
}
|
||||
}
|
||||
else if (i == 4)
|
||||
else if (i == 5)
|
||||
{
|
||||
if (videoRequest != null)
|
||||
{
|
||||
videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture);
|
||||
}
|
||||
}
|
||||
else if (i == 5)
|
||||
else if (i == 6)
|
||||
{
|
||||
if (videoRequest != null)
|
||||
{
|
||||
videoRequest.VideoBitRate = int.Parse(val, UsCulture);
|
||||
}
|
||||
}
|
||||
else if (i == 6)
|
||||
else if (i == 7)
|
||||
{
|
||||
request.AudioBitRate = int.Parse(val, UsCulture);
|
||||
}
|
||||
else if (i == 7)
|
||||
else if (i == 8)
|
||||
{
|
||||
request.AudioChannels = int.Parse(val, UsCulture);
|
||||
}
|
||||
else if (i == 8)
|
||||
else if (i == 9)
|
||||
{
|
||||
if (videoRequest != null)
|
||||
{
|
||||
request.StartTimeTicks = long.Parse(val, UsCulture);
|
||||
}
|
||||
}
|
||||
else if (i == 9)
|
||||
else if (i == 10)
|
||||
{
|
||||
if (videoRequest != null)
|
||||
{
|
||||
videoRequest.Profile = val;
|
||||
}
|
||||
}
|
||||
else if (i == 10)
|
||||
else if (i == 11)
|
||||
{
|
||||
if (videoRequest != null)
|
||||
{
|
||||
videoRequest.Level = val;
|
||||
}
|
||||
}
|
||||
else if (i == 12)
|
||||
{
|
||||
request.ForcedMimeType = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1309,37 +1323,39 @@ namespace MediaBrowser.Api.Playback
|
|||
state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
|
||||
state.PlayableStreamFileNames = new List<string>();
|
||||
|
||||
if (!string.IsNullOrEmpty(recording.RecordingInfo.Path) && File.Exists(recording.RecordingInfo.Path))
|
||||
{
|
||||
state.MediaPath = recording.RecordingInfo.Path;
|
||||
state.IsRemote = false;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(recording.RecordingInfo.Url))
|
||||
{
|
||||
state.MediaPath = recording.RecordingInfo.Url;
|
||||
state.IsRemote = true;
|
||||
}
|
||||
else
|
||||
var path = recording.RecordingInfo.Path;
|
||||
var mediaUrl = recording.RecordingInfo.Url;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(path) && string.IsNullOrWhiteSpace(mediaUrl))
|
||||
{
|
||||
var streamInfo = await LiveTvManager.GetRecordingStream(request.Id, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
state.LiveTvStreamId = streamInfo.Id;
|
||||
|
||||
if (!string.IsNullOrEmpty(streamInfo.Path) && File.Exists(streamInfo.Path))
|
||||
{
|
||||
state.MediaPath = streamInfo.Path;
|
||||
state.IsRemote = false;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(streamInfo.Url))
|
||||
{
|
||||
state.MediaPath = streamInfo.Url;
|
||||
state.IsRemote = true;
|
||||
}
|
||||
path = streamInfo.Path;
|
||||
mediaUrl = streamInfo.Url;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(path) && File.Exists(path))
|
||||
{
|
||||
state.MediaPath = path;
|
||||
state.IsRemote = false;
|
||||
|
||||
state.SendInputOverStandardInput = recording.RecordingInfo.Status == RecordingStatus.InProgress;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(mediaUrl))
|
||||
{
|
||||
state.MediaPath = mediaUrl;
|
||||
state.IsRemote = true;
|
||||
}
|
||||
|
||||
//state.RunTimeTicks = recording.RunTimeTicks;
|
||||
if (recording.RecordingInfo.Status == RecordingStatus.InProgress && !state.IsRemote)
|
||||
{
|
||||
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
state.ReadInputAtNativeFramerate = recording.RecordingInfo.Status == RecordingStatus.InProgress;
|
||||
state.SendInputOverStandardInput = recording.RecordingInfo.Status == RecordingStatus.InProgress;
|
||||
state.AudioSync = "1000";
|
||||
state.DeInterlace = true;
|
||||
}
|
||||
|
@ -1359,6 +1375,8 @@ namespace MediaBrowser.Api.Playback
|
|||
{
|
||||
state.MediaPath = streamInfo.Path;
|
||||
state.IsRemote = false;
|
||||
|
||||
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(streamInfo.Url))
|
||||
{
|
||||
|
@ -1366,7 +1384,6 @@ namespace MediaBrowser.Api.Playback
|
|||
state.IsRemote = true;
|
||||
}
|
||||
|
||||
state.SendInputOverStandardInput = true;
|
||||
state.ReadInputAtNativeFramerate = true;
|
||||
state.AudioSync = "1000";
|
||||
state.DeInterlace = true;
|
||||
|
@ -1411,6 +1428,11 @@ namespace MediaBrowser.Api.Playback
|
|||
state.SubtitleStream = GetMediaStream(mediaStreams, videoRequest.SubtitleStreamIndex, MediaStreamType.Subtitle, false);
|
||||
state.AudioStream = GetMediaStream(mediaStreams, videoRequest.AudioStreamIndex, MediaStreamType.Audio);
|
||||
|
||||
if (state.VideoStream != null && state.VideoStream.IsInterlaced)
|
||||
{
|
||||
state.DeInterlace = true;
|
||||
}
|
||||
|
||||
EnforceResolutionLimit(state, videoRequest);
|
||||
}
|
||||
else
|
||||
|
@ -1420,8 +1442,8 @@ namespace MediaBrowser.Api.Playback
|
|||
|
||||
state.HasMediaStreams = mediaStreams.Count > 0;
|
||||
|
||||
state.SegmentLength = state.ReadInputAtNativeFramerate ? 3 : 10;
|
||||
state.HlsListSize = state.ReadInputAtNativeFramerate ? 20 : 1440;
|
||||
state.SegmentLength = state.ReadInputAtNativeFramerate ? 5 : 10;
|
||||
state.HlsListSize = state.ReadInputAtNativeFramerate ? 100 : 1440;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -278,7 +278,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
|
||||
var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds);
|
||||
|
||||
var threads = GetNumberOfThreads(false);
|
||||
var threads = GetNumberOfThreads(state, false);
|
||||
|
||||
var inputModifier = GetInputModifier(state);
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
|
||||
const string vn = " -vn";
|
||||
|
||||
var threads = GetNumberOfThreads(false);
|
||||
var threads = GetNumberOfThreads(state, false);
|
||||
|
||||
var inputModifier = GetInputModifier(state);
|
||||
|
||||
|
|
|
@ -214,12 +214,16 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
|
||||
if (request.Static)
|
||||
{
|
||||
return ResultFactory.GetStaticFileResult(Request, state.MediaPath, FileShare.Read, responseHeaders, isHeadRequest);
|
||||
var contentType = state.GetMimeType(state.MediaPath);
|
||||
|
||||
return ResultFactory.GetStaticFileResult(Request, state.MediaPath, contentType, FileShare.Read, responseHeaders, isHeadRequest);
|
||||
}
|
||||
|
||||
if (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive))
|
||||
{
|
||||
return ResultFactory.GetStaticFileResult(Request, outputPath, FileShare.Read, responseHeaders, isHeadRequest);
|
||||
var contentType = state.GetMimeType(outputPath);
|
||||
|
||||
return ResultFactory.GetStaticFileResult(Request, outputPath, contentType, FileShare.Read, responseHeaders, isHeadRequest);
|
||||
}
|
||||
|
||||
return GetStreamResult(state, responseHeaders, isHeadRequest).Result;
|
||||
|
@ -287,7 +291,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
|
||||
responseHeaders["Accept-Ranges"] = "none";
|
||||
|
||||
var contentType = MimeTypes.GetMimeType(outputPath);
|
||||
var contentType = state.GetMimeType(outputPath);
|
||||
|
||||
// Headers only
|
||||
if (isHeadRequest)
|
||||
|
|
|
@ -102,7 +102,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
format = " -f mp4 -movflags frag_keyframe+empty_moov";
|
||||
}
|
||||
|
||||
var threads = GetNumberOfThreads(string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase));
|
||||
var threads = GetNumberOfThreads(state, string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var inputModifier = GetInputModifier(state);
|
||||
|
||||
|
|
|
@ -66,6 +66,8 @@ namespace MediaBrowser.Api.Playback
|
|||
public bool ThrowDebugError { get; set; }
|
||||
|
||||
public string Params { get; set; }
|
||||
|
||||
public string ForcedMimeType { get; set; }
|
||||
}
|
||||
|
||||
public class VideoStreamRequest : StreamRequest
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
@ -72,5 +73,20 @@ namespace MediaBrowser.Api.Playback
|
|||
public string InputVideoCodec { get; set; }
|
||||
|
||||
public string InputAudioCodec { get; set; }
|
||||
|
||||
public string GetMimeType(string outputPath)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(Request.ForcedMimeType))
|
||||
{
|
||||
if (VideoRequest == null)
|
||||
{
|
||||
return "audio/" + Request.ForcedMimeType;
|
||||
}
|
||||
|
||||
return "video/" + Request.ForcedMimeType;
|
||||
}
|
||||
|
||||
return MimeTypes.GetMimeType(outputPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using MediaBrowser.Controller.Drawing;
|
||||
using System;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
|
@ -63,6 +64,9 @@ namespace MediaBrowser.Api
|
|||
[ApiMember(Name = "IncludeArtists", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool IncludeArtists { get; set; }
|
||||
|
||||
[ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
|
||||
public string IncludeItemTypes { get; set; }
|
||||
|
||||
public GetSearchHints()
|
||||
{
|
||||
IncludeArtists = true;
|
||||
|
@ -130,7 +134,8 @@ namespace MediaBrowser.Api
|
|||
IncludePeople = request.IncludePeople,
|
||||
IncludeStudios = request.IncludeStudios,
|
||||
StartIndex = request.StartIndex,
|
||||
UserId = request.UserId
|
||||
UserId = request.UserId,
|
||||
IncludeItemTypes = (request.IncludeItemTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray()
|
||||
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
|
@ -206,7 +211,8 @@ namespace MediaBrowser.Api
|
|||
|
||||
result.SongCount = songs.Count;
|
||||
|
||||
result.Artists = _libraryManager.GetAllArtists(songs)
|
||||
result.Artists = songs.SelectMany(i => i.AllArtists)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
|
||||
result.AlbumArtist = songs.Select(i => i.AlbumArtist).FirstOrDefault(i => !string.IsNullOrEmpty(i));
|
||||
|
|
|
@ -211,7 +211,7 @@ namespace MediaBrowser.Api
|
|||
[ApiMember(Name = "PlayableMediaTypes", Description = "A list of playable media types, comma delimited. Audio, Video, Book, Game, Photo.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string PlayableMediaTypes { get; set; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Class SessionsService
|
||||
/// </summary>
|
||||
|
@ -368,4 +368,4 @@ namespace MediaBrowser.Api
|
|||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -73,7 +73,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
var item = string.IsNullOrEmpty(request.Id) ?
|
||||
(request.UserId.HasValue ? user.RootFolder :
|
||||
(Folder)libraryManager.RootFolder) : dtoService.GetItemByDtoId(request.Id, request.UserId);
|
||||
libraryManager.RootFolder) : dtoService.GetItemByDtoId(request.Id, request.UserId);
|
||||
|
||||
var fields = request.GetItemFields().ToList();
|
||||
|
||||
|
@ -81,7 +81,7 @@ namespace MediaBrowser.Api
|
|||
? libraryManager.RootFolder.GetRecursiveChildren(i => i.Id != item.Id)
|
||||
: user.RootFolder.GetRecursiveChildren(user, i => i.Id != item.Id);
|
||||
|
||||
var items = GetSimilaritems(item, inputItems, includeInSearch, getSimilarityScore)
|
||||
var items = GetSimilaritems(item, inputItems.Where(includeInSearch), getSimilarityScore)
|
||||
.ToList();
|
||||
|
||||
IEnumerable<BaseItem> returnItems = items;
|
||||
|
@ -106,12 +106,12 @@ namespace MediaBrowser.Api
|
|||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="inputItems">The input items.</param>
|
||||
/// <param name="includeInSearch">The include in search.</param>
|
||||
/// <param name="getSimilarityScore">The get similarity score.</param>
|
||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
internal static IEnumerable<BaseItem> GetSimilaritems(BaseItem item, IEnumerable<BaseItem> inputItems, Func<BaseItem, bool> includeInSearch, Func<BaseItem, BaseItem, int> getSimilarityScore)
|
||||
internal static IEnumerable<BaseItem> GetSimilaritems(BaseItem item, IEnumerable<BaseItem> inputItems, Func<BaseItem, BaseItem, int> getSimilarityScore)
|
||||
{
|
||||
inputItems = inputItems.Where(includeInSearch);
|
||||
var itemId = item.Id;
|
||||
inputItems = inputItems.Where(i => i.Id != itemId);
|
||||
|
||||
return inputItems.Select(i => new Tuple<BaseItem, int>(i, getSimilarityScore(item, i)))
|
||||
.Where(i => i.Item2 > 2)
|
||||
|
@ -153,7 +153,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
if (!string.IsNullOrEmpty(item1.OfficialRating) && string.Equals(item1.OfficialRating, item2.OfficialRating, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
points += 1;
|
||||
points += 10;
|
||||
}
|
||||
|
||||
// Find common genres
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace MediaBrowser.Api
|
|||
/// Class GetNextUpEpisodes
|
||||
/// </summary>
|
||||
[Route("/Shows/NextUp", "GET")]
|
||||
[Api(("Gets a list of currently installed plugins"))]
|
||||
[Api(("Gets a list of next up episodes"))]
|
||||
public class GetNextUpEpisodes : IReturn<ItemsResult>, IHasItemFields
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -53,6 +53,39 @@ namespace MediaBrowser.Api
|
|||
public string SeriesId { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Shows/Upcoming", "GET")]
|
||||
[Api(("Gets a list of upcoming episodes"))]
|
||||
public class GetUpcomingEpisodes : IReturn<ItemsResult>, IHasItemFields
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <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; }
|
||||
|
||||
/// <summary>
|
||||
/// Fields to return within the items, in addition to basic information
|
||||
/// </summary>
|
||||
/// <value>The fields.</value>
|
||||
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, OverviewHtml, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
|
||||
public string Fields { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Shows/{Id}/Similar", "GET")]
|
||||
[Api(Description = "Finds tv shows similar to a given one.")]
|
||||
public class GetSimilarShows : BaseGetSimilarItemsFromItem
|
||||
|
@ -85,7 +118,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
[ApiMember(Name = "SeasonId", Description = "Optional. Filter by season id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string SeasonId { get; set; }
|
||||
|
||||
|
||||
[ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool? IsMissing { get; set; }
|
||||
|
||||
|
@ -186,6 +219,39 @@ namespace MediaBrowser.Api
|
|||
return ToOptimizedSerializedResultUsingCache(result);
|
||||
}
|
||||
|
||||
public object Get(GetUpcomingEpisodes request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager)
|
||||
.OfType<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 pagedItems = ApplyPaging(previousEpisodes, request.StartIndex, request.Limit);
|
||||
|
||||
var fields = request.GetItemFields().ToList();
|
||||
|
||||
var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray();
|
||||
|
||||
var result = new ItemsResult
|
||||
{
|
||||
TotalRecordCount = itemsList.Count,
|
||||
Items = returnItems
|
||||
};
|
||||
|
||||
return ToOptimizedSerializedResultUsingCache(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
|
@ -198,7 +264,7 @@ namespace MediaBrowser.Api
|
|||
var itemsList = GetNextUpEpisodes(request)
|
||||
.ToList();
|
||||
|
||||
var pagedItems = ApplyPaging(request, itemsList);
|
||||
var pagedItems = ApplyPaging(itemsList, request.StartIndex, request.Limit);
|
||||
|
||||
var fields = request.GetItemFields().ToList();
|
||||
|
||||
|
@ -234,11 +300,13 @@ namespace MediaBrowser.Api
|
|||
|
||||
return FilterSeries(request, series)
|
||||
.AsParallel()
|
||||
.Select(i => GetNextUp(i, currentUser, request).Item1)
|
||||
.Where(i => i != null)
|
||||
.Select(i => GetNextUp(i, currentUser))
|
||||
.Where(i => i.Item1 != null)
|
||||
.OrderByDescending(i =>
|
||||
{
|
||||
var seriesUserData = _userDataManager.GetUserData(user.Id, i.Series.GetUserDataKey());
|
||||
var episode = i.Item1;
|
||||
|
||||
var seriesUserData = _userDataManager.GetUserData(user.Id, episode.Series.GetUserDataKey());
|
||||
|
||||
if (seriesUserData.IsFavorite)
|
||||
{
|
||||
|
@ -252,7 +320,9 @@ namespace MediaBrowser.Api
|
|||
|
||||
return 0;
|
||||
})
|
||||
.ThenByDescending(i => i.PremiereDate ?? DateTime.MinValue);
|
||||
.ThenByDescending(i =>i.Item2)
|
||||
.ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue)
|
||||
.Select(i => i.Item1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -260,9 +330,8 @@ namespace MediaBrowser.Api
|
|||
/// </summary>
|
||||
/// <param name="series">The series.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>Task{Episode}.</returns>
|
||||
private Tuple<Episode, DateTime> GetNextUp(Series series, User user, GetNextUpEpisodes request)
|
||||
private Tuple<Episode, DateTime> GetNextUp(Series series, User user)
|
||||
{
|
||||
// Get them in display order, then reverse
|
||||
var allEpisodes = series.GetSeasons(user, true, true)
|
||||
|
@ -321,21 +390,22 @@ namespace MediaBrowser.Api
|
|||
/// <summary>
|
||||
/// Applies the paging.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="startIndex">The start index.</param>
|
||||
/// <param name="limit">The limit.</param>
|
||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
private IEnumerable<BaseItem> ApplyPaging(GetNextUpEpisodes request, IEnumerable<BaseItem> items)
|
||||
private IEnumerable<BaseItem> ApplyPaging(IEnumerable<BaseItem> items, int? startIndex, int? limit)
|
||||
{
|
||||
// Start at
|
||||
if (request.StartIndex.HasValue)
|
||||
if (startIndex.HasValue)
|
||||
{
|
||||
items = items.Skip(request.StartIndex.Value);
|
||||
items = items.Skip(startIndex.Value);
|
||||
}
|
||||
|
||||
// Return limit
|
||||
if (request.Limit.HasValue)
|
||||
if (limit.HasValue)
|
||||
{
|
||||
items = items.Take(request.Limit.Value);
|
||||
items = items.Take(limit.Value);
|
||||
}
|
||||
|
||||
return items;
|
||||
|
@ -409,7 +479,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
public object Get(GetEpisodes request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
@ -435,7 +505,7 @@ namespace MediaBrowser.Api
|
|||
{
|
||||
throw new ResourceNotFoundException("No season exists with Id " + request.SeasonId);
|
||||
}
|
||||
|
||||
|
||||
episodes = season.GetEpisodes(user);
|
||||
}
|
||||
|
||||
|
|
|
@ -85,10 +85,10 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
{
|
||||
var user = UserManager.GetUserById(request.UserId.Value);
|
||||
|
||||
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
|
||||
return DtoService.GetItemByNameDto(item, fields.ToList(), user);
|
||||
}
|
||||
|
||||
return DtoService.GetBaseItemDto(item, fields.ToList());
|
||||
return DtoService.GetItemByNameDto(item, fields.ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -111,7 +111,10 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
|
||||
protected override IEnumerable<MusicArtist> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
|
||||
{
|
||||
return LibraryManager.GetAllArtists(items)
|
||||
return items
|
||||
.OfType<Audio>()
|
||||
.SelectMany(i => i.AllArtists)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.Select(name =>
|
||||
{
|
||||
try
|
||||
|
@ -126,10 +129,5 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
|
||||
}).Where(i => i != null);
|
||||
}
|
||||
|
||||
protected override IEnumerable<BaseItem> GetLibraryItems(MusicArtist item, IEnumerable<BaseItem> libraryItems)
|
||||
{
|
||||
return libraryItems.OfType<IHasArtist>().Where(i => i.HasArtist(item.Name)).Cast<BaseItem>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,15 +56,21 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
{
|
||||
User user = null;
|
||||
BaseItem item;
|
||||
List<BaseItem> libraryItems;
|
||||
|
||||
if (request.UserId.HasValue)
|
||||
{
|
||||
user = UserManager.GetUserById(request.UserId.Value);
|
||||
item = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : DtoService.GetItemByDtoId(request.ParentId, user.Id);
|
||||
|
||||
libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
item = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : DtoService.GetItemByDtoId(request.ParentId);
|
||||
|
||||
libraryItems = LibraryManager.RootFolder.RecursiveChildren.ToList();
|
||||
}
|
||||
|
||||
IEnumerable<BaseItem> items;
|
||||
|
@ -93,7 +99,7 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
|
||||
var filteredItems = FilterItems(request, extractedItems, user);
|
||||
|
||||
filteredItems = FilterByLibraryItems(request, filteredItems, user);
|
||||
filteredItems = FilterByLibraryItems(request, filteredItems, user, libraryItems);
|
||||
|
||||
filteredItems = ItemsService.ApplySortOrder(request, filteredItems, user, LibraryManager).Cast<TItemType>();
|
||||
|
||||
|
@ -122,45 +128,39 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
|
||||
var fields = request.GetItemFields().ToList();
|
||||
|
||||
var dtos = ibnItems.Select(i => GetDto(i, user, fields));
|
||||
var tuples = ibnItems.Select(i => new Tuple<TItemType, List<BaseItem>>(i, i.GetTaggedItems(libraryItems).ToList()));
|
||||
|
||||
var dtos = tuples.Select(i => GetDto(i.Item1, user, fields, i.Item2));
|
||||
|
||||
result.Items = dtos.Where(i => i != null).ToArray();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private IEnumerable<TItemType> FilterByLibraryItems(GetItemsByName request, IEnumerable<TItemType> items, User user)
|
||||
private IEnumerable<TItemType> FilterByLibraryItems(GetItemsByName request, IEnumerable<TItemType> items, User user, IEnumerable<BaseItem> libraryItems)
|
||||
{
|
||||
var filters = request.GetFilters().ToList();
|
||||
|
||||
if (filters.Contains(ItemFilter.IsPlayed))
|
||||
{
|
||||
var libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
|
||||
|
||||
items = items.Where(i => GetLibraryItems(i, libraryItems).All(l => l.IsPlayed(user)));
|
||||
items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsPlayed(user)));
|
||||
}
|
||||
|
||||
if (filters.Contains(ItemFilter.IsUnplayed))
|
||||
{
|
||||
var libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
|
||||
|
||||
items = items.Where(i => GetLibraryItems(i, libraryItems).All(l => l.IsUnplayed(user)));
|
||||
items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsUnplayed(user)));
|
||||
}
|
||||
|
||||
if (request.IsPlayed.HasValue)
|
||||
{
|
||||
var val = request.IsPlayed.Value;
|
||||
|
||||
var libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
|
||||
|
||||
items = items.Where(i => GetLibraryItems(i, libraryItems).All(l => l.IsPlayed(user)) == val);
|
||||
items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsPlayed(user)) == val);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<BaseItem> GetLibraryItems(TItemType item, IEnumerable<BaseItem> libraryItems);
|
||||
|
||||
/// <summary>
|
||||
/// Filters the items.
|
||||
/// </summary>
|
||||
|
@ -174,6 +174,10 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
{
|
||||
items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(request.NameStartsWith))
|
||||
{
|
||||
items = items.Where(i => string.Compare(request.NameStartsWith, i.SortName.Substring(0, 1), StringComparison.CurrentCultureIgnoreCase) == 0);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(request.NameLessThan))
|
||||
{
|
||||
|
@ -288,11 +292,11 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
/// <param name="item">The item.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="fields">The fields.</param>
|
||||
/// <param name="libraryItems">The library items.</param>
|
||||
/// <returns>Task{DtoBaseItem}.</returns>
|
||||
private BaseItemDto GetDto(TItemType item, User user, List<ItemFields> fields)
|
||||
private BaseItemDto GetDto(TItemType item, User user, List<ItemFields> fields, List<BaseItem> libraryItems)
|
||||
{
|
||||
var dto = user == null ? DtoService.GetBaseItemDto(item, fields) :
|
||||
DtoService.GetBaseItemDto(item, fields, user);
|
||||
var dto = DtoService.GetItemByNameDto(item, fields, libraryItems, user);
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
@ -313,9 +317,12 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
[ApiMember(Name = "NameStartsWithOrGreater", Description = "Optional filter by items whose name is sorted equally or greater than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string NameStartsWithOrGreater { get; set; }
|
||||
|
||||
[ApiMember(Name = "NameStartsWith", Description = "Optional filter by items whose name is sorted equally than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string NameStartsWith { get; set; }
|
||||
|
||||
[ApiMember(Name = "NameLessThan", Description = "Optional filter by items whose name is sorted less than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string NameLessThan { get; set; }
|
||||
|
||||
|
||||
public GetItemsByName()
|
||||
{
|
||||
Recursive = true;
|
||||
|
|
|
@ -76,10 +76,10 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
{
|
||||
var user = UserManager.GetUserById(request.UserId.Value);
|
||||
|
||||
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
|
||||
return DtoService.GetItemByNameDto(item, fields.ToList(), user);
|
||||
}
|
||||
|
||||
return DtoService.GetBaseItemDto(item, fields.ToList());
|
||||
return DtoService.GetItemByNameDto(item, fields.ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -109,10 +109,5 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.Select(name => LibraryManager.GetGameGenre(name));
|
||||
}
|
||||
|
||||
protected override IEnumerable<BaseItem> GetLibraryItems(GameGenre item, IEnumerable<BaseItem> libraryItems)
|
||||
{
|
||||
return libraryItems.Where(i => (i is Game) && i.Genres.Contains(item.Name, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,10 +81,10 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
{
|
||||
var user = UserManager.GetUserById(request.UserId.Value);
|
||||
|
||||
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
|
||||
return DtoService.GetItemByNameDto(item, fields.ToList(), user);
|
||||
}
|
||||
|
||||
return DtoService.GetBaseItemDto(item, fields.ToList());
|
||||
return DtoService.GetItemByNameDto(item, fields.ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -112,10 +112,5 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.Select(name => LibraryManager.GetGenre(name));
|
||||
}
|
||||
|
||||
protected override IEnumerable<BaseItem> GetLibraryItems(Genre item, IEnumerable<BaseItem> libraryItems)
|
||||
{
|
||||
return libraryItems.Where(i => !(i is Game) && !(i is IHasMusicGenres) && i.Genres.Contains(item.Name, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,6 +111,12 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
[ApiMember(Name = "NameStartsWithOrGreater", Description = "Optional filter by items whose name is sorted equally or greater than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string NameStartsWithOrGreater { get; set; }
|
||||
|
||||
[ApiMember(Name = "NameStartsWith", Description = "Optional filter by items whose name is sorted equally than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string NameStartsWith { get; set; }
|
||||
|
||||
[ApiMember(Name = "NameLessThan", Description = "Optional filter by items whose name is equally or lesser than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string NameLessThan { get; set; }
|
||||
|
||||
[ApiMember(Name = "AlbumArtistStartsWithOrGreater", Description = "Optional filter by items whose album artist is sorted equally or greater than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string AlbumArtistStartsWithOrGreater { get; set; }
|
||||
|
||||
|
@ -768,6 +774,15 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
{
|
||||
items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(request.NameStartsWith))
|
||||
{
|
||||
items = items.Where(i => string.Compare(request.NameStartsWith, i.SortName.Substring(0, 1), StringComparison.CurrentCultureIgnoreCase) == 0);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(request.NameLessThan))
|
||||
{
|
||||
items = items.Where(i => string.Compare(request.NameLessThan, i.SortName, StringComparison.CurrentCultureIgnoreCase) == 1);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(request.AlbumArtistStartsWithOrGreater))
|
||||
{
|
||||
|
|
|
@ -76,10 +76,10 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
{
|
||||
var user = UserManager.GetUserById(request.UserId.Value);
|
||||
|
||||
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
|
||||
return DtoService.GetItemByNameDto(item, fields.ToList(), user);
|
||||
}
|
||||
|
||||
return DtoService.GetBaseItemDto(item, fields.ToList());
|
||||
return DtoService.GetItemByNameDto(item, fields.ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -109,10 +109,5 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.Select(name => LibraryManager.GetMusicGenre(name));
|
||||
}
|
||||
|
||||
protected override IEnumerable<BaseItem> GetLibraryItems(MusicGenre item, IEnumerable<BaseItem> libraryItems)
|
||||
{
|
||||
return libraryItems.Where(i => (i is IHasMusicGenres) && i.Genres.Contains(item.Name, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,10 +93,10 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
{
|
||||
var user = UserManager.GetUserById(request.UserId.Value);
|
||||
|
||||
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
|
||||
return DtoService.GetItemByNameDto(item, fields.ToList(), user);
|
||||
}
|
||||
|
||||
return DtoService.GetBaseItemDto(item, fields.ToList());
|
||||
return DtoService.GetItemByNameDto(item, fields.ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -163,10 +163,5 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
|
||||
people.Where(p => personTypes.Contains(p.Type ?? string.Empty, StringComparer.OrdinalIgnoreCase) || personTypes.Contains(p.Role ?? string.Empty, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
protected override IEnumerable<BaseItem> GetLibraryItems(Person item, IEnumerable<BaseItem> libraryItems)
|
||||
{
|
||||
return libraryItems.Where(i => i.People.Any(p => string.Equals(p.Name, item.Name, StringComparison.OrdinalIgnoreCase)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,10 +81,10 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
{
|
||||
var user = UserManager.GetUserById(request.UserId.Value);
|
||||
|
||||
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
|
||||
return DtoService.GetItemByNameDto(item, fields.ToList(), user);
|
||||
}
|
||||
|
||||
return DtoService.GetBaseItemDto(item, fields.ToList());
|
||||
return DtoService.GetItemByNameDto(item, fields.ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -114,10 +114,5 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.Select(name => LibraryManager.GetStudio(name));
|
||||
}
|
||||
|
||||
protected override IEnumerable<BaseItem> GetLibraryItems(Studio item, IEnumerable<BaseItem> libraryItems)
|
||||
{
|
||||
return libraryItems.Where(i => i.Studios.Contains(item.Name, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -679,19 +679,35 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public object Post(MarkPlayedItem request)
|
||||
{
|
||||
var result = MarkPlayed(request).Result;
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
private async Task<UserItemDataDto> MarkPlayed(MarkPlayedItem request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
DateTime? datePlayed = null;
|
||||
|
||||
|
||||
if (!string.IsNullOrEmpty(request.DatePlayed))
|
||||
{
|
||||
datePlayed = DateTime.ParseExact(request.DatePlayed, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
|
||||
}
|
||||
|
||||
var task = UpdatePlayedStatus(user, request.Id, true, datePlayed);
|
||||
var session = GetSession();
|
||||
|
||||
return ToOptimizedResult(task.Result);
|
||||
var dto = await UpdatePlayedStatus(user, request.Id, true, datePlayed).ConfigureAwait(false);
|
||||
|
||||
foreach (var additionalUserInfo in session.AdditionalUsers)
|
||||
{
|
||||
var additionalUser = _userManager.GetUserById(new Guid(additionalUserInfo.UserId));
|
||||
|
||||
await UpdatePlayedStatus(additionalUser, request.Id, true, datePlayed).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
private SessionInfo GetSession()
|
||||
|
@ -780,13 +796,29 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
/// <param name="request">The request.</param>
|
||||
public object Delete(MarkUnplayedItem request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var task = UpdatePlayedStatus(user, request.Id, false, null);
|
||||
var task = MarkUnplayed(request);
|
||||
|
||||
return ToOptimizedResult(task.Result);
|
||||
}
|
||||
|
||||
private async Task<UserItemDataDto> MarkUnplayed(MarkUnplayedItem request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var session = GetSession();
|
||||
|
||||
var dto = await UpdatePlayedStatus(user, request.Id, false, null).ConfigureAwait(false);
|
||||
|
||||
foreach (var additionalUserInfo in session.AdditionalUsers)
|
||||
{
|
||||
var additionalUser = _userManager.GetUserById(new Guid(additionalUserInfo.UserId));
|
||||
|
||||
await UpdatePlayedStatus(additionalUser, request.Id, false, null).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the played status.
|
||||
/// </summary>
|
||||
|
|
|
@ -7,7 +7,6 @@ using MediaBrowser.Model.Querying;
|
|||
using ServiceStack;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
|
@ -81,10 +80,10 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
{
|
||||
var user = UserManager.GetUserById(request.UserId.Value);
|
||||
|
||||
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
|
||||
return DtoService.GetItemByNameDto(item, fields.ToList(), user);
|
||||
}
|
||||
|
||||
return DtoService.GetBaseItemDto(item, fields.ToList());
|
||||
return DtoService.GetItemByNameDto(item, fields.ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -115,19 +114,5 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
.Distinct()
|
||||
.Select(year => LibraryManager.GetYear(year));
|
||||
}
|
||||
|
||||
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||
|
||||
protected override IEnumerable<BaseItem> GetLibraryItems(Year item, IEnumerable<BaseItem> libraryItems)
|
||||
{
|
||||
int year;
|
||||
|
||||
if (!int.TryParse(item.Name, NumberStyles.Integer, UsCulture, out year))
|
||||
{
|
||||
return libraryItems;
|
||||
}
|
||||
|
||||
return libraryItems.Where(i => i.ProductionYear.HasValue && i.ProductionYear.Value == year);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
67
MediaBrowser.Controller/Channels/ChannelItemInfo.cs
Normal file
67
MediaBrowser.Controller/Channels/ChannelItemInfo.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Controller.Channels
|
||||
{
|
||||
public class ChannelItemInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Id { get; set; }
|
||||
|
||||
public ChannelItemType Type { get; set; }
|
||||
|
||||
public string OfficialRating { get; set; }
|
||||
|
||||
public string Overview { get; set; }
|
||||
|
||||
public List<string> Genres { get; set; }
|
||||
|
||||
public List<PersonInfo> People { get; set; }
|
||||
|
||||
public float? CommunityRating { get; set; }
|
||||
|
||||
public long? RunTimeTicks { get; set; }
|
||||
|
||||
public bool IsInfinite { get; set; }
|
||||
|
||||
public string ImageUrl { get; set; }
|
||||
|
||||
public ChannelMediaType MediaType { get; set; }
|
||||
|
||||
public ChannelMediaContentType ContentType { get; set; }
|
||||
|
||||
public ChannelItemInfo()
|
||||
{
|
||||
Genres = new List<string>();
|
||||
People = new List<PersonInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChannelItemType
|
||||
{
|
||||
Media = 0,
|
||||
|
||||
Category = 1
|
||||
}
|
||||
|
||||
public enum ChannelMediaType
|
||||
{
|
||||
Audio = 0,
|
||||
|
||||
Video = 1
|
||||
}
|
||||
|
||||
public enum ChannelMediaContentType
|
||||
{
|
||||
Clip = 0,
|
||||
|
||||
Podcast = 1,
|
||||
|
||||
Trailer = 2,
|
||||
|
||||
Movie = 3,
|
||||
|
||||
Episode = 4
|
||||
}
|
||||
}
|
59
MediaBrowser.Controller/Channels/IChannel.cs
Normal file
59
MediaBrowser.Controller/Channels/IChannel.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Controller.Channels
|
||||
{
|
||||
public interface IChannel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the home page URL.
|
||||
/// </summary>
|
||||
/// <value>The home page URL.</value>
|
||||
string HomePageUrl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the capabilities.
|
||||
/// </summary>
|
||||
/// <returns>ChannelCapabilities.</returns>
|
||||
ChannelCapabilities GetCapabilities();
|
||||
|
||||
/// <summary>
|
||||
/// Searches the specified search term.
|
||||
/// </summary>
|
||||
/// <param name="searchTerm">The search term.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{IEnumerable{ChannelItemInfo}}.</returns>
|
||||
Task<IEnumerable<ChannelItemInfo>> Search(string searchTerm, User user, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the channel items.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{IEnumerable{ChannelItem}}.</returns>
|
||||
Task<IEnumerable<ChannelItemInfo>> GetChannelItems(User user, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the channel items.
|
||||
/// </summary>
|
||||
/// <param name="categoryId">The category identifier.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{IEnumerable{ChannelItem}}.</returns>
|
||||
Task<IEnumerable<ChannelItemInfo>> GetChannelItems(string categoryId, User user, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public class ChannelCapabilities
|
||||
{
|
||||
public bool CanSearch { get; set; }
|
||||
}
|
||||
}
|
12
MediaBrowser.Controller/Channels/IChannelManager.cs
Normal file
12
MediaBrowser.Controller/Channels/IChannelManager.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Controller.Channels
|
||||
{
|
||||
public interface IChannelManager
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Controller.Collections
|
||||
{
|
||||
public class CollectionCreationOptions : IHasProviderIds
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public Guid? ParentId { get; set; }
|
||||
|
||||
public bool IsLocked { get; set; }
|
||||
|
||||
public Dictionary<string, string> ProviderIds { get; set; }
|
||||
|
||||
public CollectionCreationOptions()
|
||||
{
|
||||
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
32
MediaBrowser.Controller/Collections/ICollectionManager.cs
Normal file
32
MediaBrowser.Controller/Collections/ICollectionManager.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Controller.Collections
|
||||
{
|
||||
public interface ICollectionManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the collection.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task CreateCollection(CollectionCreationOptions options);
|
||||
|
||||
/// <summary>
|
||||
/// Adds to collection.
|
||||
/// </summary>
|
||||
/// <param name="collectionId">The collection identifier.</param>
|
||||
/// <param name="itemIds">The item ids.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task AddToCollection(Guid collectionId, IEnumerable<Guid> itemIds);
|
||||
|
||||
/// <summary>
|
||||
/// Removes from collection.
|
||||
/// </summary>
|
||||
/// <param name="collectionId">The collection identifier.</param>
|
||||
/// <param name="itemIds">The item ids.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds);
|
||||
}
|
||||
}
|
|
@ -73,5 +73,26 @@ namespace MediaBrowser.Controller.Dto
|
|||
/// <param name="owner">The owner.</param>
|
||||
/// <returns>Task{BaseItemDto}.</returns>
|
||||
BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item by name dto.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="fields">The fields.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>BaseItemDto.</returns>
|
||||
BaseItemDto GetItemByNameDto<T>(T item, List<ItemFields> fields, User user = null)
|
||||
where T : BaseItem, IItemByName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item by name dto.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="fields">The fields.</param>
|
||||
/// <param name="taggedItems">The tagged items.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>BaseItemDto.</returns>
|
||||
BaseItemDto GetItemByNameDto<T>(T item, List<ItemFields> fields, List<BaseItem> taggedItems, User user = null)
|
||||
where T : BaseItem, IItemByName;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,24 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||
/// <value>The artist.</value>
|
||||
public List<string> Artists { get; set; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
public List<string> AllArtists
|
||||
{
|
||||
get
|
||||
{
|
||||
var list = new List<string>();
|
||||
|
||||
if (!string.IsNullOrEmpty(AlbumArtist))
|
||||
{
|
||||
list.Add(AlbumArtist);
|
||||
}
|
||||
list.AddRange(Artists);
|
||||
|
||||
return list;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the album.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
using MediaBrowser.Common.Progress;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -17,9 +15,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||
/// </summary>
|
||||
public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasTags, IHasProductionLocations, IHasLookupInfo<ArtistInfo>
|
||||
{
|
||||
[IgnoreDataMember]
|
||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
||||
|
||||
public bool IsAccessedByName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -65,7 +60,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||
|
||||
public MusicArtist()
|
||||
{
|
||||
UserItemCountList = new List<ItemByNameCounts>();
|
||||
Tags = new List<string>();
|
||||
ProductionLocations = new List<string>();
|
||||
}
|
||||
|
@ -230,5 +224,10 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||
|
||||
return info;
|
||||
}
|
||||
|
||||
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
|
||||
{
|
||||
return inputItems.OfType<IHasArtist>().Where(i => i.HasArtist(Name)).Cast<BaseItem>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System.Runtime.Serialization;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities.Audio
|
||||
{
|
||||
|
@ -10,11 +9,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||
/// </summary>
|
||||
public class MusicGenre : BaseItem, IItemByName
|
||||
{
|
||||
public MusicGenre()
|
||||
{
|
||||
UserItemCountList = new List<ItemByNameCounts>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user data key.
|
||||
/// </summary>
|
||||
|
@ -24,9 +18,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||
return "MusicGenre-" + Name;
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the folder containing the item.
|
||||
/// If the item is a folder, it returns the folder itself
|
||||
|
@ -51,5 +42,10 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
|
||||
{
|
||||
return inputItems.Where(i => (i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,6 +124,15 @@ namespace MediaBrowser.Controller.Entities
|
|||
}
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public virtual bool IsHidden
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public virtual bool IsOwnedItem
|
||||
{
|
||||
|
@ -1175,7 +1184,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
return GetImageInfo(type, imageIndex) != null;
|
||||
}
|
||||
|
||||
public void SetImagePath(ImageType type, int index, FileInfo file)
|
||||
public void SetImagePath(ImageType type, int index, FileSystemInfo file)
|
||||
{
|
||||
if (type == ImageType.Chapter)
|
||||
{
|
||||
|
@ -1330,7 +1339,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <param name="images">The images.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="System.ArgumentException">Cannot call AddImages with chapter images</exception>
|
||||
public bool AddImages(ImageType imageType, IEnumerable<FileInfo> images)
|
||||
public bool AddImages(ImageType imageType, IEnumerable<FileSystemInfo> images)
|
||||
{
|
||||
if (imageType == ImageType.Chapter)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using MediaBrowser.Model.Entities;
|
||||
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -8,18 +7,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// </summary>
|
||||
public abstract class BasePluginFolder : Folder, ICollectionFolder, IByReferenceItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the location.
|
||||
/// </summary>
|
||||
/// <value>The type of the location.</value>
|
||||
public override LocationType LocationType
|
||||
{
|
||||
get
|
||||
{
|
||||
return LocationType.Virtual;
|
||||
}
|
||||
}
|
||||
|
||||
protected BasePluginFolder()
|
||||
{
|
||||
DisplayMediaType = "CollectionFolder";
|
||||
|
|
|
@ -264,7 +264,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
[IgnoreDataMember]
|
||||
public IEnumerable<BaseItem> Children
|
||||
{
|
||||
get { return ActualChildren; }
|
||||
get { return ActualChildren.Where(i => !i.IsHidden); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -745,9 +745,9 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
var list = new List<BaseItem>();
|
||||
|
||||
AddChildrenToList(user, includeLinkedChildren, list, false, null);
|
||||
var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, false, null);
|
||||
|
||||
return list;
|
||||
return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -905,13 +905,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <returns>BaseItem.</returns>
|
||||
private BaseItem GetLinkedChild(LinkedChild info)
|
||||
{
|
||||
if (string.IsNullOrEmpty(info.Path))
|
||||
{
|
||||
throw new ArgumentException("Encountered linked child with empty path.");
|
||||
}
|
||||
|
||||
BaseItem item = null;
|
||||
|
||||
// First get using the cached Id
|
||||
if (info.ItemId.HasValue)
|
||||
{
|
||||
|
@ -920,20 +913,19 @@ namespace MediaBrowser.Controller.Entities
|
|||
return null;
|
||||
}
|
||||
|
||||
item = LibraryManager.GetItemById(info.ItemId.Value);
|
||||
var itemById = LibraryManager.GetItemById(info.ItemId.Value);
|
||||
|
||||
if (itemById != null)
|
||||
{
|
||||
return itemById;
|
||||
}
|
||||
}
|
||||
|
||||
// If still null, search by path
|
||||
if (item == null)
|
||||
{
|
||||
item = LibraryManager.RootFolder.FindByPath(info.Path);
|
||||
}
|
||||
var item = FindLinkedChild(info);
|
||||
|
||||
// If still null, log
|
||||
if (item == null)
|
||||
{
|
||||
Logger.Warn("Unable to find linked item at {0}", info.Path);
|
||||
|
||||
// Don't keep searching over and over
|
||||
info.ItemId = Guid.Empty;
|
||||
}
|
||||
|
@ -946,6 +938,43 @@ namespace MediaBrowser.Controller.Entities
|
|||
return item;
|
||||
}
|
||||
|
||||
private BaseItem FindLinkedChild(LinkedChild info)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(info.Path))
|
||||
{
|
||||
var itemByPath = LibraryManager.RootFolder.FindByPath(info.Path);
|
||||
|
||||
if (itemByPath == null)
|
||||
{
|
||||
Logger.Warn("Unable to find linked item at path {0}", info.Path);
|
||||
}
|
||||
|
||||
return itemByPath;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(info.ItemName) && !string.IsNullOrWhiteSpace(info.ItemType))
|
||||
{
|
||||
return LibraryManager.RootFolder.RecursiveChildren.FirstOrDefault(i =>
|
||||
{
|
||||
if (string.Equals(i.Name, info.ItemName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.Equals(i.GetType().Name, info.ItemType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (info.ItemYear.HasValue)
|
||||
{
|
||||
return info.ItemYear.Value == (i.ProductionYear ?? -1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
|
||||
{
|
||||
var changesFound = false;
|
||||
|
@ -1106,5 +1135,10 @@ namespace MediaBrowser.Controller.Entities
|
|||
return GetRecursiveChildren(user).Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual)
|
||||
.All(i => i.IsUnplayed(user));
|
||||
}
|
||||
|
||||
public IEnumerable<BaseItem> GetHiddenChildren()
|
||||
{
|
||||
return ActualChildren.Where(i => i.IsHidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
using MediaBrowser.Model.Dto;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
public class GameGenre : BaseItem, IItemByName
|
||||
{
|
||||
public GameGenre()
|
||||
{
|
||||
UserItemCountList = new List<ItemByNameCounts>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user data key.
|
||||
/// </summary>
|
||||
|
@ -20,9 +15,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
return "GameGenre-" + Name;
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the folder containing the item.
|
||||
/// If the item is a folder, it returns the folder itself
|
||||
|
@ -47,5 +39,10 @@ namespace MediaBrowser.Controller.Entities
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
|
||||
{
|
||||
return inputItems.Where(i => (i is Game) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
|
@ -9,11 +10,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// </summary>
|
||||
public class Genre : BaseItem, IItemByName
|
||||
{
|
||||
public Genre()
|
||||
{
|
||||
UserItemCountList = new List<ItemByNameCounts>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user data key.
|
||||
/// </summary>
|
||||
|
@ -23,9 +19,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
return "Genre-" + Name;
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the folder containing the item.
|
||||
/// If the item is a folder, it returns the folder itself
|
||||
|
@ -50,5 +43,10 @@ namespace MediaBrowser.Controller.Entities
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
|
||||
{
|
||||
return inputItems.Where(i => !(i is Game) && !(i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <param name="type">The type.</param>
|
||||
/// <param name="index">The index.</param>
|
||||
/// <param name="file">The file.</param>
|
||||
void SetImagePath(ImageType type, int index, FileInfo file);
|
||||
void SetImagePath(ImageType type, int index, FileSystemInfo file);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified type has image.
|
||||
|
@ -129,7 +129,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <param name="imageType">Type of the image.</param>
|
||||
/// <param name="images">The images.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||
bool AddImages(ImageType imageType, IEnumerable<FileInfo> images);
|
||||
bool AddImages(ImageType imageType, IEnumerable<FileSystemInfo> images);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [is save local metadata enabled].
|
||||
|
@ -180,7 +180,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <param name="item">The item.</param>
|
||||
/// <param name="imageType">Type of the image.</param>
|
||||
/// <param name="file">The file.</param>
|
||||
public static void SetImagePath(this IHasImages item, ImageType imageType, FileInfo file)
|
||||
public static void SetImagePath(this IHasImages item, ImageType imageType, FileSystemInfo file)
|
||||
{
|
||||
item.SetImagePath(imageType, 0, file);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
using MediaBrowser.Model.Dto;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
|
@ -10,37 +7,16 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// </summary>
|
||||
public interface IItemByName
|
||||
{
|
||||
List<ItemByNameCounts> UserItemCountList { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the tagged items.
|
||||
/// </summary>
|
||||
/// <param name="inputItems">The input items.</param>
|
||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems);
|
||||
}
|
||||
|
||||
public interface IHasDualAccess : IItemByName
|
||||
{
|
||||
bool IsAccessedByName { get; }
|
||||
}
|
||||
|
||||
public static class ItemByNameExtensions
|
||||
{
|
||||
public static ItemByNameCounts GetItemByNameCounts(this IItemByName item, Guid userId)
|
||||
{
|
||||
if (userId == Guid.Empty)
|
||||
{
|
||||
throw new ArgumentNullException("userId");
|
||||
}
|
||||
|
||||
return item.UserItemCountList.FirstOrDefault(i => i.UserId == userId);
|
||||
}
|
||||
|
||||
public static void SetItemByNameCounts(this IItemByName item, Guid userId, ItemByNameCounts counts)
|
||||
{
|
||||
var current = item.UserItemCountList.FirstOrDefault(i => i.UserId == userId);
|
||||
|
||||
if (current != null)
|
||||
{
|
||||
item.UserItemCountList.Remove(current);
|
||||
}
|
||||
|
||||
counts.UserId = userId;
|
||||
item.UserItemCountList.Add(counts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,10 @@ namespace MediaBrowser.Controller.Entities
|
|||
public string Path { get; set; }
|
||||
public LinkedChildType Type { get; set; }
|
||||
|
||||
public string ItemName { get; set; }
|
||||
public string ItemType { get; set; }
|
||||
public int? ItemYear { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Serves as a cache
|
||||
/// </summary>
|
||||
|
@ -18,8 +22,8 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
public enum LinkedChildType
|
||||
{
|
||||
Manual = 1,
|
||||
Shortcut = 2
|
||||
Manual = 0,
|
||||
Shortcut = 1
|
||||
}
|
||||
|
||||
public class LinkedChildComparer : IEqualityComparer<LinkedChild>
|
||||
|
@ -35,7 +39,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
public int GetHashCode(LinkedChild obj)
|
||||
{
|
||||
return (obj.Path + obj.Type.ToString()).GetHashCode();
|
||||
return (obj.Path + obj.Type).GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
|
@ -10,19 +10,11 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// </summary>
|
||||
public class Person : BaseItem, IItemByName, IHasLookupInfo<PersonLookupInfo>
|
||||
{
|
||||
public Person()
|
||||
{
|
||||
UserItemCountList = new List<ItemByNameCounts>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the place of birth.
|
||||
/// </summary>
|
||||
/// <value>The place of birth.</value>
|
||||
public string PlaceOfBirth { get; set; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user data key.
|
||||
|
@ -62,6 +54,11 @@ namespace MediaBrowser.Controller.Entities
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
|
||||
{
|
||||
return inputItems.Where(i => i.People.Any(p => string.Equals(p.Name, Name, StringComparison.OrdinalIgnoreCase)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System.Runtime.Serialization;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
|
@ -10,11 +9,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// </summary>
|
||||
public class Studio : BaseItem, IItemByName
|
||||
{
|
||||
public Studio()
|
||||
{
|
||||
UserItemCountList = new List<ItemByNameCounts>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user data key.
|
||||
/// </summary>
|
||||
|
@ -24,9 +18,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
return "Studio-" + Name;
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the folder containing the item.
|
||||
/// If the item is a folder, it returns the folder itself
|
||||
|
@ -51,5 +42,10 @@ namespace MediaBrowser.Controller.Entities
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
|
||||
{
|
||||
return inputItems.Where(i => i.Studios.Contains(Name, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using MediaBrowser.Controller.Providers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
@ -23,7 +24,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
var hasChanges = base.BeforeMetadataRefresh();
|
||||
|
||||
if (string.Equals("default", Name, System.StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals("default", Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Name = "Media Folders";
|
||||
hasChanges = true;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System.Runtime.Serialization;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
|
@ -10,14 +9,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// </summary>
|
||||
public class Year : BaseItem, IItemByName
|
||||
{
|
||||
public Year()
|
||||
{
|
||||
UserItemCountList = new List<ItemByNameCounts>();
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user data key.
|
||||
/// </summary>
|
||||
|
@ -51,5 +42,19 @@ namespace MediaBrowser.Controller.Entities
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
|
||||
{
|
||||
int year;
|
||||
|
||||
var usCulture = new CultureInfo("en-US");
|
||||
|
||||
if (!int.TryParse(Name, NumberStyles.Integer, usCulture, out year))
|
||||
{
|
||||
return inputItems;
|
||||
}
|
||||
|
||||
return inputItems.Where(i => i.ProductionYear.HasValue && i.ProductionYear.Value == year);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -304,19 +304,6 @@ namespace MediaBrowser.Controller.Library
|
|||
/// <returns>System.String.</returns>
|
||||
string FindCollectionType(BaseItem item);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all artists.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{System.String}.</returns>
|
||||
IEnumerable<string> GetAllArtists();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all artists.
|
||||
/// </summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <returns>IEnumerable{System.String}.</returns>
|
||||
IEnumerable<string> GetAllArtists(IEnumerable<BaseItem> items);
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the root path list.
|
||||
/// </summary>
|
||||
|
|
|
@ -51,9 +51,8 @@ namespace MediaBrowser.Controller.Library
|
|||
/// Refreshes metadata for each user
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <param name="force">if set to <c>true</c> [force].</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task RefreshUsersMetadata(CancellationToken cancellationToken, bool force = false);
|
||||
Task RefreshUsersMetadata(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Renames the user.
|
||||
|
|
|
@ -27,94 +27,94 @@ namespace MediaBrowser.Controller.Library
|
|||
/// <summary>
|
||||
/// A season folder must contain one of these somewhere in the name
|
||||
/// </summary>
|
||||
private static readonly string[] SeasonFolderNames = new[]
|
||||
{
|
||||
"season",
|
||||
"sæson",
|
||||
"temporada",
|
||||
"saison",
|
||||
"staffel",
|
||||
"series",
|
||||
"сезон"
|
||||
};
|
||||
private static readonly string[] SeasonFolderNames =
|
||||
{
|
||||
"season",
|
||||
"sæson",
|
||||
"temporada",
|
||||
"saison",
|
||||
"staffel",
|
||||
"series",
|
||||
"сезон"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Used to detect paths that represent episodes, need to make sure they don't also
|
||||
/// match movie titles like "2001 A Space..."
|
||||
/// Currently we limit the numbers here to 2 digits to try and avoid this
|
||||
/// </summary>
|
||||
private static readonly Regex[] EpisodeExpressions = new[]
|
||||
{
|
||||
new Regex(
|
||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)[sS](?<seasonnumber>\d{1,4})[x,X]?[eE](?<epnumber>\d{1,3})[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})[^\\\/]*$",
|
||||
RegexOptions.Compiled)
|
||||
};
|
||||
private static readonly Regex[] MultipleEpisodeExpressions = new[]
|
||||
{
|
||||
new Regex(
|
||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[eExX](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})(-[xE]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )\d{1,4}[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled)
|
||||
};
|
||||
private static readonly Regex[] EpisodeExpressions =
|
||||
{
|
||||
new Regex(
|
||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)[sS](?<seasonnumber>\d{1,4})[x,X]?[eE](?<epnumber>\d{1,3})[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})[^\\\/]*$",
|
||||
RegexOptions.Compiled)
|
||||
};
|
||||
private static readonly Regex[] MultipleEpisodeExpressions =
|
||||
{
|
||||
new Regex(
|
||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[eExX](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})(-[xE]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )\d{1,4}[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||
RegexOptions.Compiled)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// To avoid the following matching movies they are only valid when contained in a folder which has been matched as a being season
|
||||
/// </summary>
|
||||
private static readonly Regex[] EpisodeExpressionsInASeasonFolder = new[]
|
||||
{
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<epnumber>\d{1,2})\s?-\s?[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
// 01 - blah.avi, 01-blah.avi
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<epnumber>\d{1,2})[^\d\\]*[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
// 01.avi, 01.blah.avi "01 - 22 blah.avi"
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seasonnumber>\d)(?<epnumber>\d{1,2})[^\d\\]+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
// 01.avi, 01.blah.avi
|
||||
new Regex(
|
||||
@".*(\\|\/)\D*\d+(?<epnumber>\d{2})",
|
||||
RegexOptions.Compiled)
|
||||
// hell0 - 101 - hello.avi
|
||||
private static readonly Regex[] EpisodeExpressionsInASeasonFolder =
|
||||
{
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<epnumber>\d{1,2})\s?-\s?[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
// 01 - blah.avi, 01-blah.avi
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<epnumber>\d{1,2})[^\d\\]*[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
// 01.avi, 01.blah.avi "01 - 22 blah.avi"
|
||||
new Regex(
|
||||
@".*(\\|\/)(?<seasonnumber>\d)(?<epnumber>\d{1,2})[^\d\\]+[^\\\/]*$",
|
||||
RegexOptions.Compiled),
|
||||
// 01.avi, 01.blah.avi
|
||||
new Regex(
|
||||
@".*(\\|\/)\D*\d+(?<epnumber>\d{2})",
|
||||
RegexOptions.Compiled)
|
||||
// hell0 - 101 - hello.avi
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the season number from path.
|
||||
|
@ -151,8 +151,8 @@ namespace MediaBrowser.Controller.Library
|
|||
/// <returns>System.Nullable{System.Int32}.</returns>
|
||||
private static int? GetSeasonNumberFromPathSubstring(string path)
|
||||
{
|
||||
int numericStart = -1;
|
||||
int length = 0;
|
||||
var numericStart = -1;
|
||||
var length = 0;
|
||||
|
||||
// Find out where the numbers start, and then keep going until they end
|
||||
for (var i = 0; i < path.Length; i++)
|
||||
|
|
|
@ -138,13 +138,6 @@ namespace MediaBrowser.Controller.LiveTv
|
|||
/// <param name="id">The identifier.</param>
|
||||
/// <returns>Channel.</returns>
|
||||
LiveTvChannel GetInternalChannel(string id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal program.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <returns>LiveTvProgram.</returns>
|
||||
LiveTvProgram GetInternalProgram(string id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the recording.
|
||||
|
|
|
@ -1,20 +1,13 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Controller.LiveTv
|
||||
{
|
||||
public class LiveTvChannel : BaseItem, IItemByName
|
||||
{
|
||||
public LiveTvChannel()
|
||||
{
|
||||
UserItemCountList = new List<ItemByNameCounts>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user data key.
|
||||
/// </summary>
|
||||
|
@ -24,9 +17,6 @@ namespace MediaBrowser.Controller.LiveTv
|
|||
return GetClientTypeName() + "-" + Name;
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the folder containing the item.
|
||||
/// If the item is a folder, it returns the folder itself
|
||||
|
@ -119,5 +109,10 @@ namespace MediaBrowser.Controller.LiveTv
|
|||
{
|
||||
return "Channel";
|
||||
}
|
||||
|
||||
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
|
||||
{
|
||||
return new List<BaseItem>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,6 +68,11 @@
|
|||
<Compile Include="..\SharedVersion.cs">
|
||||
<Link>Properties\SharedVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Channels\ChannelItemInfo.cs" />
|
||||
<Compile Include="Channels\IChannel.cs" />
|
||||
<Compile Include="Channels\IChannelManager.cs" />
|
||||
<Compile Include="Collections\CollectionCreationOptions.cs" />
|
||||
<Compile Include="Collections\ICollectionManager.cs" />
|
||||
<Compile Include="Drawing\IImageProcessor.cs" />
|
||||
<Compile Include="Drawing\ImageFormat.cs" />
|
||||
<Compile Include="Drawing\ImageProcessingOptions.cs" />
|
||||
|
|
|
@ -95,6 +95,18 @@ namespace MediaBrowser.Controller.Net
|
|||
/// <returns>System.Object.</returns>
|
||||
object GetStaticFileResult(IRequest requestContext, string path, FileShare fileShare = FileShare.Read, IDictionary<string, string> responseHeaders = null, bool isHeadRequest = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the static file result.
|
||||
/// </summary>
|
||||
/// <param name="requestContext">The request context.</param>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="contentType">Type of the content.</param>
|
||||
/// <param name="fileShare">The file share.</param>
|
||||
/// <param name="responseHeaders">The response headers.</param>
|
||||
/// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
object GetStaticFileResult(IRequest requestContext, string path, string contentType, FileShare fileShare = FileShare.Read, IDictionary<string, string> responseHeaders = null, bool isHeadRequest = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the optimized serialized result using cache.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System.Collections.Concurrent;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
@ -10,10 +10,8 @@ namespace MediaBrowser.Controller.Providers
|
|||
public interface IDirectoryService
|
||||
{
|
||||
List<FileSystemInfo> GetFileSystemEntries(string path);
|
||||
IEnumerable<FileInfo> GetFiles(string path);
|
||||
IEnumerable<DirectoryInfo> GetDirectories(string path);
|
||||
FileInfo GetFile(string path);
|
||||
DirectoryInfo GetDirectory(string path);
|
||||
IEnumerable<FileSystemInfo> GetFiles(string path);
|
||||
FileSystemInfo GetFile(string path);
|
||||
}
|
||||
|
||||
public class DirectoryService : IDirectoryService
|
||||
|
@ -50,31 +48,17 @@ namespace MediaBrowser.Controller.Providers
|
|||
return entries;
|
||||
}
|
||||
|
||||
public IEnumerable<FileInfo> GetFiles(string path)
|
||||
public IEnumerable<FileSystemInfo> GetFiles(string path)
|
||||
{
|
||||
return GetFileSystemEntries(path).OfType<FileInfo>();
|
||||
return GetFileSystemEntries(path).Where(i => (i.Attributes & FileAttributes.Directory) != FileAttributes.Directory);
|
||||
}
|
||||
|
||||
public IEnumerable<DirectoryInfo> GetDirectories(string path)
|
||||
{
|
||||
return GetFileSystemEntries(path).OfType<DirectoryInfo>();
|
||||
}
|
||||
|
||||
public FileInfo GetFile(string path)
|
||||
public FileSystemInfo GetFile(string path)
|
||||
{
|
||||
var directory = Path.GetDirectoryName(path);
|
||||
var filename = Path.GetFileName(path);
|
||||
|
||||
return GetFiles(directory).FirstOrDefault(i => string.Equals(i.Name, filename, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
|
||||
public DirectoryInfo GetDirectory(string path)
|
||||
{
|
||||
var directory = Path.GetDirectoryName(path);
|
||||
var name = Path.GetFileName(path);
|
||||
|
||||
return GetDirectories(directory).FirstOrDefault(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace MediaBrowser.Controller.Providers
|
|||
|
||||
public class LocalImageInfo
|
||||
{
|
||||
public FileInfo FileInfo { get; set; }
|
||||
public FileSystemInfo FileInfo { get; set; }
|
||||
public ImageType Type { get; set; }
|
||||
}
|
||||
|
||||
|
|
|
@ -76,6 +76,13 @@ namespace MediaBrowser.Controller.Session
|
|||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
Task OnPlaybackStopped(PlaybackStopInfo info);
|
||||
|
||||
/// <summary>
|
||||
/// Reports the session ended.
|
||||
/// </summary>
|
||||
/// <param name="sessionId">The session identifier.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task ReportSessionEnded(Guid sessionId);
|
||||
|
||||
/// <summary>
|
||||
/// Sends the system command.
|
||||
/// </summary>
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
</Compile>
|
||||
<Compile Include="PlayTo\Argument.cs" />
|
||||
<Compile Include="PlayTo\Configuration\DlnaProfile.cs" />
|
||||
<Compile Include="PlayTo\Configuration\PluginConfiguration.cs" />
|
||||
<Compile Include="PlayTo\Configuration\PlayToConfiguration.cs" />
|
||||
<Compile Include="PlayTo\Configuration\TranscodeSetting.cs" />
|
||||
<Compile Include="PlayTo\CurrentIdEventArgs.cs" />
|
||||
<Compile Include="PlayTo\Device.cs">
|
||||
|
|
|
@ -29,7 +29,12 @@
|
|||
FriendlyName = "^TV$",
|
||||
ModelNumber = @"1\.0",
|
||||
ModelName = "Samsung DTV DMR",
|
||||
TranscodeSettings = TranscodeSettings.GetDefaultTranscodingSettings()
|
||||
TranscodeSettings = new[]
|
||||
{
|
||||
new TranscodeSettings {Container = "mkv", MimeType = "x-mkv"},
|
||||
new TranscodeSettings {Container = "flac", TargetContainer = "mp3"},
|
||||
new TranscodeSettings {Container = "m4a", TargetContainer = "mp3"}
|
||||
}
|
||||
};
|
||||
|
||||
var profile1 = new DlnaProfile
|
||||
|
@ -38,7 +43,12 @@
|
|||
ClientType = "DLNA",
|
||||
FriendlyName = @"(^\[TV\][A-Z]{2}\d{2}(E|F)[A-Z]?\d{3,4}.*)|^\[TV\] Samsung",
|
||||
ModelNumber = @"(1\.0)|(AllShare1\.0)",
|
||||
TranscodeSettings = TranscodeSettings.GetDefaultTranscodingSettings()
|
||||
TranscodeSettings = new[]
|
||||
{
|
||||
new TranscodeSettings {Container = "mkv", MimeType = "x-mkv"},
|
||||
new TranscodeSettings {Container = "flac", TargetContainer = "mp3"},
|
||||
new TranscodeSettings {Container = "m4a", TargetContainer = "mp3"}
|
||||
}
|
||||
};
|
||||
|
||||
var profile2 = new DlnaProfile
|
||||
|
@ -47,7 +57,12 @@
|
|||
ClientType = "DLNA",
|
||||
FriendlyName = @"(^TV-\d{2}C\d{3}.*)|(^\[TV\][A-Z]{2}\d{2}(D)[A-Z]?\d{3,4}.*)|^\[TV\] Samsung",
|
||||
ModelNumber = @"(1\.0)|(AllShare1\.0)",
|
||||
TranscodeSettings = TranscodeSettings.GetDefaultTranscodingSettings()
|
||||
TranscodeSettings = new[]
|
||||
{
|
||||
new TranscodeSettings {Container = "mkv", MimeType = "x-mkv"},
|
||||
new TranscodeSettings {Container = "flac", TargetContainer = "mp3"},
|
||||
new TranscodeSettings {Container = "m4a", TargetContainer = "mp3"}
|
||||
}
|
||||
};
|
||||
|
||||
var profile3 = new DlnaProfile
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace MediaBrowser.Dlna.PlayTo.Configuration
|
||||
{
|
||||
|
@ -20,6 +21,14 @@ namespace MediaBrowser.Dlna.PlayTo.Configuration
|
|||
/// </value>
|
||||
public string TargetContainer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Mimetype to enforce
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The MimeType.
|
||||
/// </value>
|
||||
public string MimeType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The default transcoding settings
|
||||
/// </summary>
|
||||
|
@ -46,19 +55,19 @@ namespace MediaBrowser.Dlna.PlayTo.Configuration
|
|||
{
|
||||
if (!string.IsNullOrEmpty(profile.FriendlyName))
|
||||
{
|
||||
if (!string.Equals(deviceProperties.Name, profile.FriendlyName, StringComparison.OrdinalIgnoreCase))
|
||||
if (!Regex.IsMatch(deviceProperties.Name, profile.FriendlyName))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profile.ModelNumber))
|
||||
{
|
||||
if (!string.Equals(deviceProperties.ModelNumber, profile.ModelNumber, StringComparison.OrdinalIgnoreCase))
|
||||
if (!Regex.IsMatch(deviceProperties.ModelNumber, profile.ModelNumber))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profile.ModelName))
|
||||
{
|
||||
if (!string.Equals(deviceProperties.ModelName, profile.ModelName, StringComparison.OrdinalIgnoreCase))
|
||||
if (!Regex.IsMatch(deviceProperties.ModelName, profile.ModelName))
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -76,8 +76,8 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
|
||||
_transportState = value;
|
||||
|
||||
if (value == "PLAYING" || value == "STOPPED")
|
||||
NotifyPlaybackChanged(value == "STOPPED");
|
||||
if (value == TRANSPORTSTATE.PLAYING || value == TRANSPORTSTATE.STOPPED)
|
||||
NotifyPlaybackChanged(value == TRANSPORTSTATE.STOPPED);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
{
|
||||
get
|
||||
{
|
||||
return TransportState == "PLAYING";
|
||||
return TransportState == TRANSPORTSTATE.PLAYING;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
{
|
||||
get
|
||||
{
|
||||
return (TransportState == "TRANSITIONING");
|
||||
return (TransportState == TRANSPORTSTATE.TRANSITIONING);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
{
|
||||
get
|
||||
{
|
||||
return TransportState == "PAUSED" || TransportState == "PAUSED_PLAYBACK";
|
||||
return TransportState == TRANSPORTSTATE.PAUSED || TransportState == TRANSPORTSTATE.PAUSED_PLAYBACK;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
{
|
||||
get
|
||||
{
|
||||
return (TransportState == "STOPPED");
|
||||
return TransportState == TRANSPORTSTATE.STOPPED;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,23 +127,39 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
private int GetTimerIntervalMs()
|
||||
private int GetPlaybackTimerIntervalMs()
|
||||
{
|
||||
return 10000;
|
||||
return 2000;
|
||||
}
|
||||
|
||||
private int GetInactiveTimerIntervalMs()
|
||||
{
|
||||
return 20000;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
UpdateTime = DateTime.UtcNow;
|
||||
|
||||
var interval = GetTimerIntervalMs();
|
||||
var interval = GetPlaybackTimerIntervalMs();
|
||||
|
||||
_timer = new Timer(TimerCallback, null, interval, interval);
|
||||
}
|
||||
|
||||
private void RestartTimer()
|
||||
{
|
||||
var interval = GetTimerIntervalMs();
|
||||
var interval = GetPlaybackTimerIntervalMs();
|
||||
|
||||
_timer.Change(interval, interval);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Restarts the timer in inactive mode.
|
||||
/// </summary>
|
||||
private void RestartTimerInactive()
|
||||
{
|
||||
var interval = GetInactiveTimerIntervalMs();
|
||||
|
||||
_timer.Change(interval, interval);
|
||||
}
|
||||
|
@ -230,11 +246,9 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
{
|
||||
StopTimer();
|
||||
|
||||
TransportState = "STOPPED";
|
||||
await SetStop().ConfigureAwait(false);
|
||||
CurrentId = "0";
|
||||
|
||||
await Task.Delay(50).ConfigureAwait(false);
|
||||
|
||||
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
|
||||
if (command == null)
|
||||
return false;
|
||||
|
@ -261,7 +275,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
await SetPlay().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_count = 5;
|
||||
_lapsCount = GetLapsCount();
|
||||
RestartTimer();
|
||||
|
||||
return true;
|
||||
|
@ -322,7 +336,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType, 1))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
_count = 5;
|
||||
_lapsCount = GetLapsCount();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -338,7 +352,6 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
.ConfigureAwait(false);
|
||||
|
||||
await Task.Delay(50).ConfigureAwait(false);
|
||||
_count = 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -362,8 +375,13 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
|
||||
#region Get data
|
||||
|
||||
// TODO: What is going on here
|
||||
int _count = 5;
|
||||
private int GetLapsCount()
|
||||
{
|
||||
// No need to get all data every lap, just every X time.
|
||||
return 10;
|
||||
}
|
||||
|
||||
int _lapsCount = 0;
|
||||
|
||||
private async void TimerCallback(object sender)
|
||||
{
|
||||
|
@ -374,18 +392,24 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
|
||||
try
|
||||
{
|
||||
var hasTrack = await GetPositionInfo().ConfigureAwait(false);
|
||||
await GetTransportInfo().ConfigureAwait(false);
|
||||
|
||||
// TODO: Why make these requests if hasTrack==false?
|
||||
if (_count > 5)
|
||||
//If we're not playing anything no need to get additional data
|
||||
if (TransportState != TRANSPORTSTATE.STOPPED)
|
||||
{
|
||||
await GetTransportInfo().ConfigureAwait(false);
|
||||
if (!hasTrack)
|
||||
var hasTrack = await GetPositionInfo().ConfigureAwait(false);
|
||||
|
||||
// TODO: Why make these requests if hasTrack==false?
|
||||
// TODO ANSWER Some vendors don't include track in GetPositionInfo, use GetMediaInfo instead.
|
||||
if (_lapsCount > GetLapsCount())
|
||||
{
|
||||
await GetMediaInfo().ConfigureAwait(false);
|
||||
if (!hasTrack)
|
||||
{
|
||||
await GetMediaInfo().ConfigureAwait(false);
|
||||
}
|
||||
await GetVolume().ConfigureAwait(false);
|
||||
_lapsCount = 0;
|
||||
}
|
||||
await GetVolume().ConfigureAwait(false);
|
||||
_count = 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -393,11 +417,16 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
_logger.ErrorException("Error updating device info", ex);
|
||||
}
|
||||
|
||||
_count++;
|
||||
_lapsCount++;
|
||||
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
RestartTimer();
|
||||
//If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive
|
||||
if (TransportState != TRANSPORTSTATE.STOPPED)
|
||||
RestartTimer();
|
||||
else
|
||||
RestartTimerInactive();
|
||||
}
|
||||
|
||||
private async Task GetVolume()
|
||||
|
@ -747,5 +776,16 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
{
|
||||
return String.Format("{0} - {1}", Properties.Name, Properties.BaseUrl);
|
||||
}
|
||||
|
||||
private class TRANSPORTSTATE
|
||||
{
|
||||
public const string STOPPED = "STOPPED";
|
||||
public const string PLAYING = "PLAYING";
|
||||
public const string TRANSITIONING = "TRANSITIONING";
|
||||
public const string PAUSED_PLAYBACK = "PAUSED_PLAYBACK";
|
||||
public const string PAUSED = "PAUSED";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ using Timer = System.Timers.Timer;
|
|||
|
||||
namespace MediaBrowser.Dlna.PlayTo
|
||||
{
|
||||
public class PlayToController : ISessionController
|
||||
public class PlayToController : ISessionController, IDisposable
|
||||
{
|
||||
private Device _device;
|
||||
private BaseItem _currentItem = null;
|
||||
|
@ -131,6 +131,14 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
|
||||
((Timer)sender).Stop();
|
||||
|
||||
|
||||
if (!IsSessionActive)
|
||||
{
|
||||
//Session is inactive, mark it for Disposal and don't start the elapsed timer.
|
||||
await _sessionManager.ReportSessionEnded(this._session.Id);
|
||||
return;
|
||||
}
|
||||
|
||||
await ReportProgress().ConfigureAwait(false);
|
||||
|
||||
if (!_disposed && IsSessionActive)
|
||||
|
@ -479,3 +487,4 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,38 +1,105 @@
|
|||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Dlna.PlayTo
|
||||
{
|
||||
public class PlayToServerEntryPoint : IServerEntryPoint
|
||||
{
|
||||
private bool _disposed;
|
||||
private PlayToManager _manager;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly ILogger _logger;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IItemRepository _itemRepo;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly INetworkManager _networkManager;
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
private readonly PlayToManager _manager;
|
||||
|
||||
public PlayToServerEntryPoint(ILogManager logManager, ISessionManager sessionManager, IUserManager userManager, IHttpClient httpClient, INetworkManager networkManager, IItemRepository itemRepository, ILibraryManager libraryManager)
|
||||
public PlayToServerEntryPoint(ILogManager logManager, IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepo, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager)
|
||||
{
|
||||
_manager = new PlayToManager(logManager.GetLogger("PlayTo"), sessionManager, httpClient, itemRepository, libraryManager, networkManager, userManager);
|
||||
_config = config;
|
||||
_sessionManager = sessionManager;
|
||||
_httpClient = httpClient;
|
||||
_itemRepo = itemRepo;
|
||||
_libraryManager = libraryManager;
|
||||
_networkManager = networkManager;
|
||||
_userManager = userManager;
|
||||
_logger = logManager.GetLogger("PlayTo");
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
//_manager.Start();
|
||||
_config.ConfigurationUpdated += ConfigurationUpdated;
|
||||
ReloadPlayToManager();
|
||||
}
|
||||
|
||||
void ConfigurationUpdated(object sender, EventArgs e)
|
||||
{
|
||||
ReloadPlayToManager();
|
||||
}
|
||||
|
||||
private void ReloadPlayToManager()
|
||||
{
|
||||
var isStarted = _manager != null;
|
||||
|
||||
if (_config.Configuration.DlnaOptions.EnablePlayTo && !isStarted)
|
||||
{
|
||||
StartPlayToManager();
|
||||
}
|
||||
else if (!_config.Configuration.DlnaOptions.EnablePlayTo && isStarted)
|
||||
{
|
||||
DisposePlayToManager();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly object _syncLock = new object();
|
||||
private void StartPlayToManager()
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
_manager = new PlayToManager(_logger, _sessionManager, _httpClient, _itemRepo, _libraryManager, _networkManager, _userManager);
|
||||
_manager.Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error starting PlayTo manager", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DisposePlayToManager()
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (_manager != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_manager.Stop();
|
||||
_manager.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error disposing PlayTo manager", ex);
|
||||
}
|
||||
_manager = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Dispose
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_disposed = true;
|
||||
_manager.Stop();
|
||||
_manager.Dispose();
|
||||
}
|
||||
DisposePlayToManager();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -16,6 +16,8 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
|
||||
public string FileFormat { get; set; }
|
||||
|
||||
public string MimeType { get; set; }
|
||||
|
||||
public int PlayState { get; set; }
|
||||
|
||||
public string StreamUrl { get; set; }
|
||||
|
@ -51,10 +53,21 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
{
|
||||
if (string.IsNullOrWhiteSpace(transcodeSetting.Container))
|
||||
continue;
|
||||
if (path.EndsWith(transcodeSetting.Container))
|
||||
if (path.EndsWith(transcodeSetting.Container) && !string.IsNullOrWhiteSpace(transcodeSetting.TargetContainer))
|
||||
{
|
||||
playlistItem.Transcode = true;
|
||||
playlistItem.FileFormat = transcodeSetting.TargetContainer;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(transcodeSetting.MimeType))
|
||||
playlistItem.MimeType = transcodeSetting.MimeType;
|
||||
|
||||
return playlistItem;
|
||||
}
|
||||
if (path.EndsWith(transcodeSetting.Container) && !string.IsNullOrWhiteSpace(transcodeSetting.MimeType))
|
||||
{
|
||||
playlistItem.Transcode = false;
|
||||
playlistItem.FileFormat = transcodeSetting.Container;
|
||||
playlistItem.MimeType = transcodeSetting.MimeType;
|
||||
return playlistItem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,9 +96,12 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
/// <returns>The url to send to the device</returns>
|
||||
internal static string GetVideoUrl(DeviceProperties deviceProperties, PlaylistItem item, List<MediaStream> streams, string serverAddress)
|
||||
{
|
||||
string dlnaCommand = string.Empty;
|
||||
if (!item.Transcode)
|
||||
return string.Format("{0}/Videos/{1}/stream.{2}?Static=True", serverAddress, item.ItemId, item.FileFormat);
|
||||
|
||||
{
|
||||
dlnaCommand = BuildDlnaUrl(deviceProperties.UUID, !item.Transcode, null, null, null, null, null, null, null, null, null, null, item.MimeType);
|
||||
return string.Format("{0}/Videos/{1}/stream.{2}?{3}", serverAddress, item.ItemId, item.FileFormat, dlnaCommand);
|
||||
}
|
||||
var videostream = streams.Where(m => m.Type == MediaStreamType.Video).OrderBy(m => m.IsDefault).FirstOrDefault();
|
||||
var audiostream = streams.Where(m => m.Type == MediaStreamType.Audio).OrderBy(m => m.IsDefault).FirstOrDefault();
|
||||
|
||||
|
@ -117,7 +120,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
audioChannels = 2;
|
||||
}
|
||||
|
||||
string dlnaCommand = BuildDlnaUrl(deviceProperties.UUID, videoCodec, audioCodec, null, null, videoBitrate, audioChannels, audioBitrate, item.StartPositionTicks, "baseline", "3");
|
||||
dlnaCommand = BuildDlnaUrl(deviceProperties.UUID, !item.Transcode, videoCodec, audioCodec, null, null, videoBitrate, audioChannels, audioBitrate, item.StartPositionTicks, "baseline", "3", item.MimeType);
|
||||
return string.Format("{0}/Videos/{1}/stream.{2}?{3}", serverAddress, item.ItemId, item.FileFormat, dlnaCommand);
|
||||
}
|
||||
|
||||
|
@ -162,12 +165,12 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
/// <summary>
|
||||
/// Builds the dlna URL.
|
||||
/// </summary>
|
||||
private static string BuildDlnaUrl(string deviceID, VideoCodecs? videoCodec, AudioCodecs? audioCodec, int? subtitleIndex, int? audiostreamIndex, int? videoBitrate, int? audiochannels, int? audioBitrate, long? startPositionTicks, string profile, string videoLevel)
|
||||
private static string BuildDlnaUrl(string deviceID, bool isStatic, VideoCodecs? videoCodec, AudioCodecs? audioCodec, int? subtitleIndex, int? audiostreamIndex, int? videoBitrate, int? audiochannels, int? audioBitrate, long? startPositionTicks, string profile, string videoLevel, string mimeType)
|
||||
{
|
||||
var usCulture = new CultureInfo("en-US");
|
||||
|
||||
var dlnaparam = string.Format("Params={0};", deviceID);
|
||||
|
||||
dlnaparam += isStatic ? "true;" : "false;";
|
||||
dlnaparam += videoCodec.HasValue ? videoCodec.Value + ";" : ";";
|
||||
dlnaparam += audioCodec.HasValue ? audioCodec.Value + ";" : ";";
|
||||
dlnaparam += audiostreamIndex.HasValue ? audiostreamIndex.Value.ToString(usCulture) + ";" : ";";
|
||||
|
@ -178,6 +181,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
dlnaparam += startPositionTicks.HasValue ? startPositionTicks.Value.ToString(usCulture) + ";" : ";";
|
||||
dlnaparam += profile + ";";
|
||||
dlnaparam += videoLevel + ";";
|
||||
dlnaparam += mimeType + ";";
|
||||
|
||||
return dlnaparam;
|
||||
}
|
||||
|
|
|
@ -80,6 +80,9 @@
|
|||
<Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
|
||||
<Link>Configuration\BaseApplicationConfiguration.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Configuration\DlnaOptions.cs">
|
||||
<Link>Configuration\DlnaOptions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Configuration\ManualLoginCategory.cs">
|
||||
<Link>Configuration\ManualLoginCategory.cs</Link>
|
||||
</Compile>
|
||||
|
@ -131,6 +134,9 @@
|
|||
<Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
|
||||
<Link>Dto\ItemIndex.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Dto\RecommendationDto.cs">
|
||||
<Link>Dto\RecommendationDto.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Dto\StreamOptions.cs">
|
||||
<Link>Dto\StreamOptions.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
@ -67,6 +67,9 @@
|
|||
<Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
|
||||
<Link>Configuration\BaseApplicationConfiguration.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Configuration\DlnaOptions.cs">
|
||||
<Link>Configuration\DlnaOptions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Configuration\ManualLoginCategory.cs">
|
||||
<Link>Configuration\ManualLoginCategory.cs</Link>
|
||||
</Compile>
|
||||
|
@ -118,6 +121,9 @@
|
|||
<Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
|
||||
<Link>Dto\ItemIndex.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Dto\RecommendationDto.cs">
|
||||
<Link>Dto\RecommendationDto.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Dto\StreamOptions.cs">
|
||||
<Link>Dto\StreamOptions.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
@ -244,7 +244,7 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// <param name="query">The query.</param>
|
||||
/// <returns>Task{ItemsResult}.</returns>
|
||||
Task<ItemsResult> GetSeasonsAsync(SeasonQuery query);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Queries for items
|
||||
/// </summary>
|
||||
|
@ -346,7 +346,14 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>Task{ItemsResult}.</returns>
|
||||
Task<ItemsResult> GetNextUpAsync(NextUpQuery query);
|
||||
Task<ItemsResult> GetNextUpEpisodesAsync(NextUpQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the upcoming episodes asynchronous.
|
||||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>Task{ItemsResult}.</returns>
|
||||
Task<ItemsResult> GetUpcomingEpisodesAsync(NextUpQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a genre
|
||||
|
@ -772,7 +779,7 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// <param name="options">The options.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
string GetImageUrl(ProgramInfoDto item, ImageOptions options);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets an image url that can be used to download an image from the api
|
||||
/// </summary>
|
||||
|
@ -902,7 +909,7 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// <param name="options">The options.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
string GetThumbImageUrl(BaseItemDto item, ImageOptions options);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the url needed to stream an audio file
|
||||
/// </summary>
|
||||
|
@ -958,7 +965,7 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{ChannelInfoDto}.</returns>
|
||||
Task<ChannelInfoDto> GetLiveTvChannelAsync(string id, string userId, CancellationToken cancellationToken);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the live tv recordings asynchronous.
|
||||
/// </summary>
|
||||
|
@ -975,7 +982,7 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{RecordingInfoDto}.</returns>
|
||||
Task<RecordingInfoDto> GetLiveTvRecordingAsync(string id, string userId, CancellationToken cancellationToken);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the live tv recording groups asynchronous.
|
||||
/// </summary>
|
||||
|
@ -992,7 +999,7 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{RecordingGroupDto}.</returns>
|
||||
Task<RecordingGroupDto> GetLiveTvRecordingGroupAsync(string id, string userId, CancellationToken cancellationToken);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the live tv timers asynchronous.
|
||||
/// </summary>
|
||||
|
@ -1009,6 +1016,15 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
|
||||
Task<QueryResult<ProgramInfoDto>> GetLiveTvProgramsAsync(ProgramQuery query, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the live tv program asynchronous.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <param name="userId">The user identifier.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{ProgramInfoDto}.</returns>
|
||||
Task<ProgramInfoDto> GetLiveTvProgramAsync(string id, string userId, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the recommended live tv programs asynchronous.
|
||||
/// </summary>
|
||||
|
@ -1016,7 +1032,39 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
|
||||
Task<QueryResult<ProgramInfoDto>> GetRecommendedLiveTvProgramsAsync(RecommendedProgramQuery query, CancellationToken cancellationToken);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates the live tv timer asynchronous.
|
||||
/// </summary>
|
||||
/// <param name="timer">The timer.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task CreateLiveTvTimerAsync(TimerInfoDto timer, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the live tv timer asynchronous.
|
||||
/// </summary>
|
||||
/// <param name="timer">The timer.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task UpdateLiveTvTimerAsync(TimerInfoDto timer, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the live tv series timer asynchronous.
|
||||
/// </summary>
|
||||
/// <param name="timer">The timer.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task CreateLiveTvSeriesTimerAsync(SeriesTimerInfoDto timer, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the live tv series timer asynchronous.
|
||||
/// </summary>
|
||||
/// <param name="timer">The timer.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task UpdateLiveTvSeriesTimerAsync(SeriesTimerInfoDto timer, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the live tv timer asynchronous.
|
||||
/// </summary>
|
||||
|
@ -1024,7 +1072,7 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{TimerInfoDto}.</returns>
|
||||
Task<TimerInfoDto> GetLiveTvTimerAsync(string id, CancellationToken cancellationToken);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the live tv series timers asynchronous.
|
||||
/// </summary>
|
||||
|
@ -1056,7 +1104,7 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task CancelLiveTvSeriesTimerAsync(string id, CancellationToken cancellationToken);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the live tv recording asynchronous.
|
||||
/// </summary>
|
||||
|
@ -1064,5 +1112,27 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task DeleteLiveTvRecordingAsync(string id, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default timer information.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{SeriesTimerInfoDto}.</returns>
|
||||
Task<SeriesTimerInfoDto> GetDefaultLiveTvTimerInfo(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the live tv guide information.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{GuideInfo}.</returns>
|
||||
Task<GuideInfo> GetLiveTvGuideInfo(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default timer information.
|
||||
/// </summary>
|
||||
/// <param name="programId">The program identifier.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{SeriesTimerInfoDto}.</returns>
|
||||
Task<SeriesTimerInfoDto> GetDefaultLiveTvTimerInfo(string programId, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
|
@ -19,6 +19,8 @@ namespace MediaBrowser.Model.Configuration
|
|||
|
||||
public bool DeleteEmptyFolders { get; set; }
|
||||
|
||||
public bool CopyOriginalFile { get; set; }
|
||||
|
||||
public TvFileOrganizationOptions()
|
||||
{
|
||||
MinFileSizeMb = 50;
|
||||
|
@ -31,6 +33,8 @@ namespace MediaBrowser.Model.Configuration
|
|||
MultiEpisodeNamePattern = "%sn - %sx%0e-x%0ed - %en.%ext";
|
||||
SeasonFolderPattern = "Season %s";
|
||||
SeasonZeroFolderName = "Season 0";
|
||||
|
||||
CopyOriginalFile = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
8
MediaBrowser.Model/Configuration/DlnaOptions.cs
Normal file
8
MediaBrowser.Model/Configuration/DlnaOptions.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
namespace MediaBrowser.Model.Configuration
|
||||
{
|
||||
public class DlnaOptions
|
||||
{
|
||||
public bool EnablePlayTo { get; set; }
|
||||
}
|
||||
}
|
|
@ -212,6 +212,8 @@ namespace MediaBrowser.Model.Configuration
|
|||
public string ServerName { get; set; }
|
||||
public string WanDdns { get; set; }
|
||||
|
||||
public DlnaOptions DlnaOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
|
||||
/// </summary>
|
||||
|
@ -271,6 +273,8 @@ namespace MediaBrowser.Model.Configuration
|
|||
};
|
||||
|
||||
MetadataOptions = options.ToArray();
|
||||
|
||||
DlnaOptions = new DlnaOptions();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
29
MediaBrowser.Model/Dto/RecommendationDto.cs
Normal file
29
MediaBrowser.Model/Dto/RecommendationDto.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
|
||||
namespace MediaBrowser.Model.Dto
|
||||
{
|
||||
public class RecommendationDto
|
||||
{
|
||||
public BaseItemDto[] Items { get; set; }
|
||||
|
||||
public RecommendationType RecommendationType { get; set; }
|
||||
|
||||
public string BaselineItemName { get; set; }
|
||||
|
||||
public string CategoryId { get; set; }
|
||||
}
|
||||
|
||||
public enum RecommendationType
|
||||
{
|
||||
SimilarToRecentlyPlayed = 0,
|
||||
|
||||
SimilarToLikedItem = 1,
|
||||
|
||||
HasDirectorFromRecentlyPlayed = 2,
|
||||
|
||||
HasActorFromRecentlyPlayed = 3,
|
||||
|
||||
HasLikedDirector = 4,
|
||||
|
||||
HasLikedActor = 5
|
||||
}
|
||||
}
|
|
@ -46,6 +46,30 @@ namespace MediaBrowser.Model.Entities
|
|||
/// <value>The primary image tag.</value>
|
||||
public Guid? PrimaryImageTag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the thumb image tag.
|
||||
/// </summary>
|
||||
/// <value>The thumb image tag.</value>
|
||||
public Guid? ThumbImageTag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the thumb item identifier.
|
||||
/// </summary>
|
||||
/// <value>The thumb item identifier.</value>
|
||||
public string ThumbItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the thumb image tag.
|
||||
/// </summary>
|
||||
/// <value>The thumb image tag.</value>
|
||||
public Guid? BackdropImageTag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the thumb item identifier.
|
||||
/// </summary>
|
||||
/// <value>The thumb item identifier.</value>
|
||||
public string BackdropItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance has primary image.
|
||||
/// </summary>
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
<Compile Include="ApiClient\ServerEventArgs.cs" />
|
||||
<Compile Include="Configuration\AutoOrganize.cs" />
|
||||
<Compile Include="Configuration\BaseApplicationConfiguration.cs" />
|
||||
<Compile Include="Configuration\DlnaOptions.cs" />
|
||||
<Compile Include="Configuration\ManualLoginCategory.cs" />
|
||||
<Compile Include="Configuration\MetadataPlugin.cs" />
|
||||
<Compile Include="Configuration\MetadataOptions.cs" />
|
||||
|
@ -73,6 +74,7 @@
|
|||
<Compile Include="Dto\ItemByNameCounts.cs" />
|
||||
<Compile Include="Dto\ItemCounts.cs" />
|
||||
<Compile Include="Dto\ItemIndex.cs" />
|
||||
<Compile Include="Dto\RecommendationDto.cs" />
|
||||
<Compile Include="Entities\PackageReviewInfo.cs" />
|
||||
<Compile Include="FileOrganization\FileOrganizationResult.cs" />
|
||||
<Compile Include="FileOrganization\FileOrganizationQuery.cs" />
|
||||
|
|
|
@ -218,6 +218,18 @@ namespace MediaBrowser.Model.Querying
|
|||
/// <value>The name starts with or greater.</value>
|
||||
public string NameStartsWithOrGreater { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name starts with.
|
||||
/// </summary>
|
||||
/// <value>The name starts with or greater.</value>
|
||||
public string NameStartsWith { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name starts with.
|
||||
/// </summary>
|
||||
/// <value>The name lessthan.</value>
|
||||
public string NameLessThan { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the album artist starts with or greater.
|
||||
/// </summary>
|
||||
|
|
|
@ -75,16 +75,12 @@ namespace MediaBrowser.Model.Querying
|
|||
public const string IsFolder = "IsFolder";
|
||||
public const string IsUnplayed = "IsUnplayed";
|
||||
public const string IsPlayed = "IsPlayed";
|
||||
public const string TrailerCount = "TrailerCount";
|
||||
public const string MovieCount = "MovieCount";
|
||||
public const string SeriesCount = "SeriesCount";
|
||||
public const string EpisodeCount = "EpisodeCount";
|
||||
public const string SongCount = "SongCount";
|
||||
public const string AlbumCount = "AlbumCount";
|
||||
public const string MusicVideoCount = "MusicVideoCount";
|
||||
public const string SeriesSortName = "SeriesSortName";
|
||||
public const string VideoBitRate = "VideoBitRate";
|
||||
public const string AirTime = "AirTime";
|
||||
public const string Metascore = "Metascore";
|
||||
public const string Studio = "Studio";
|
||||
public const string Players = "Players";
|
||||
public const string GameSystem = "GameSystem";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,6 +86,10 @@ namespace MediaBrowser.Model.Querying
|
|||
public string NameStartsWithOrGreater { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name starts with
|
||||
/// </summary>
|
||||
/// <value>The name starts with or greater.</value>
|
||||
public string NameStartsWith { get; set; }
|
||||
/// Gets or sets the name less than.
|
||||
/// </summary>
|
||||
/// <value>The name less than.</value>
|
||||
|
|
|
@ -33,4 +33,32 @@ namespace MediaBrowser.Model.Querying
|
|||
/// <value>The fields.</value>
|
||||
public ItemFields[] Fields { get; set; }
|
||||
}
|
||||
|
||||
public class UpcomingEpisodesQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public string UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Skips over a given number of items within the results. Use for paging.
|
||||
/// </summary>
|
||||
/// <value>The start index.</value>
|
||||
public int? StartIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of items to return
|
||||
/// </summary>
|
||||
/// <value>The limit.</value>
|
||||
public int? Limit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fields to return within the items, in addition to basic information
|
||||
/// </summary>
|
||||
/// <value>The fields.</value>
|
||||
public ItemFields[] Fields { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ namespace MediaBrowser.Model.Search
|
|||
public bool IncludeStudios { get; set; }
|
||||
public bool IncludeArtists { get; set; }
|
||||
|
||||
public string[] IncludeItemTypes { get; set; }
|
||||
|
||||
public SearchQuery()
|
||||
{
|
||||
IncludeArtists = true;
|
||||
|
@ -40,6 +42,8 @@ namespace MediaBrowser.Model.Search
|
|||
IncludeMedia = true;
|
||||
IncludePeople = true;
|
||||
IncludeStudios = true;
|
||||
|
||||
IncludeItemTypes = new string[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace MediaBrowser.Model.Session
|
|||
/// </summary>
|
||||
/// <value>The name of the item.</value>
|
||||
public string ItemName { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the context (Movies, Music, Tv, etc)
|
||||
/// Applicable to genres, studios and persons only because the context of items and artists can be inferred.
|
||||
|
@ -40,4 +40,4 @@ namespace MediaBrowser.Model.Session
|
|||
public const string TvShows = "TvShows";
|
||||
public const string Games = "Games";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,9 +4,9 @@ namespace MediaBrowser.Model.Session
|
|||
public class MessageCommand
|
||||
{
|
||||
public string Header { get; set; }
|
||||
|
||||
|
||||
public string Text { get; set; }
|
||||
|
||||
public long? TimeoutMs { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,4 +43,4 @@ namespace MediaBrowser.Model.Session
|
|||
/// </summary>
|
||||
PlayLast
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,4 +38,4 @@ namespace MediaBrowser.Model.Session
|
|||
|
||||
public long? SeekPositionTicks { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ namespace MediaBrowser.Providers.AdultVideos
|
|||
new MovieXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
|
||||
}
|
||||
|
||||
protected override FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
||||
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
||||
{
|
||||
return MovieXmlProvider.GetXmlFileInfo(info, FileSystem);
|
||||
}
|
||||
|
|
|
@ -201,7 +201,7 @@ namespace MediaBrowser.Providers.All
|
|||
PopulateBackdrops(images, files, imagePrefix, "background", "background-", ImageType.Backdrop);
|
||||
PopulateBackdrops(images, files, imagePrefix, "art", "art-", ImageType.Backdrop);
|
||||
|
||||
var extraFanartFolder = files.OfType<DirectoryInfo>()
|
||||
var extraFanartFolder = files
|
||||
.FirstOrDefault(i => string.Equals(i.Name, "extrafanart", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (extraFanartFolder != null)
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace MediaBrowser.Providers
|
|||
FileSystem = fileSystem;
|
||||
}
|
||||
|
||||
protected abstract FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService);
|
||||
protected abstract FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService);
|
||||
|
||||
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
@ -37,6 +38,15 @@ namespace MediaBrowser.Providers.BoxSets
|
|||
protected override void MergeData(BoxSet source, BoxSet target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
|
||||
{
|
||||
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
|
||||
|
||||
if (mergeMetadataSettings)
|
||||
{
|
||||
var list = source.LinkedChildren.ToList();
|
||||
|
||||
list.AddRange(target.LinkedChildren.Where(i => i.Type == LinkedChildType.Shortcut));
|
||||
|
||||
target.LinkedChildren = list;
|
||||
}
|
||||
}
|
||||
|
||||
protected override ItemUpdateType BeforeSave(BoxSet item)
|
||||
|
|
129
MediaBrowser.Providers/BoxSets/BoxSetXmlParser.cs
Normal file
129
MediaBrowser.Providers/BoxSets/BoxSetXmlParser.cs
Normal file
|
@ -0,0 +1,129 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Xml;
|
||||
|
||||
namespace MediaBrowser.Providers.BoxSets
|
||||
{
|
||||
public class BoxSetXmlParser : BaseItemXmlParser<BoxSet>
|
||||
{
|
||||
private readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||
|
||||
public BoxSetXmlParser(ILogger logger)
|
||||
: base(logger)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void FetchDataFromXmlNode(XmlReader reader, BoxSet item)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "CollectionItems":
|
||||
|
||||
using (var subReader = reader.ReadSubtree())
|
||||
{
|
||||
FetchFromCollectionItemsNode(subReader, item);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
base.FetchDataFromXmlNode(reader, item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void FetchFromCollectionItemsNode(XmlReader reader, BoxSet item)
|
||||
{
|
||||
reader.MoveToContent();
|
||||
|
||||
var list = new List<LinkedChild>();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "CollectionItem":
|
||||
{
|
||||
using (var subReader = reader.ReadSubtree())
|
||||
{
|
||||
var child = GetLinkedChild(subReader);
|
||||
|
||||
if (child != null)
|
||||
{
|
||||
list.Add(child);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item.LinkedChildren = list;
|
||||
}
|
||||
|
||||
private LinkedChild GetLinkedChild(XmlReader reader)
|
||||
{
|
||||
reader.MoveToContent();
|
||||
|
||||
var linkedItem = new LinkedChild
|
||||
{
|
||||
Type = LinkedChildType.Manual
|
||||
};
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "Name":
|
||||
{
|
||||
linkedItem.ItemName = reader.ReadElementContentAsString();
|
||||
break;
|
||||
}
|
||||
|
||||
case "Type":
|
||||
{
|
||||
linkedItem.ItemType = reader.ReadElementContentAsString();
|
||||
break;
|
||||
}
|
||||
|
||||
case "Year":
|
||||
{
|
||||
var val = reader.ReadElementContentAsString();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
int rval;
|
||||
|
||||
if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval))
|
||||
{
|
||||
linkedItem.ItemYear = rval;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string.IsNullOrWhiteSpace(linkedItem.ItemName) || string.IsNullOrWhiteSpace(linkedItem.ItemType) ? null : linkedItem;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,10 +22,10 @@ namespace MediaBrowser.Providers.BoxSets
|
|||
|
||||
protected override void Fetch(LocalMetadataResult<BoxSet> result, string path, CancellationToken cancellationToken)
|
||||
{
|
||||
new BaseItemXmlParser<BoxSet>(_logger).Fetch(result.Item, path, cancellationToken);
|
||||
new BoxSetXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
|
||||
}
|
||||
|
||||
protected override FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
||||
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
||||
{
|
||||
return directoryService.GetFile(Path.Combine(info.Path, "collection.xml"));
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.Folders
|
|||
new BaseItemXmlParser<Folder>(_logger).Fetch(result.Item, path, cancellationToken);
|
||||
}
|
||||
|
||||
protected override FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
||||
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
||||
{
|
||||
return new FileInfo(Path.Combine(info.Path, "folder.xml"));
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Games
|
|||
new GameSystemXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
|
||||
}
|
||||
|
||||
protected override FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
||||
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
||||
{
|
||||
return directoryService.GetFile(Path.Combine(info.Path, "gamesystem.xml"));
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Games
|
|||
new GameXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
|
||||
}
|
||||
|
||||
protected override FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
||||
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
||||
{
|
||||
var fileInfo = FileSystem.GetFileSystemInfo(info.Path);
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.LiveTv
|
|||
new BaseItemXmlParser<LiveTvChannel>(_logger).Fetch(result.Item, path, cancellationToken);
|
||||
}
|
||||
|
||||
protected override FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
||||
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
||||
{
|
||||
return directoryService.GetFile(Path.Combine(info.Path, "channel.xml"));
|
||||
}
|
||||
|
|
|
@ -305,22 +305,20 @@ namespace MediaBrowser.Providers.Manager
|
|||
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.ImageUpdate;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(localItem.Item.Name))
|
||||
if (string.IsNullOrWhiteSpace(localItem.Item.Name))
|
||||
{
|
||||
MergeData(localItem.Item, temp, new List<MetadataFields>(), !options.ReplaceAllMetadata, true);
|
||||
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;
|
||||
|
||||
// Only one local provider allowed per item
|
||||
hasLocalMetadata = true;
|
||||
break;
|
||||
localItem.Item.Name = item.Name ?? Path.GetFileNameWithoutExtension(item.Path);
|
||||
}
|
||||
|
||||
Logger.Error("Invalid local metadata found for: " + item.Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug("{0} returned no metadata for {1}", providerName, item.Path ?? item.Name);
|
||||
MergeData(localItem.Item, temp, new List<MetadataFields>(), !options.ReplaceAllMetadata, true);
|
||||
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;
|
||||
|
||||
// Only one local provider allowed per item
|
||||
hasLocalMetadata = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Logger.Debug("{0} returned no metadata for {1}", providerName, item.Path ?? item.Name);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user