Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
9e33966ee1
|
@ -164,7 +164,10 @@ namespace MediaBrowser.Api
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return libraryManager.GetAllArtists()
|
return libraryManager.RootFolder.RecursiveChildren
|
||||||
|
.OfType<Audio>()
|
||||||
|
.SelectMany(i => i.AllArtists)
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
.FirstOrDefault(i =>
|
.FirstOrDefault(i =>
|
||||||
{
|
{
|
||||||
i = _dashReplaceChars.Aggregate(i, (current, c) => current.Replace(c, SlugChar));
|
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))
|
.Select(i => _dtoService.GetBaseItemDto(i, fields, user))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var artists = _libraryManager.GetAllArtists(allItems)
|
var artists = allItems.OfType<Audio>()
|
||||||
.Randomize()
|
.SelectMany(i => i.AllArtists)
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Randomize()
|
||||||
.Select(i =>
|
.Select(i =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
@ -155,7 +155,7 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
var games = items.OfType<Game>().ToList();
|
var games = items.OfType<Game>().ToList();
|
||||||
|
|
||||||
summary.ClientInstalledGameCount = games.Count(i => !i.IsPlaceHolder);
|
summary.ClientInstalledGameCount = games.Count(i => i.IsPlaceHolder);
|
||||||
|
|
||||||
summary.GameCount = games.Count;
|
summary.GameCount = games.Count;
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
UpdateItem(request, item);
|
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)
|
if (dontFetchMetaChanged && item.IsFolder)
|
||||||
{
|
{
|
||||||
|
@ -107,7 +107,7 @@ namespace MediaBrowser.Api
|
||||||
foreach (var child in folder.RecursiveChildren.ToList())
|
foreach (var child in folder.RecursiveChildren.ToList())
|
||||||
{
|
{
|
||||||
child.DontFetchMeta = newLockData;
|
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);
|
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)
|
public void Post(UpdateArtist request)
|
||||||
|
@ -141,7 +141,7 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
UpdateItem(request, item);
|
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)
|
public void Post(UpdateStudio request)
|
||||||
|
@ -157,7 +157,7 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
UpdateItem(request, item);
|
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)
|
public void Post(UpdateMusicGenre request)
|
||||||
|
@ -173,7 +173,7 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
UpdateItem(request, item);
|
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)
|
public void Post(UpdateGameGenre request)
|
||||||
|
@ -189,7 +189,7 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
UpdateItem(request, item);
|
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)
|
public void Post(UpdateGenre request)
|
||||||
|
@ -205,7 +205,7 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
UpdateItem(request, item);
|
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)
|
private void UpdateItem(BaseItemDto request, BaseItem item)
|
||||||
|
|
|
@ -66,7 +66,8 @@
|
||||||
<Compile Include="..\SharedVersion.cs">
|
<Compile Include="..\SharedVersion.cs">
|
||||||
<Link>Properties\SharedVersion.cs</Link>
|
<Link>Properties\SharedVersion.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="AlbumsService.cs" />
|
<Compile Include="Movies\CollectionService.cs" />
|
||||||
|
<Compile Include="Music\AlbumsService.cs" />
|
||||||
<Compile Include="AppThemeService.cs" />
|
<Compile Include="AppThemeService.cs" />
|
||||||
<Compile Include="BaseApiService.cs" />
|
<Compile Include="BaseApiService.cs" />
|
||||||
<Compile Include="ConfigurationService.cs" />
|
<Compile Include="ConfigurationService.cs" />
|
||||||
|
@ -81,7 +82,7 @@
|
||||||
<Compile Include="Images\ImageRequest.cs" />
|
<Compile Include="Images\ImageRequest.cs" />
|
||||||
<Compile Include="Images\ImageService.cs" />
|
<Compile Include="Images\ImageService.cs" />
|
||||||
<Compile Include="Images\ImageWriter.cs" />
|
<Compile Include="Images\ImageWriter.cs" />
|
||||||
<Compile Include="InstantMixService.cs" />
|
<Compile Include="Music\InstantMixService.cs" />
|
||||||
<Compile Include="ItemLookupService.cs" />
|
<Compile Include="ItemLookupService.cs" />
|
||||||
<Compile Include="ItemRefreshService.cs" />
|
<Compile Include="ItemRefreshService.cs" />
|
||||||
<Compile Include="ItemUpdateService.cs" />
|
<Compile Include="ItemUpdateService.cs" />
|
||||||
|
@ -91,7 +92,7 @@
|
||||||
<Compile Include="Library\LibraryStructureService.cs" />
|
<Compile Include="Library\LibraryStructureService.cs" />
|
||||||
<Compile Include="LiveTv\LiveTvService.cs" />
|
<Compile Include="LiveTv\LiveTvService.cs" />
|
||||||
<Compile Include="LocalizationService.cs" />
|
<Compile Include="LocalizationService.cs" />
|
||||||
<Compile Include="MoviesService.cs" />
|
<Compile Include="Movies\MoviesService.cs" />
|
||||||
<Compile Include="NewsService.cs" />
|
<Compile Include="NewsService.cs" />
|
||||||
<Compile Include="NotificationsService.cs" />
|
<Compile Include="NotificationsService.cs" />
|
||||||
<Compile Include="PackageReviewService.cs" />
|
<Compile Include="PackageReviewService.cs" />
|
||||||
|
@ -118,7 +119,7 @@
|
||||||
<Compile Include="SessionsService.cs" />
|
<Compile Include="SessionsService.cs" />
|
||||||
<Compile Include="SimilarItemsHelper.cs" />
|
<Compile Include="SimilarItemsHelper.cs" />
|
||||||
<Compile Include="SystemService.cs" />
|
<Compile Include="SystemService.cs" />
|
||||||
<Compile Include="TrailersService.cs" />
|
<Compile Include="Movies\TrailersService.cs" />
|
||||||
<Compile Include="TvShowsService.cs" />
|
<Compile Include="TvShowsService.cs" />
|
||||||
<Compile Include="UserLibrary\ArtistsService.cs" />
|
<Compile Include="UserLibrary\ArtistsService.cs" />
|
||||||
<Compile Include="UserLibrary\BaseItemsByNameService.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 MediaBrowser.Controller.Persistence;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
|
|
||||||
namespace MediaBrowser.Api
|
namespace MediaBrowser.Api.Movies
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class GetSimilarTrailers
|
/// 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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace MediaBrowser.Api
|
namespace MediaBrowser.Api.Music
|
||||||
{
|
{
|
||||||
[Route("/Albums/{Id}/Similar", "GET")]
|
[Route("/Albums/{Id}/Similar", "GET")]
|
||||||
[Api(Description = "Finds albums similar to a given album.")]
|
[Api(Description = "Finds albums similar to a given album.")]
|
|
@ -7,7 +7,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace MediaBrowser.Api
|
namespace MediaBrowser.Api.Music
|
||||||
{
|
{
|
||||||
[Route("/Songs/{Id}/InstantMix", "GET")]
|
[Route("/Songs/{Id}/InstantMix", "GET")]
|
||||||
[Api(Description = "Creates an instant playlist based on a given song")]
|
[Api(Description = "Creates an instant playlist based on a given song")]
|
|
@ -279,8 +279,19 @@ namespace MediaBrowser.Api.Playback
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>System.Int32.</returns>
|
/// <returns>System.Int32.</returns>
|
||||||
/// <exception cref="System.Exception">Unrecognized MediaEncodingQuality value.</exception>
|
/// <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/
|
// 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
|
// 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.
|
// 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);
|
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
|
// 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
|
//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)
|
if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
|
||||||
{
|
{
|
||||||
var MaxwidthParam = request.MaxWidth.Value.ToString(UsCulture);
|
var MaxwidthParam = request.MaxWidth.Value.ToString(UsCulture);
|
||||||
var MaxheightParam = request.MaxHeight.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);
|
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);
|
var isH264Output = outputVideoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
@ -603,7 +614,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
private string GetExtractedAssPath(StreamState state, bool performConversion)
|
private string GetExtractedAssPath(StreamState state, bool performConversion)
|
||||||
{
|
{
|
||||||
var path = EncodingManager.GetSubtitleCachePath(state.MediaPath, state.SubtitleStream.Index, ".ass");
|
var path = EncodingManager.GetSubtitleCachePath(state.MediaPath, state.SubtitleStream.Index, ".ass");
|
||||||
|
|
||||||
if (performConversion)
|
if (performConversion)
|
||||||
{
|
{
|
||||||
InputType type;
|
InputType type;
|
||||||
|
@ -987,20 +998,15 @@ namespace MediaBrowser.Api.Playback
|
||||||
|
|
||||||
if (state.VideoStream != null)
|
if (state.VideoStream != null)
|
||||||
{
|
{
|
||||||
var isUpscaling = false;
|
var isUpscaling = state.VideoRequest.Height.HasValue && state.VideoStream.Height.HasValue &&
|
||||||
|
state.VideoRequest.Height.Value > state.VideoStream.Height.Value;
|
||||||
if (state.VideoRequest.Height.HasValue && state.VideoStream.Height.HasValue &&
|
|
||||||
state.VideoRequest.Height.Value > state.VideoStream.Height.Value)
|
|
||||||
{
|
|
||||||
isUpscaling = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.VideoRequest.Width.HasValue && state.VideoStream.Width.HasValue &&
|
if (state.VideoRequest.Width.HasValue && state.VideoStream.Width.HasValue &&
|
||||||
state.VideoRequest.Width.Value > state.VideoStream.Width.Value)
|
state.VideoRequest.Width.Value > state.VideoStream.Width.Value)
|
||||||
{
|
{
|
||||||
isUpscaling = true;
|
isUpscaling = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't allow bitrate increases unless upscaling
|
// Don't allow bitrate increases unless upscaling
|
||||||
if (!isUpscaling)
|
if (!isUpscaling)
|
||||||
{
|
{
|
||||||
|
@ -1198,66 +1204,74 @@ namespace MediaBrowser.Api.Playback
|
||||||
request.DeviceId = val;
|
request.DeviceId = val;
|
||||||
}
|
}
|
||||||
else if (i == 1)
|
else if (i == 1)
|
||||||
|
{
|
||||||
|
request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
else if (i == 2)
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
videoRequest.VideoCodec = (VideoCodecs)Enum.Parse(typeof(VideoCodecs), val, true);
|
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);
|
request.AudioCodec = (AudioCodecs)Enum.Parse(typeof(AudioCodecs), val, true);
|
||||||
}
|
}
|
||||||
else if (i == 3)
|
else if (i == 4)
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
videoRequest.AudioStreamIndex = int.Parse(val, UsCulture);
|
videoRequest.AudioStreamIndex = int.Parse(val, UsCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (i == 4)
|
else if (i == 5)
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture);
|
videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (i == 5)
|
else if (i == 6)
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
videoRequest.VideoBitRate = int.Parse(val, UsCulture);
|
videoRequest.VideoBitRate = int.Parse(val, UsCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (i == 6)
|
else if (i == 7)
|
||||||
{
|
{
|
||||||
request.AudioBitRate = int.Parse(val, UsCulture);
|
request.AudioBitRate = int.Parse(val, UsCulture);
|
||||||
}
|
}
|
||||||
else if (i == 7)
|
else if (i == 8)
|
||||||
{
|
{
|
||||||
request.AudioChannels = int.Parse(val, UsCulture);
|
request.AudioChannels = int.Parse(val, UsCulture);
|
||||||
}
|
}
|
||||||
else if (i == 8)
|
else if (i == 9)
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
request.StartTimeTicks = long.Parse(val, UsCulture);
|
request.StartTimeTicks = long.Parse(val, UsCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (i == 9)
|
else if (i == 10)
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
videoRequest.Profile = val;
|
videoRequest.Profile = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (i == 10)
|
else if (i == 11)
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
videoRequest.Level = val;
|
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.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
|
||||||
state.PlayableStreamFileNames = new List<string>();
|
state.PlayableStreamFileNames = new List<string>();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(recording.RecordingInfo.Path) && File.Exists(recording.RecordingInfo.Path))
|
var path = recording.RecordingInfo.Path;
|
||||||
{
|
var mediaUrl = recording.RecordingInfo.Url;
|
||||||
state.MediaPath = recording.RecordingInfo.Path;
|
|
||||||
state.IsRemote = false;
|
if (string.IsNullOrWhiteSpace(path) && string.IsNullOrWhiteSpace(mediaUrl))
|
||||||
}
|
|
||||||
else if (!string.IsNullOrEmpty(recording.RecordingInfo.Url))
|
|
||||||
{
|
|
||||||
state.MediaPath = recording.RecordingInfo.Url;
|
|
||||||
state.IsRemote = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
var streamInfo = await LiveTvManager.GetRecordingStream(request.Id, cancellationToken).ConfigureAwait(false);
|
var streamInfo = await LiveTvManager.GetRecordingStream(request.Id, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
state.LiveTvStreamId = streamInfo.Id;
|
state.LiveTvStreamId = streamInfo.Id;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(streamInfo.Path) && File.Exists(streamInfo.Path))
|
path = streamInfo.Path;
|
||||||
{
|
mediaUrl = streamInfo.Url;
|
||||||
state.MediaPath = streamInfo.Path;
|
}
|
||||||
state.IsRemote = false;
|
|
||||||
}
|
if (!string.IsNullOrEmpty(path) && File.Exists(path))
|
||||||
else if (!string.IsNullOrEmpty(streamInfo.Url))
|
{
|
||||||
{
|
state.MediaPath = path;
|
||||||
state.MediaPath = streamInfo.Url;
|
state.IsRemote = false;
|
||||||
state.IsRemote = true;
|
|
||||||
}
|
state.SendInputOverStandardInput = recording.RecordingInfo.Status == RecordingStatus.InProgress;
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(mediaUrl))
|
||||||
|
{
|
||||||
|
state.MediaPath = mediaUrl;
|
||||||
|
state.IsRemote = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//state.RunTimeTicks = recording.RunTimeTicks;
|
//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.ReadInputAtNativeFramerate = recording.RecordingInfo.Status == RecordingStatus.InProgress;
|
||||||
state.SendInputOverStandardInput = recording.RecordingInfo.Status == RecordingStatus.InProgress;
|
|
||||||
state.AudioSync = "1000";
|
state.AudioSync = "1000";
|
||||||
state.DeInterlace = true;
|
state.DeInterlace = true;
|
||||||
}
|
}
|
||||||
|
@ -1359,6 +1375,8 @@ namespace MediaBrowser.Api.Playback
|
||||||
{
|
{
|
||||||
state.MediaPath = streamInfo.Path;
|
state.MediaPath = streamInfo.Path;
|
||||||
state.IsRemote = false;
|
state.IsRemote = false;
|
||||||
|
|
||||||
|
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else if (!string.IsNullOrEmpty(streamInfo.Url))
|
else if (!string.IsNullOrEmpty(streamInfo.Url))
|
||||||
{
|
{
|
||||||
|
@ -1366,7 +1384,6 @@ namespace MediaBrowser.Api.Playback
|
||||||
state.IsRemote = true;
|
state.IsRemote = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.SendInputOverStandardInput = true;
|
|
||||||
state.ReadInputAtNativeFramerate = true;
|
state.ReadInputAtNativeFramerate = true;
|
||||||
state.AudioSync = "1000";
|
state.AudioSync = "1000";
|
||||||
state.DeInterlace = true;
|
state.DeInterlace = true;
|
||||||
|
@ -1411,6 +1428,11 @@ namespace MediaBrowser.Api.Playback
|
||||||
state.SubtitleStream = GetMediaStream(mediaStreams, videoRequest.SubtitleStreamIndex, MediaStreamType.Subtitle, false);
|
state.SubtitleStream = GetMediaStream(mediaStreams, videoRequest.SubtitleStreamIndex, MediaStreamType.Subtitle, false);
|
||||||
state.AudioStream = GetMediaStream(mediaStreams, videoRequest.AudioStreamIndex, MediaStreamType.Audio);
|
state.AudioStream = GetMediaStream(mediaStreams, videoRequest.AudioStreamIndex, MediaStreamType.Audio);
|
||||||
|
|
||||||
|
if (state.VideoStream != null && state.VideoStream.IsInterlaced)
|
||||||
|
{
|
||||||
|
state.DeInterlace = true;
|
||||||
|
}
|
||||||
|
|
||||||
EnforceResolutionLimit(state, videoRequest);
|
EnforceResolutionLimit(state, videoRequest);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1420,8 +1442,8 @@ namespace MediaBrowser.Api.Playback
|
||||||
|
|
||||||
state.HasMediaStreams = mediaStreams.Count > 0;
|
state.HasMediaStreams = mediaStreams.Count > 0;
|
||||||
|
|
||||||
state.SegmentLength = state.ReadInputAtNativeFramerate ? 3 : 10;
|
state.SegmentLength = state.ReadInputAtNativeFramerate ? 5 : 10;
|
||||||
state.HlsListSize = state.ReadInputAtNativeFramerate ? 20 : 1440;
|
state.HlsListSize = state.ReadInputAtNativeFramerate ? 100 : 1440;
|
||||||
|
|
||||||
return state;
|
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 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);
|
var inputModifier = GetInputModifier(state);
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||||
|
|
||||||
const string vn = " -vn";
|
const string vn = " -vn";
|
||||||
|
|
||||||
var threads = GetNumberOfThreads(false);
|
var threads = GetNumberOfThreads(state, false);
|
||||||
|
|
||||||
var inputModifier = GetInputModifier(state);
|
var inputModifier = GetInputModifier(state);
|
||||||
|
|
||||||
|
|
|
@ -214,12 +214,16 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||||
|
|
||||||
if (request.Static)
|
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))
|
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;
|
return GetStreamResult(state, responseHeaders, isHeadRequest).Result;
|
||||||
|
@ -287,7 +291,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||||
|
|
||||||
responseHeaders["Accept-Ranges"] = "none";
|
responseHeaders["Accept-Ranges"] = "none";
|
||||||
|
|
||||||
var contentType = MimeTypes.GetMimeType(outputPath);
|
var contentType = state.GetMimeType(outputPath);
|
||||||
|
|
||||||
// Headers only
|
// Headers only
|
||||||
if (isHeadRequest)
|
if (isHeadRequest)
|
||||||
|
|
|
@ -102,7 +102,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||||
format = " -f mp4 -movflags frag_keyframe+empty_moov";
|
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);
|
var inputModifier = GetInputModifier(state);
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,8 @@ namespace MediaBrowser.Api.Playback
|
||||||
public bool ThrowDebugError { get; set; }
|
public bool ThrowDebugError { get; set; }
|
||||||
|
|
||||||
public string Params { get; set; }
|
public string Params { get; set; }
|
||||||
|
|
||||||
|
public string ForcedMimeType { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VideoStreamRequest : StreamRequest
|
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 MediaBrowser.Model.IO;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -72,5 +73,20 @@ namespace MediaBrowser.Api.Playback
|
||||||
public string InputVideoCodec { get; set; }
|
public string InputVideoCodec { get; set; }
|
||||||
|
|
||||||
public string InputAudioCodec { 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.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
|
@ -63,6 +64,9 @@ namespace MediaBrowser.Api
|
||||||
[ApiMember(Name = "IncludeArtists", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
[ApiMember(Name = "IncludeArtists", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||||
public bool IncludeArtists { get; set; }
|
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()
|
public GetSearchHints()
|
||||||
{
|
{
|
||||||
IncludeArtists = true;
|
IncludeArtists = true;
|
||||||
|
@ -130,7 +134,8 @@ namespace MediaBrowser.Api
|
||||||
IncludePeople = request.IncludePeople,
|
IncludePeople = request.IncludePeople,
|
||||||
IncludeStudios = request.IncludeStudios,
|
IncludeStudios = request.IncludeStudios,
|
||||||
StartIndex = request.StartIndex,
|
StartIndex = request.StartIndex,
|
||||||
UserId = request.UserId
|
UserId = request.UserId,
|
||||||
|
IncludeItemTypes = (request.IncludeItemTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray()
|
||||||
|
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -206,7 +211,8 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
result.SongCount = songs.Count;
|
result.SongCount = songs.Count;
|
||||||
|
|
||||||
result.Artists = _libraryManager.GetAllArtists(songs)
|
result.Artists = songs.SelectMany(i => i.AllArtists)
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
result.AlbumArtist = songs.Select(i => i.AlbumArtist).FirstOrDefault(i => !string.IsNullOrEmpty(i));
|
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")]
|
[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; }
|
public string PlayableMediaTypes { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class SessionsService
|
/// Class SessionsService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -368,4 +368,4 @@ namespace MediaBrowser.Api
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -73,7 +73,7 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
var item = string.IsNullOrEmpty(request.Id) ?
|
var item = string.IsNullOrEmpty(request.Id) ?
|
||||||
(request.UserId.HasValue ? user.RootFolder :
|
(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();
|
var fields = request.GetItemFields().ToList();
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ namespace MediaBrowser.Api
|
||||||
? libraryManager.RootFolder.GetRecursiveChildren(i => i.Id != item.Id)
|
? libraryManager.RootFolder.GetRecursiveChildren(i => i.Id != item.Id)
|
||||||
: user.RootFolder.GetRecursiveChildren(user, 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();
|
.ToList();
|
||||||
|
|
||||||
IEnumerable<BaseItem> returnItems = items;
|
IEnumerable<BaseItem> returnItems = items;
|
||||||
|
@ -106,12 +106,12 @@ namespace MediaBrowser.Api
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
/// <param name="inputItems">The input items.</param>
|
/// <param name="inputItems">The input items.</param>
|
||||||
/// <param name="includeInSearch">The include in search.</param>
|
|
||||||
/// <param name="getSimilarityScore">The get similarity score.</param>
|
/// <param name="getSimilarityScore">The get similarity score.</param>
|
||||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
/// <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)))
|
return inputItems.Select(i => new Tuple<BaseItem, int>(i, getSimilarityScore(item, i)))
|
||||||
.Where(i => i.Item2 > 2)
|
.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))
|
if (!string.IsNullOrEmpty(item1.OfficialRating) && string.Equals(item1.OfficialRating, item2.OfficialRating, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
points += 1;
|
points += 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find common genres
|
// Find common genres
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace MediaBrowser.Api
|
||||||
/// Class GetNextUpEpisodes
|
/// Class GetNextUpEpisodes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Route("/Shows/NextUp", "GET")]
|
[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
|
public class GetNextUpEpisodes : IReturn<ItemsResult>, IHasItemFields
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -53,6 +53,39 @@ namespace MediaBrowser.Api
|
||||||
public string SeriesId { get; set; }
|
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")]
|
[Route("/Shows/{Id}/Similar", "GET")]
|
||||||
[Api(Description = "Finds tv shows similar to a given one.")]
|
[Api(Description = "Finds tv shows similar to a given one.")]
|
||||||
public class GetSimilarShows : BaseGetSimilarItemsFromItem
|
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")]
|
[ApiMember(Name = "SeasonId", Description = "Optional. Filter by season id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||||
public string SeasonId { get; set; }
|
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")]
|
[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; }
|
public bool? IsMissing { get; set; }
|
||||||
|
|
||||||
|
@ -186,6 +219,39 @@ namespace MediaBrowser.Api
|
||||||
return ToOptimizedSerializedResultUsingCache(result);
|
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>
|
/// <summary>
|
||||||
/// Gets the specified request.
|
/// Gets the specified request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -198,7 +264,7 @@ namespace MediaBrowser.Api
|
||||||
var itemsList = GetNextUpEpisodes(request)
|
var itemsList = GetNextUpEpisodes(request)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var pagedItems = ApplyPaging(request, itemsList);
|
var pagedItems = ApplyPaging(itemsList, request.StartIndex, request.Limit);
|
||||||
|
|
||||||
var fields = request.GetItemFields().ToList();
|
var fields = request.GetItemFields().ToList();
|
||||||
|
|
||||||
|
@ -234,11 +300,13 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
return FilterSeries(request, series)
|
return FilterSeries(request, series)
|
||||||
.AsParallel()
|
.AsParallel()
|
||||||
.Select(i => GetNextUp(i, currentUser, request).Item1)
|
.Select(i => GetNextUp(i, currentUser))
|
||||||
.Where(i => i != null)
|
.Where(i => i.Item1 != null)
|
||||||
.OrderByDescending(i =>
|
.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)
|
if (seriesUserData.IsFavorite)
|
||||||
{
|
{
|
||||||
|
@ -252,7 +320,9 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
})
|
})
|
||||||
.ThenByDescending(i => i.PremiereDate ?? DateTime.MinValue);
|
.ThenByDescending(i =>i.Item2)
|
||||||
|
.ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue)
|
||||||
|
.Select(i => i.Item1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -260,9 +330,8 @@ namespace MediaBrowser.Api
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="series">The series.</param>
|
/// <param name="series">The series.</param>
|
||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
/// <param name="request">The request.</param>
|
|
||||||
/// <returns>Task{Episode}.</returns>
|
/// <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
|
// Get them in display order, then reverse
|
||||||
var allEpisodes = series.GetSeasons(user, true, true)
|
var allEpisodes = series.GetSeasons(user, true, true)
|
||||||
|
@ -321,21 +390,22 @@ namespace MediaBrowser.Api
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies the paging.
|
/// Applies the paging.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request">The request.</param>
|
|
||||||
/// <param name="items">The items.</param>
|
/// <param name="items">The items.</param>
|
||||||
|
/// <param name="startIndex">The start index.</param>
|
||||||
|
/// <param name="limit">The limit.</param>
|
||||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
/// <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
|
// Start at
|
||||||
if (request.StartIndex.HasValue)
|
if (startIndex.HasValue)
|
||||||
{
|
{
|
||||||
items = items.Skip(request.StartIndex.Value);
|
items = items.Skip(startIndex.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return limit
|
// Return limit
|
||||||
if (request.Limit.HasValue)
|
if (limit.HasValue)
|
||||||
{
|
{
|
||||||
items = items.Take(request.Limit.Value);
|
items = items.Take(limit.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
|
@ -409,7 +479,7 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Get(GetEpisodes request)
|
public object Get(GetEpisodes request)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(request.UserId);
|
var user = _userManager.GetUserById(request.UserId);
|
||||||
|
@ -435,7 +505,7 @@ namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
throw new ResourceNotFoundException("No season exists with Id " + request.SeasonId);
|
throw new ResourceNotFoundException("No season exists with Id " + request.SeasonId);
|
||||||
}
|
}
|
||||||
|
|
||||||
episodes = season.GetEpisodes(user);
|
episodes = season.GetEpisodes(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,10 +85,10 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
{
|
{
|
||||||
var user = UserManager.GetUserById(request.UserId.Value);
|
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>
|
/// <summary>
|
||||||
|
@ -111,7 +111,10 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
|
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
|
||||||
protected override IEnumerable<MusicArtist> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
|
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 =>
|
.Select(name =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -126,10 +129,5 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
|
|
||||||
}).Where(i => i != null);
|
}).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;
|
User user = null;
|
||||||
BaseItem item;
|
BaseItem item;
|
||||||
|
List<BaseItem> libraryItems;
|
||||||
|
|
||||||
if (request.UserId.HasValue)
|
if (request.UserId.HasValue)
|
||||||
{
|
{
|
||||||
user = UserManager.GetUserById(request.UserId.Value);
|
user = UserManager.GetUserById(request.UserId.Value);
|
||||||
item = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : DtoService.GetItemByDtoId(request.ParentId, user.Id);
|
item = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : DtoService.GetItemByDtoId(request.ParentId, user.Id);
|
||||||
|
|
||||||
|
libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
item = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : DtoService.GetItemByDtoId(request.ParentId);
|
item = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : DtoService.GetItemByDtoId(request.ParentId);
|
||||||
|
|
||||||
|
libraryItems = LibraryManager.RootFolder.RecursiveChildren.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<BaseItem> items;
|
IEnumerable<BaseItem> items;
|
||||||
|
@ -93,7 +99,7 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
|
|
||||||
var filteredItems = FilterItems(request, extractedItems, user);
|
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>();
|
filteredItems = ItemsService.ApplySortOrder(request, filteredItems, user, LibraryManager).Cast<TItemType>();
|
||||||
|
|
||||||
|
@ -122,45 +128,39 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
|
|
||||||
var fields = request.GetItemFields().ToList();
|
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();
|
result.Items = dtos.Where(i => i != null).ToArray();
|
||||||
|
|
||||||
return result;
|
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();
|
var filters = request.GetFilters().ToList();
|
||||||
|
|
||||||
if (filters.Contains(ItemFilter.IsPlayed))
|
if (filters.Contains(ItemFilter.IsPlayed))
|
||||||
{
|
{
|
||||||
var libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
|
items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsPlayed(user)));
|
||||||
|
|
||||||
items = items.Where(i => GetLibraryItems(i, libraryItems).All(l => l.IsPlayed(user)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filters.Contains(ItemFilter.IsUnplayed))
|
if (filters.Contains(ItemFilter.IsUnplayed))
|
||||||
{
|
{
|
||||||
var libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
|
items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsUnplayed(user)));
|
||||||
|
|
||||||
items = items.Where(i => GetLibraryItems(i, libraryItems).All(l => l.IsUnplayed(user)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.IsPlayed.HasValue)
|
if (request.IsPlayed.HasValue)
|
||||||
{
|
{
|
||||||
var val = request.IsPlayed.Value;
|
var val = request.IsPlayed.Value;
|
||||||
|
|
||||||
var libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
|
items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsPlayed(user)) == val);
|
||||||
|
|
||||||
items = items.Where(i => GetLibraryItems(i, libraryItems).All(l => l.IsPlayed(user)) == val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract IEnumerable<BaseItem> GetLibraryItems(TItemType item, IEnumerable<BaseItem> libraryItems);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Filters the items.
|
/// Filters the items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -174,6 +174,10 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
{
|
{
|
||||||
items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1);
|
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))
|
if (!string.IsNullOrEmpty(request.NameLessThan))
|
||||||
{
|
{
|
||||||
|
@ -288,11 +292,11 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
/// <param name="fields">The fields.</param>
|
/// <param name="fields">The fields.</param>
|
||||||
|
/// <param name="libraryItems">The library items.</param>
|
||||||
/// <returns>Task{DtoBaseItem}.</returns>
|
/// <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) :
|
var dto = DtoService.GetItemByNameDto(item, fields, libraryItems, user);
|
||||||
DtoService.GetBaseItemDto(item, fields, user);
|
|
||||||
|
|
||||||
return dto;
|
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")]
|
[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; }
|
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")]
|
[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 string NameLessThan { get; set; }
|
||||||
|
|
||||||
public GetItemsByName()
|
public GetItemsByName()
|
||||||
{
|
{
|
||||||
Recursive = true;
|
Recursive = true;
|
||||||
|
|
|
@ -76,10 +76,10 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
{
|
{
|
||||||
var user = UserManager.GetUserById(request.UserId.Value);
|
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>
|
/// <summary>
|
||||||
|
@ -109,10 +109,5 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
.Select(name => LibraryManager.GetGameGenre(name));
|
.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);
|
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>
|
/// <summary>
|
||||||
|
@ -112,10 +112,5 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
.Select(name => LibraryManager.GetGenre(name));
|
.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")]
|
[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; }
|
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")]
|
[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; }
|
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);
|
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))
|
if (!string.IsNullOrEmpty(request.AlbumArtistStartsWithOrGreater))
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,10 +76,10 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
{
|
{
|
||||||
var user = UserManager.GetUserById(request.UserId.Value);
|
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>
|
/// <summary>
|
||||||
|
@ -109,10 +109,5 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
.Select(name => LibraryManager.GetMusicGenre(name));
|
.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);
|
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>
|
/// <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));
|
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);
|
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>
|
/// <summary>
|
||||||
|
@ -114,10 +114,5 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
.Select(name => LibraryManager.GetStudio(name));
|
.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>
|
/// </summary>
|
||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The request.</param>
|
||||||
public object Post(MarkPlayedItem request)
|
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);
|
var user = _userManager.GetUserById(request.UserId);
|
||||||
|
|
||||||
DateTime? datePlayed = null;
|
DateTime? datePlayed = null;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(request.DatePlayed))
|
if (!string.IsNullOrEmpty(request.DatePlayed))
|
||||||
{
|
{
|
||||||
datePlayed = DateTime.ParseExact(request.DatePlayed, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
|
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()
|
private SessionInfo GetSession()
|
||||||
|
@ -780,13 +796,29 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The request.</param>
|
||||||
public object Delete(MarkUnplayedItem request)
|
public object Delete(MarkUnplayedItem request)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(request.UserId);
|
var task = MarkUnplayed(request);
|
||||||
|
|
||||||
var task = UpdatePlayedStatus(user, request.Id, false, null);
|
|
||||||
|
|
||||||
return ToOptimizedResult(task.Result);
|
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>
|
/// <summary>
|
||||||
/// Updates the played status.
|
/// Updates the played status.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -7,7 +7,6 @@ using MediaBrowser.Model.Querying;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.UserLibrary
|
namespace MediaBrowser.Api.UserLibrary
|
||||||
|
@ -81,10 +80,10 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
{
|
{
|
||||||
var user = UserManager.GetUserById(request.UserId.Value);
|
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>
|
/// <summary>
|
||||||
|
@ -115,19 +114,5 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.Select(year => LibraryManager.GetYear(year));
|
.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>
|
/// <param name="owner">The owner.</param>
|
||||||
/// <returns>Task{BaseItemDto}.</returns>
|
/// <returns>Task{BaseItemDto}.</returns>
|
||||||
BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null);
|
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>
|
/// <value>The artist.</value>
|
||||||
public List<string> Artists { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets or sets the album.
|
/// Gets or sets the album.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
using MediaBrowser.Common.Progress;
|
using MediaBrowser.Common.Progress;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using MediaBrowser.Model.Dto;
|
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -17,9 +15,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasTags, IHasProductionLocations, IHasLookupInfo<ArtistInfo>
|
public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasTags, IHasProductionLocations, IHasLookupInfo<ArtistInfo>
|
||||||
{
|
{
|
||||||
[IgnoreDataMember]
|
|
||||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
|
||||||
|
|
||||||
public bool IsAccessedByName { get; set; }
|
public bool IsAccessedByName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -65,7 +60,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||||
|
|
||||||
public MusicArtist()
|
public MusicArtist()
|
||||||
{
|
{
|
||||||
UserItemCountList = new List<ItemByNameCounts>();
|
|
||||||
Tags = new List<string>();
|
Tags = new List<string>();
|
||||||
ProductionLocations = new List<string>();
|
ProductionLocations = new List<string>();
|
||||||
}
|
}
|
||||||
|
@ -230,5 +224,10 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||||
|
|
||||||
return info;
|
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 System;
|
||||||
using MediaBrowser.Model.Dto;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities.Audio
|
namespace MediaBrowser.Controller.Entities.Audio
|
||||||
{
|
{
|
||||||
|
@ -10,11 +9,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MusicGenre : BaseItem, IItemByName
|
public class MusicGenre : BaseItem, IItemByName
|
||||||
{
|
{
|
||||||
public MusicGenre()
|
|
||||||
{
|
|
||||||
UserItemCountList = new List<ItemByNameCounts>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user data key.
|
/// Gets the user data key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -24,9 +18,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||||
return "MusicGenre-" + Name;
|
return "MusicGenre-" + Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
[IgnoreDataMember]
|
|
||||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the folder containing the item.
|
/// Returns the folder containing the item.
|
||||||
/// If the item is a folder, it returns the folder itself
|
/// If the item is a folder, it returns the folder itself
|
||||||
|
@ -51,5 +42,10 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||||
return false;
|
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]
|
[IgnoreDataMember]
|
||||||
public virtual bool IsOwnedItem
|
public virtual bool IsOwnedItem
|
||||||
{
|
{
|
||||||
|
@ -1175,7 +1184,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return GetImageInfo(type, imageIndex) != null;
|
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)
|
if (type == ImageType.Chapter)
|
||||||
{
|
{
|
||||||
|
@ -1330,7 +1339,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <param name="images">The images.</param>
|
/// <param name="images">The images.</param>
|
||||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||||
/// <exception cref="System.ArgumentException">Cannot call AddImages with chapter images</exception>
|
/// <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)
|
if (imageType == ImageType.Chapter)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using MediaBrowser.Model.Entities;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -8,18 +7,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BasePluginFolder : Folder, ICollectionFolder, IByReferenceItem
|
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()
|
protected BasePluginFolder()
|
||||||
{
|
{
|
||||||
DisplayMediaType = "CollectionFolder";
|
DisplayMediaType = "CollectionFolder";
|
||||||
|
|
|
@ -264,7 +264,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
[IgnoreDataMember]
|
[IgnoreDataMember]
|
||||||
public IEnumerable<BaseItem> Children
|
public IEnumerable<BaseItem> Children
|
||||||
{
|
{
|
||||||
get { return ActualChildren; }
|
get { return ActualChildren.Where(i => !i.IsHidden); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -745,9 +745,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
var list = new List<BaseItem>();
|
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>
|
/// <summary>
|
||||||
|
@ -905,13 +905,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <returns>BaseItem.</returns>
|
/// <returns>BaseItem.</returns>
|
||||||
private BaseItem GetLinkedChild(LinkedChild info)
|
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
|
// First get using the cached Id
|
||||||
if (info.ItemId.HasValue)
|
if (info.ItemId.HasValue)
|
||||||
{
|
{
|
||||||
|
@ -920,20 +913,19 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return null;
|
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
|
var item = FindLinkedChild(info);
|
||||||
if (item == null)
|
|
||||||
{
|
|
||||||
item = LibraryManager.RootFolder.FindByPath(info.Path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If still null, log
|
// If still null, log
|
||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
Logger.Warn("Unable to find linked item at {0}", info.Path);
|
|
||||||
|
|
||||||
// Don't keep searching over and over
|
// Don't keep searching over and over
|
||||||
info.ItemId = Guid.Empty;
|
info.ItemId = Guid.Empty;
|
||||||
}
|
}
|
||||||
|
@ -946,6 +938,43 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return item;
|
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)
|
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var changesFound = false;
|
var changesFound = false;
|
||||||
|
@ -1106,5 +1135,10 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return GetRecursiveChildren(user).Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual)
|
return GetRecursiveChildren(user).Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual)
|
||||||
.All(i => i.IsUnplayed(user));
|
.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.Collections.Generic;
|
||||||
using System.Runtime.Serialization;
|
using System.Linq;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
public class GameGenre : BaseItem, IItemByName
|
public class GameGenre : BaseItem, IItemByName
|
||||||
{
|
{
|
||||||
public GameGenre()
|
|
||||||
{
|
|
||||||
UserItemCountList = new List<ItemByNameCounts>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user data key.
|
/// Gets the user data key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -20,9 +15,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return "GameGenre-" + Name;
|
return "GameGenre-" + Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
[IgnoreDataMember]
|
|
||||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the folder containing the item.
|
/// Returns the folder containing the item.
|
||||||
/// If the item is a folder, it returns the folder itself
|
/// If the item is a folder, it returns the folder itself
|
||||||
|
@ -47,5 +39,10 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return false;
|
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.Collections.Generic;
|
||||||
using System.Runtime.Serialization;
|
using System.Linq;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
|
@ -9,11 +10,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Genre : BaseItem, IItemByName
|
public class Genre : BaseItem, IItemByName
|
||||||
{
|
{
|
||||||
public Genre()
|
|
||||||
{
|
|
||||||
UserItemCountList = new List<ItemByNameCounts>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user data key.
|
/// Gets the user data key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -23,9 +19,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return "Genre-" + Name;
|
return "Genre-" + Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
[IgnoreDataMember]
|
|
||||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the folder containing the item.
|
/// Returns the folder containing the item.
|
||||||
/// If the item is a folder, it returns the folder itself
|
/// If the item is a folder, it returns the folder itself
|
||||||
|
@ -50,5 +43,10 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return false;
|
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="type">The type.</param>
|
||||||
/// <param name="index">The index.</param>
|
/// <param name="index">The index.</param>
|
||||||
/// <param name="file">The file.</param>
|
/// <param name="file">The file.</param>
|
||||||
void SetImagePath(ImageType type, int index, FileInfo file);
|
void SetImagePath(ImageType type, int index, FileSystemInfo file);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified type has image.
|
/// 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="imageType">Type of the image.</param>
|
||||||
/// <param name="images">The images.</param>
|
/// <param name="images">The images.</param>
|
||||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Determines whether [is save local metadata enabled].
|
/// Determines whether [is save local metadata enabled].
|
||||||
|
@ -180,7 +180,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
/// <param name="imageType">Type of the image.</param>
|
/// <param name="imageType">Type of the image.</param>
|
||||||
/// <param name="file">The file.</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);
|
item.SetImagePath(imageType, 0, file);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
using MediaBrowser.Model.Dto;
|
using System.Collections.Generic;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
|
@ -10,37 +7,16 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IItemByName
|
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
|
public interface IHasDualAccess : IItemByName
|
||||||
{
|
{
|
||||||
bool IsAccessedByName { get; }
|
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 string Path { get; set; }
|
||||||
public LinkedChildType Type { get; set; }
|
public LinkedChildType Type { get; set; }
|
||||||
|
|
||||||
|
public string ItemName { get; set; }
|
||||||
|
public string ItemType { get; set; }
|
||||||
|
public int? ItemYear { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serves as a cache
|
/// Serves as a cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -18,8 +22,8 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
public enum LinkedChildType
|
public enum LinkedChildType
|
||||||
{
|
{
|
||||||
Manual = 1,
|
Manual = 0,
|
||||||
Shortcut = 2
|
Shortcut = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LinkedChildComparer : IEqualityComparer<LinkedChild>
|
public class LinkedChildComparer : IEqualityComparer<LinkedChild>
|
||||||
|
@ -35,7 +39,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
public int GetHashCode(LinkedChild obj)
|
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.Controller.Providers;
|
||||||
using MediaBrowser.Model.Dto;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.Serialization;
|
using System.Linq;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
|
@ -10,19 +10,11 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Person : BaseItem, IItemByName, IHasLookupInfo<PersonLookupInfo>
|
public class Person : BaseItem, IItemByName, IHasLookupInfo<PersonLookupInfo>
|
||||||
{
|
{
|
||||||
public Person()
|
|
||||||
{
|
|
||||||
UserItemCountList = new List<ItemByNameCounts>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the place of birth.
|
/// Gets or sets the place of birth.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The place of birth.</value>
|
/// <value>The place of birth.</value>
|
||||||
public string PlaceOfBirth { get; set; }
|
public string PlaceOfBirth { get; set; }
|
||||||
|
|
||||||
[IgnoreDataMember]
|
|
||||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user data key.
|
/// Gets the user data key.
|
||||||
|
@ -62,6 +54,11 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return false;
|
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>
|
/// <summary>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System.Runtime.Serialization;
|
using System;
|
||||||
using MediaBrowser.Model.Dto;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
|
@ -10,11 +9,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Studio : BaseItem, IItemByName
|
public class Studio : BaseItem, IItemByName
|
||||||
{
|
{
|
||||||
public Studio()
|
|
||||||
{
|
|
||||||
UserItemCountList = new List<ItemByNameCounts>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user data key.
|
/// Gets the user data key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -24,9 +18,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return "Studio-" + Name;
|
return "Studio-" + Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
[IgnoreDataMember]
|
|
||||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the folder containing the item.
|
/// Returns the folder containing the item.
|
||||||
/// If the item is a folder, it returns the folder itself
|
/// If the item is a folder, it returns the folder itself
|
||||||
|
@ -51,5 +42,10 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return false;
|
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 MediaBrowser.Controller.Providers;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
var hasChanges = base.BeforeMetadataRefresh();
|
var hasChanges = base.BeforeMetadataRefresh();
|
||||||
|
|
||||||
if (string.Equals("default", Name, System.StringComparison.OrdinalIgnoreCase))
|
if (string.Equals("default", Name, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
Name = "Media Folders";
|
Name = "Media Folders";
|
||||||
hasChanges = true;
|
hasChanges = true;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System.Runtime.Serialization;
|
using System.Collections.Generic;
|
||||||
using MediaBrowser.Model.Dto;
|
using System.Globalization;
|
||||||
using System;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
|
@ -10,14 +9,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Year : BaseItem, IItemByName
|
public class Year : BaseItem, IItemByName
|
||||||
{
|
{
|
||||||
public Year()
|
|
||||||
{
|
|
||||||
UserItemCountList = new List<ItemByNameCounts>();
|
|
||||||
}
|
|
||||||
|
|
||||||
[IgnoreDataMember]
|
|
||||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user data key.
|
/// Gets the user data key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -51,5 +42,19 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return false;
|
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>
|
/// <returns>System.String.</returns>
|
||||||
string FindCollectionType(BaseItem item);
|
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>
|
/// <summary>
|
||||||
/// Normalizes the root path list.
|
/// Normalizes the root path list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -51,9 +51,8 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// Refreshes metadata for each user
|
/// Refreshes metadata for each user
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <param name="force">if set to <c>true</c> [force].</param>
|
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task RefreshUsersMetadata(CancellationToken cancellationToken, bool force = false);
|
Task RefreshUsersMetadata(CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Renames the user.
|
/// Renames the user.
|
||||||
|
|
|
@ -27,94 +27,94 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A season folder must contain one of these somewhere in the name
|
/// A season folder must contain one of these somewhere in the name
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly string[] SeasonFolderNames = new[]
|
private static readonly string[] SeasonFolderNames =
|
||||||
{
|
{
|
||||||
"season",
|
"season",
|
||||||
"sæson",
|
"sæson",
|
||||||
"temporada",
|
"temporada",
|
||||||
"saison",
|
"saison",
|
||||||
"staffel",
|
"staffel",
|
||||||
"series",
|
"series",
|
||||||
"сезон"
|
"сезон"
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to detect paths that represent episodes, need to make sure they don't also
|
/// Used to detect paths that represent episodes, need to make sure they don't also
|
||||||
/// match movie titles like "2001 A Space..."
|
/// match movie titles like "2001 A Space..."
|
||||||
/// Currently we limit the numbers here to 2 digits to try and avoid this
|
/// Currently we limit the numbers here to 2 digits to try and avoid this
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly Regex[] EpisodeExpressions = new[]
|
private static readonly Regex[] EpisodeExpressions =
|
||||||
{
|
{
|
||||||
new Regex(
|
new Regex(
|
||||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})[^\\\/]*$",
|
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})[^\\\/]*$",
|
||||||
RegexOptions.Compiled),
|
RegexOptions.Compiled),
|
||||||
new Regex(
|
new Regex(
|
||||||
@".*(\\|\/)[sS](?<seasonnumber>\d{1,4})[x,X]?[eE](?<epnumber>\d{1,3})[^\\\/]*$",
|
@".*(\\|\/)[sS](?<seasonnumber>\d{1,4})[x,X]?[eE](?<epnumber>\d{1,3})[^\\\/]*$",
|
||||||
RegexOptions.Compiled),
|
RegexOptions.Compiled),
|
||||||
new Regex(
|
new Regex(
|
||||||
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))[^\\\/]*$",
|
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))[^\\\/]*$",
|
||||||
RegexOptions.Compiled),
|
RegexOptions.Compiled),
|
||||||
new Regex(
|
new Regex(
|
||||||
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})[^\\\/]*$",
|
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})[^\\\/]*$",
|
||||||
RegexOptions.Compiled)
|
RegexOptions.Compiled)
|
||||||
};
|
};
|
||||||
private static readonly Regex[] MultipleEpisodeExpressions = new[]
|
private static readonly Regex[] MultipleEpisodeExpressions =
|
||||||
{
|
{
|
||||||
new Regex(
|
new Regex(
|
||||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[eExX](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[eExX](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||||
RegexOptions.Compiled),
|
RegexOptions.Compiled),
|
||||||
new Regex(
|
new Regex(
|
||||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||||
RegexOptions.Compiled),
|
RegexOptions.Compiled),
|
||||||
new Regex(
|
new Regex(
|
||||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||||
RegexOptions.Compiled),
|
RegexOptions.Compiled),
|
||||||
new Regex(
|
new Regex(
|
||||||
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})(-[xE]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})(-[xE]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||||
RegexOptions.Compiled),
|
RegexOptions.Compiled),
|
||||||
new Regex(
|
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}))+[^\\\/]*$",
|
@".*(\\|\/)(?<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),
|
RegexOptions.Compiled),
|
||||||
new Regex(
|
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}))+[^\\\/]*$",
|
@".*(\\|\/)(?<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),
|
RegexOptions.Compiled),
|
||||||
new Regex(
|
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}))+[^\\\/]*$",
|
@".*(\\|\/)(?<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),
|
RegexOptions.Compiled),
|
||||||
new Regex(
|
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}))+[^\\\/]*$",
|
@".*(\\|\/)(?<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),
|
RegexOptions.Compiled),
|
||||||
new Regex(
|
new Regex(
|
||||||
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||||
RegexOptions.Compiled),
|
RegexOptions.Compiled),
|
||||||
new Regex(
|
new Regex(
|
||||||
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
|
||||||
RegexOptions.Compiled)
|
RegexOptions.Compiled)
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// To avoid the following matching movies they are only valid when contained in a folder which has been matched as a being season
|
/// To avoid the following matching movies they are only valid when contained in a folder which has been matched as a being season
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly Regex[] EpisodeExpressionsInASeasonFolder = new[]
|
private static readonly Regex[] EpisodeExpressionsInASeasonFolder =
|
||||||
{
|
{
|
||||||
new Regex(
|
new Regex(
|
||||||
@".*(\\|\/)(?<epnumber>\d{1,2})\s?-\s?[^\\\/]*$",
|
@".*(\\|\/)(?<epnumber>\d{1,2})\s?-\s?[^\\\/]*$",
|
||||||
RegexOptions.Compiled),
|
RegexOptions.Compiled),
|
||||||
// 01 - blah.avi, 01-blah.avi
|
// 01 - blah.avi, 01-blah.avi
|
||||||
new Regex(
|
new Regex(
|
||||||
@".*(\\|\/)(?<epnumber>\d{1,2})[^\d\\]*[^\\\/]*$",
|
@".*(\\|\/)(?<epnumber>\d{1,2})[^\d\\]*[^\\\/]*$",
|
||||||
RegexOptions.Compiled),
|
RegexOptions.Compiled),
|
||||||
// 01.avi, 01.blah.avi "01 - 22 blah.avi"
|
// 01.avi, 01.blah.avi "01 - 22 blah.avi"
|
||||||
new Regex(
|
new Regex(
|
||||||
@".*(\\|\/)(?<seasonnumber>\d)(?<epnumber>\d{1,2})[^\d\\]+[^\\\/]*$",
|
@".*(\\|\/)(?<seasonnumber>\d)(?<epnumber>\d{1,2})[^\d\\]+[^\\\/]*$",
|
||||||
RegexOptions.Compiled),
|
RegexOptions.Compiled),
|
||||||
// 01.avi, 01.blah.avi
|
// 01.avi, 01.blah.avi
|
||||||
new Regex(
|
new Regex(
|
||||||
@".*(\\|\/)\D*\d+(?<epnumber>\d{2})",
|
@".*(\\|\/)\D*\d+(?<epnumber>\d{2})",
|
||||||
RegexOptions.Compiled)
|
RegexOptions.Compiled)
|
||||||
// hell0 - 101 - hello.avi
|
// hell0 - 101 - hello.avi
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the season number from path.
|
/// Gets the season number from path.
|
||||||
|
@ -151,8 +151,8 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <returns>System.Nullable{System.Int32}.</returns>
|
/// <returns>System.Nullable{System.Int32}.</returns>
|
||||||
private static int? GetSeasonNumberFromPathSubstring(string path)
|
private static int? GetSeasonNumberFromPathSubstring(string path)
|
||||||
{
|
{
|
||||||
int numericStart = -1;
|
var numericStart = -1;
|
||||||
int length = 0;
|
var length = 0;
|
||||||
|
|
||||||
// Find out where the numbers start, and then keep going until they end
|
// Find out where the numbers start, and then keep going until they end
|
||||||
for (var i = 0; i < path.Length; i++)
|
for (var i = 0; i < path.Length; i++)
|
||||||
|
|
|
@ -138,13 +138,6 @@ namespace MediaBrowser.Controller.LiveTv
|
||||||
/// <param name="id">The identifier.</param>
|
/// <param name="id">The identifier.</param>
|
||||||
/// <returns>Channel.</returns>
|
/// <returns>Channel.</returns>
|
||||||
LiveTvChannel GetInternalChannel(string id);
|
LiveTvChannel GetInternalChannel(string id);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the internal program.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">The identifier.</param>
|
|
||||||
/// <returns>LiveTvProgram.</returns>
|
|
||||||
LiveTvProgram GetInternalProgram(string id);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the recording.
|
/// Gets the recording.
|
||||||
|
|
|
@ -1,20 +1,13 @@
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using MediaBrowser.Model.Dto;
|
|
||||||
using MediaBrowser.Model.LiveTv;
|
using MediaBrowser.Model.LiveTv;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.LiveTv
|
namespace MediaBrowser.Controller.LiveTv
|
||||||
{
|
{
|
||||||
public class LiveTvChannel : BaseItem, IItemByName
|
public class LiveTvChannel : BaseItem, IItemByName
|
||||||
{
|
{
|
||||||
public LiveTvChannel()
|
|
||||||
{
|
|
||||||
UserItemCountList = new List<ItemByNameCounts>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user data key.
|
/// Gets the user data key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -24,9 +17,6 @@ namespace MediaBrowser.Controller.LiveTv
|
||||||
return GetClientTypeName() + "-" + Name;
|
return GetClientTypeName() + "-" + Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
[IgnoreDataMember]
|
|
||||||
public List<ItemByNameCounts> UserItemCountList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the folder containing the item.
|
/// Returns the folder containing the item.
|
||||||
/// If the item is a folder, it returns the folder itself
|
/// If the item is a folder, it returns the folder itself
|
||||||
|
@ -119,5 +109,10 @@ namespace MediaBrowser.Controller.LiveTv
|
||||||
{
|
{
|
||||||
return "Channel";
|
return "Channel";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
|
||||||
|
{
|
||||||
|
return new List<BaseItem>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,11 @@
|
||||||
<Compile Include="..\SharedVersion.cs">
|
<Compile Include="..\SharedVersion.cs">
|
||||||
<Link>Properties\SharedVersion.cs</Link>
|
<Link>Properties\SharedVersion.cs</Link>
|
||||||
</Compile>
|
</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\IImageProcessor.cs" />
|
||||||
<Compile Include="Drawing\ImageFormat.cs" />
|
<Compile Include="Drawing\ImageFormat.cs" />
|
||||||
<Compile Include="Drawing\ImageProcessingOptions.cs" />
|
<Compile Include="Drawing\ImageProcessingOptions.cs" />
|
||||||
|
|
|
@ -95,6 +95,18 @@ namespace MediaBrowser.Controller.Net
|
||||||
/// <returns>System.Object.</returns>
|
/// <returns>System.Object.</returns>
|
||||||
object GetStaticFileResult(IRequest requestContext, string path, FileShare fileShare = FileShare.Read, IDictionary<string, string> responseHeaders = null, bool isHeadRequest = false);
|
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>
|
/// <summary>
|
||||||
/// Gets the optimized serialized result using cache.
|
/// Gets the optimized serialized result using cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Collections.Concurrent;
|
using MediaBrowser.Model.Logging;
|
||||||
using MediaBrowser.Model.Logging;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -10,10 +10,8 @@ namespace MediaBrowser.Controller.Providers
|
||||||
public interface IDirectoryService
|
public interface IDirectoryService
|
||||||
{
|
{
|
||||||
List<FileSystemInfo> GetFileSystemEntries(string path);
|
List<FileSystemInfo> GetFileSystemEntries(string path);
|
||||||
IEnumerable<FileInfo> GetFiles(string path);
|
IEnumerable<FileSystemInfo> GetFiles(string path);
|
||||||
IEnumerable<DirectoryInfo> GetDirectories(string path);
|
FileSystemInfo GetFile(string path);
|
||||||
FileInfo GetFile(string path);
|
|
||||||
DirectoryInfo GetDirectory(string path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DirectoryService : IDirectoryService
|
public class DirectoryService : IDirectoryService
|
||||||
|
@ -50,31 +48,17 @@ namespace MediaBrowser.Controller.Providers
|
||||||
return entries;
|
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)
|
public FileSystemInfo GetFile(string path)
|
||||||
{
|
|
||||||
return GetFileSystemEntries(path).OfType<DirectoryInfo>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileInfo GetFile(string path)
|
|
||||||
{
|
{
|
||||||
var directory = Path.GetDirectoryName(path);
|
var directory = Path.GetDirectoryName(path);
|
||||||
var filename = Path.GetFileName(path);
|
var filename = Path.GetFileName(path);
|
||||||
|
|
||||||
return GetFiles(directory).FirstOrDefault(i => string.Equals(i.Name, filename, StringComparison.OrdinalIgnoreCase));
|
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 class LocalImageInfo
|
||||||
{
|
{
|
||||||
public FileInfo FileInfo { get; set; }
|
public FileSystemInfo FileInfo { get; set; }
|
||||||
public ImageType Type { get; set; }
|
public ImageType Type { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,13 @@ namespace MediaBrowser.Controller.Session
|
||||||
/// <exception cref="System.ArgumentNullException"></exception>
|
/// <exception cref="System.ArgumentNullException"></exception>
|
||||||
Task OnPlaybackStopped(PlaybackStopInfo info);
|
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>
|
/// <summary>
|
||||||
/// Sends the system command.
|
/// Sends the system command.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="PlayTo\Argument.cs" />
|
<Compile Include="PlayTo\Argument.cs" />
|
||||||
<Compile Include="PlayTo\Configuration\DlnaProfile.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\Configuration\TranscodeSetting.cs" />
|
||||||
<Compile Include="PlayTo\CurrentIdEventArgs.cs" />
|
<Compile Include="PlayTo\CurrentIdEventArgs.cs" />
|
||||||
<Compile Include="PlayTo\Device.cs">
|
<Compile Include="PlayTo\Device.cs">
|
||||||
|
|
|
@ -29,7 +29,12 @@
|
||||||
FriendlyName = "^TV$",
|
FriendlyName = "^TV$",
|
||||||
ModelNumber = @"1\.0",
|
ModelNumber = @"1\.0",
|
||||||
ModelName = "Samsung DTV DMR",
|
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
|
var profile1 = new DlnaProfile
|
||||||
|
@ -38,7 +43,12 @@
|
||||||
ClientType = "DLNA",
|
ClientType = "DLNA",
|
||||||
FriendlyName = @"(^\[TV\][A-Z]{2}\d{2}(E|F)[A-Z]?\d{3,4}.*)|^\[TV\] Samsung",
|
FriendlyName = @"(^\[TV\][A-Z]{2}\d{2}(E|F)[A-Z]?\d{3,4}.*)|^\[TV\] Samsung",
|
||||||
ModelNumber = @"(1\.0)|(AllShare1\.0)",
|
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
|
var profile2 = new DlnaProfile
|
||||||
|
@ -47,7 +57,12 @@
|
||||||
ClientType = "DLNA",
|
ClientType = "DLNA",
|
||||||
FriendlyName = @"(^TV-\d{2}C\d{3}.*)|(^\[TV\][A-Z]{2}\d{2}(D)[A-Z]?\d{3,4}.*)|^\[TV\] Samsung",
|
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)",
|
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
|
var profile3 = new DlnaProfile
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace MediaBrowser.Dlna.PlayTo.Configuration
|
namespace MediaBrowser.Dlna.PlayTo.Configuration
|
||||||
{
|
{
|
||||||
|
@ -20,6 +21,14 @@ namespace MediaBrowser.Dlna.PlayTo.Configuration
|
||||||
/// </value>
|
/// </value>
|
||||||
public string TargetContainer { get; set; }
|
public string TargetContainer { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Mimetype to enforce
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The MimeType.
|
||||||
|
/// </value>
|
||||||
|
public string MimeType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default transcoding settings
|
/// The default transcoding settings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -46,19 +55,19 @@ namespace MediaBrowser.Dlna.PlayTo.Configuration
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(profile.FriendlyName))
|
if (!string.IsNullOrEmpty(profile.FriendlyName))
|
||||||
{
|
{
|
||||||
if (!string.Equals(deviceProperties.Name, profile.FriendlyName, StringComparison.OrdinalIgnoreCase))
|
if (!Regex.IsMatch(deviceProperties.Name, profile.FriendlyName))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(profile.ModelNumber))
|
if (!string.IsNullOrEmpty(profile.ModelNumber))
|
||||||
{
|
{
|
||||||
if (!string.Equals(deviceProperties.ModelNumber, profile.ModelNumber, StringComparison.OrdinalIgnoreCase))
|
if (!Regex.IsMatch(deviceProperties.ModelNumber, profile.ModelNumber))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(profile.ModelName))
|
if (!string.IsNullOrEmpty(profile.ModelName))
|
||||||
{
|
{
|
||||||
if (!string.Equals(deviceProperties.ModelName, profile.ModelName, StringComparison.OrdinalIgnoreCase))
|
if (!Regex.IsMatch(deviceProperties.ModelName, profile.ModelName))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,8 +76,8 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
|
|
||||||
_transportState = value;
|
_transportState = value;
|
||||||
|
|
||||||
if (value == "PLAYING" || value == "STOPPED")
|
if (value == TRANSPORTSTATE.PLAYING || value == TRANSPORTSTATE.STOPPED)
|
||||||
NotifyPlaybackChanged(value == "STOPPED");
|
NotifyPlaybackChanged(value == TRANSPORTSTATE.STOPPED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return TransportState == "PLAYING";
|
return TransportState == TRANSPORTSTATE.PLAYING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return (TransportState == "TRANSITIONING");
|
return (TransportState == TRANSPORTSTATE.TRANSITIONING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return TransportState == "PAUSED" || TransportState == "PAUSED_PLAYBACK";
|
return TransportState == TRANSPORTSTATE.PAUSED || TransportState == TRANSPORTSTATE.PAUSED_PLAYBACK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return (TransportState == "STOPPED");
|
return TransportState == TRANSPORTSTATE.STOPPED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,23 +127,39 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetTimerIntervalMs()
|
private int GetPlaybackTimerIntervalMs()
|
||||||
{
|
{
|
||||||
return 10000;
|
return 2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetInactiveTimerIntervalMs()
|
||||||
|
{
|
||||||
|
return 20000;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
UpdateTime = DateTime.UtcNow;
|
UpdateTime = DateTime.UtcNow;
|
||||||
|
|
||||||
var interval = GetTimerIntervalMs();
|
var interval = GetPlaybackTimerIntervalMs();
|
||||||
|
|
||||||
_timer = new Timer(TimerCallback, null, interval, interval);
|
_timer = new Timer(TimerCallback, null, interval, interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RestartTimer()
|
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);
|
_timer.Change(interval, interval);
|
||||||
}
|
}
|
||||||
|
@ -230,11 +246,9 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
{
|
{
|
||||||
StopTimer();
|
StopTimer();
|
||||||
|
|
||||||
TransportState = "STOPPED";
|
await SetStop().ConfigureAwait(false);
|
||||||
CurrentId = "0";
|
CurrentId = "0";
|
||||||
|
|
||||||
await Task.Delay(50).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
|
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
|
||||||
if (command == null)
|
if (command == null)
|
||||||
return false;
|
return false;
|
||||||
|
@ -261,7 +275,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
await SetPlay().ConfigureAwait(false);
|
await SetPlay().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
_count = 5;
|
_lapsCount = GetLapsCount();
|
||||||
RestartTimer();
|
RestartTimer();
|
||||||
|
|
||||||
return true;
|
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))
|
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType, 1))
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
_count = 5;
|
_lapsCount = GetLapsCount();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,7 +352,6 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
await Task.Delay(50).ConfigureAwait(false);
|
await Task.Delay(50).ConfigureAwait(false);
|
||||||
_count = 4;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,8 +375,13 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
|
|
||||||
#region Get data
|
#region Get data
|
||||||
|
|
||||||
// TODO: What is going on here
|
private int GetLapsCount()
|
||||||
int _count = 5;
|
{
|
||||||
|
// No need to get all data every lap, just every X time.
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _lapsCount = 0;
|
||||||
|
|
||||||
private async void TimerCallback(object sender)
|
private async void TimerCallback(object sender)
|
||||||
{
|
{
|
||||||
|
@ -374,18 +392,24 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var hasTrack = await GetPositionInfo().ConfigureAwait(false);
|
await GetTransportInfo().ConfigureAwait(false);
|
||||||
|
|
||||||
// TODO: Why make these requests if hasTrack==false?
|
//If we're not playing anything no need to get additional data
|
||||||
if (_count > 5)
|
if (TransportState != TRANSPORTSTATE.STOPPED)
|
||||||
{
|
{
|
||||||
await GetTransportInfo().ConfigureAwait(false);
|
var hasTrack = await GetPositionInfo().ConfigureAwait(false);
|
||||||
if (!hasTrack)
|
|
||||||
|
// 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)
|
catch (Exception ex)
|
||||||
|
@ -393,11 +417,16 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
_logger.ErrorException("Error updating device info", ex);
|
_logger.ErrorException("Error updating device info", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
_count++;
|
_lapsCount++;
|
||||||
|
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
return;
|
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()
|
private async Task GetVolume()
|
||||||
|
@ -747,5 +776,16 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
{
|
{
|
||||||
return String.Format("{0} - {1}", Properties.Name, Properties.BaseUrl);
|
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
|
namespace MediaBrowser.Dlna.PlayTo
|
||||||
{
|
{
|
||||||
public class PlayToController : ISessionController
|
public class PlayToController : ISessionController, IDisposable
|
||||||
{
|
{
|
||||||
private Device _device;
|
private Device _device;
|
||||||
private BaseItem _currentItem = null;
|
private BaseItem _currentItem = null;
|
||||||
|
@ -131,6 +131,14 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
|
|
||||||
((Timer)sender).Stop();
|
((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);
|
await ReportProgress().ConfigureAwait(false);
|
||||||
|
|
||||||
if (!_disposed && IsSessionActive)
|
if (!_disposed && IsSessionActive)
|
||||||
|
@ -479,3 +487,4 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,38 +1,105 @@
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Controller.Plugins;
|
using MediaBrowser.Controller.Plugins;
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace MediaBrowser.Dlna.PlayTo
|
namespace MediaBrowser.Dlna.PlayTo
|
||||||
{
|
{
|
||||||
public class PlayToServerEntryPoint : IServerEntryPoint
|
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, IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepo, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager)
|
||||||
|
|
||||||
public PlayToServerEntryPoint(ILogManager logManager, ISessionManager sessionManager, IUserManager userManager, IHttpClient httpClient, INetworkManager networkManager, IItemRepository itemRepository, ILibraryManager libraryManager)
|
|
||||||
{
|
{
|
||||||
_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()
|
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
|
#region Dispose
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (!_disposed)
|
DisposePlayToManager();
|
||||||
{
|
|
||||||
_disposed = true;
|
|
||||||
_manager.Stop();
|
|
||||||
_manager.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
|
@ -16,6 +16,8 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
|
|
||||||
public string FileFormat { get; set; }
|
public string FileFormat { get; set; }
|
||||||
|
|
||||||
|
public string MimeType { get; set; }
|
||||||
|
|
||||||
public int PlayState { get; set; }
|
public int PlayState { get; set; }
|
||||||
|
|
||||||
public string StreamUrl { get; set; }
|
public string StreamUrl { get; set; }
|
||||||
|
@ -51,10 +53,21 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(transcodeSetting.Container))
|
if (string.IsNullOrWhiteSpace(transcodeSetting.Container))
|
||||||
continue;
|
continue;
|
||||||
if (path.EndsWith(transcodeSetting.Container))
|
if (path.EndsWith(transcodeSetting.Container) && !string.IsNullOrWhiteSpace(transcodeSetting.TargetContainer))
|
||||||
{
|
{
|
||||||
playlistItem.Transcode = true;
|
playlistItem.Transcode = true;
|
||||||
playlistItem.FileFormat = transcodeSetting.TargetContainer;
|
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;
|
return playlistItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,9 +96,12 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
/// <returns>The url to send to the device</returns>
|
/// <returns>The url to send to the device</returns>
|
||||||
internal static string GetVideoUrl(DeviceProperties deviceProperties, PlaylistItem item, List<MediaStream> streams, string serverAddress)
|
internal static string GetVideoUrl(DeviceProperties deviceProperties, PlaylistItem item, List<MediaStream> streams, string serverAddress)
|
||||||
{
|
{
|
||||||
|
string dlnaCommand = string.Empty;
|
||||||
if (!item.Transcode)
|
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 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();
|
var audiostream = streams.Where(m => m.Type == MediaStreamType.Audio).OrderBy(m => m.IsDefault).FirstOrDefault();
|
||||||
|
|
||||||
|
@ -117,7 +120,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
audioChannels = 2;
|
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);
|
return string.Format("{0}/Videos/{1}/stream.{2}?{3}", serverAddress, item.ItemId, item.FileFormat, dlnaCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,12 +165,12 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builds the dlna URL.
|
/// Builds the dlna URL.
|
||||||
/// </summary>
|
/// </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 usCulture = new CultureInfo("en-US");
|
||||||
|
|
||||||
var dlnaparam = string.Format("Params={0};", deviceID);
|
var dlnaparam = string.Format("Params={0};", deviceID);
|
||||||
|
dlnaparam += isStatic ? "true;" : "false;";
|
||||||
dlnaparam += videoCodec.HasValue ? videoCodec.Value + ";" : ";";
|
dlnaparam += videoCodec.HasValue ? videoCodec.Value + ";" : ";";
|
||||||
dlnaparam += audioCodec.HasValue ? audioCodec.Value + ";" : ";";
|
dlnaparam += audioCodec.HasValue ? audioCodec.Value + ";" : ";";
|
||||||
dlnaparam += audiostreamIndex.HasValue ? audiostreamIndex.Value.ToString(usCulture) + ";" : ";";
|
dlnaparam += audiostreamIndex.HasValue ? audiostreamIndex.Value.ToString(usCulture) + ";" : ";";
|
||||||
|
@ -178,6 +181,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
dlnaparam += startPositionTicks.HasValue ? startPositionTicks.Value.ToString(usCulture) + ";" : ";";
|
dlnaparam += startPositionTicks.HasValue ? startPositionTicks.Value.ToString(usCulture) + ";" : ";";
|
||||||
dlnaparam += profile + ";";
|
dlnaparam += profile + ";";
|
||||||
dlnaparam += videoLevel + ";";
|
dlnaparam += videoLevel + ";";
|
||||||
|
dlnaparam += mimeType + ";";
|
||||||
|
|
||||||
return dlnaparam;
|
return dlnaparam;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
|
<Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
|
||||||
<Link>Configuration\BaseApplicationConfiguration.cs</Link>
|
<Link>Configuration\BaseApplicationConfiguration.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Configuration\DlnaOptions.cs">
|
||||||
|
<Link>Configuration\DlnaOptions.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Configuration\ManualLoginCategory.cs">
|
<Compile Include="..\MediaBrowser.Model\Configuration\ManualLoginCategory.cs">
|
||||||
<Link>Configuration\ManualLoginCategory.cs</Link>
|
<Link>Configuration\ManualLoginCategory.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -131,6 +134,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
|
<Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
|
||||||
<Link>Dto\ItemIndex.cs</Link>
|
<Link>Dto\ItemIndex.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Dto\RecommendationDto.cs">
|
||||||
|
<Link>Dto\RecommendationDto.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Dto\StreamOptions.cs">
|
<Compile Include="..\MediaBrowser.Model\Dto\StreamOptions.cs">
|
||||||
<Link>Dto\StreamOptions.cs</Link>
|
<Link>Dto\StreamOptions.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
@ -67,6 +67,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
|
<Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
|
||||||
<Link>Configuration\BaseApplicationConfiguration.cs</Link>
|
<Link>Configuration\BaseApplicationConfiguration.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Configuration\DlnaOptions.cs">
|
||||||
|
<Link>Configuration\DlnaOptions.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Configuration\ManualLoginCategory.cs">
|
<Compile Include="..\MediaBrowser.Model\Configuration\ManualLoginCategory.cs">
|
||||||
<Link>Configuration\ManualLoginCategory.cs</Link>
|
<Link>Configuration\ManualLoginCategory.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -118,6 +121,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
|
<Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
|
||||||
<Link>Dto\ItemIndex.cs</Link>
|
<Link>Dto\ItemIndex.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Dto\RecommendationDto.cs">
|
||||||
|
<Link>Dto\RecommendationDto.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Dto\StreamOptions.cs">
|
<Compile Include="..\MediaBrowser.Model\Dto\StreamOptions.cs">
|
||||||
<Link>Dto\StreamOptions.cs</Link>
|
<Link>Dto\StreamOptions.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
@ -244,7 +244,7 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// <param name="query">The query.</param>
|
/// <param name="query">The query.</param>
|
||||||
/// <returns>Task{ItemsResult}.</returns>
|
/// <returns>Task{ItemsResult}.</returns>
|
||||||
Task<ItemsResult> GetSeasonsAsync(SeasonQuery query);
|
Task<ItemsResult> GetSeasonsAsync(SeasonQuery query);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queries for items
|
/// Queries for items
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -346,7 +346,14 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="query">The query.</param>
|
/// <param name="query">The query.</param>
|
||||||
/// <returns>Task{ItemsResult}.</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Gets a genre
|
/// Gets a genre
|
||||||
|
@ -772,7 +779,7 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// <param name="options">The options.</param>
|
/// <param name="options">The options.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
string GetImageUrl(ProgramInfoDto item, ImageOptions options);
|
string GetImageUrl(ProgramInfoDto item, ImageOptions options);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an image url that can be used to download an image from the api
|
/// Gets an image url that can be used to download an image from the api
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -902,7 +909,7 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// <param name="options">The options.</param>
|
/// <param name="options">The options.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
string GetThumbImageUrl(BaseItemDto item, ImageOptions options);
|
string GetThumbImageUrl(BaseItemDto item, ImageOptions options);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the url needed to stream an audio file
|
/// Gets the url needed to stream an audio file
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -958,7 +965,7 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{ChannelInfoDto}.</returns>
|
/// <returns>Task{ChannelInfoDto}.</returns>
|
||||||
Task<ChannelInfoDto> GetLiveTvChannelAsync(string id, string userId, CancellationToken cancellationToken);
|
Task<ChannelInfoDto> GetLiveTvChannelAsync(string id, string userId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the live tv recordings asynchronous.
|
/// Gets the live tv recordings asynchronous.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -975,7 +982,7 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{RecordingInfoDto}.</returns>
|
/// <returns>Task{RecordingInfoDto}.</returns>
|
||||||
Task<RecordingInfoDto> GetLiveTvRecordingAsync(string id, string userId, CancellationToken cancellationToken);
|
Task<RecordingInfoDto> GetLiveTvRecordingAsync(string id, string userId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the live tv recording groups asynchronous.
|
/// Gets the live tv recording groups asynchronous.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -992,7 +999,7 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{RecordingGroupDto}.</returns>
|
/// <returns>Task{RecordingGroupDto}.</returns>
|
||||||
Task<RecordingGroupDto> GetLiveTvRecordingGroupAsync(string id, string userId, CancellationToken cancellationToken);
|
Task<RecordingGroupDto> GetLiveTvRecordingGroupAsync(string id, string userId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the live tv timers asynchronous.
|
/// Gets the live tv timers asynchronous.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1009,6 +1016,15 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
|
/// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
|
||||||
Task<QueryResult<ProgramInfoDto>> GetLiveTvProgramsAsync(ProgramQuery query, CancellationToken cancellationToken);
|
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>
|
/// <summary>
|
||||||
/// Gets the recommended live tv programs asynchronous.
|
/// Gets the recommended live tv programs asynchronous.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1016,7 +1032,39 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
|
/// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
|
||||||
Task<QueryResult<ProgramInfoDto>> GetRecommendedLiveTvProgramsAsync(RecommendedProgramQuery query, CancellationToken cancellationToken);
|
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>
|
/// <summary>
|
||||||
/// Gets the live tv timer asynchronous.
|
/// Gets the live tv timer asynchronous.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1024,7 +1072,7 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{TimerInfoDto}.</returns>
|
/// <returns>Task{TimerInfoDto}.</returns>
|
||||||
Task<TimerInfoDto> GetLiveTvTimerAsync(string id, CancellationToken cancellationToken);
|
Task<TimerInfoDto> GetLiveTvTimerAsync(string id, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the live tv series timers asynchronous.
|
/// Gets the live tv series timers asynchronous.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1056,7 +1104,7 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task CancelLiveTvSeriesTimerAsync(string id, CancellationToken cancellationToken);
|
Task CancelLiveTvSeriesTimerAsync(string id, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes the live tv recording asynchronous.
|
/// Deletes the live tv recording asynchronous.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1064,5 +1112,27 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task DeleteLiveTvRecordingAsync(string id, CancellationToken cancellationToken);
|
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 DeleteEmptyFolders { get; set; }
|
||||||
|
|
||||||
|
public bool CopyOriginalFile { get; set; }
|
||||||
|
|
||||||
public TvFileOrganizationOptions()
|
public TvFileOrganizationOptions()
|
||||||
{
|
{
|
||||||
MinFileSizeMb = 50;
|
MinFileSizeMb = 50;
|
||||||
|
@ -31,6 +33,8 @@ namespace MediaBrowser.Model.Configuration
|
||||||
MultiEpisodeNamePattern = "%sn - %sx%0e-x%0ed - %en.%ext";
|
MultiEpisodeNamePattern = "%sn - %sx%0e-x%0ed - %en.%ext";
|
||||||
SeasonFolderPattern = "Season %s";
|
SeasonFolderPattern = "Season %s";
|
||||||
SeasonZeroFolderName = "Season 0";
|
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 ServerName { get; set; }
|
||||||
public string WanDdns { get; set; }
|
public string WanDdns { get; set; }
|
||||||
|
|
||||||
|
public DlnaOptions DlnaOptions { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
|
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -271,6 +273,8 @@ namespace MediaBrowser.Model.Configuration
|
||||||
};
|
};
|
||||||
|
|
||||||
MetadataOptions = options.ToArray();
|
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>
|
/// <value>The primary image tag.</value>
|
||||||
public Guid? PrimaryImageTag { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this instance has primary image.
|
/// Gets a value indicating whether this instance has primary image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
<Compile Include="ApiClient\ServerEventArgs.cs" />
|
<Compile Include="ApiClient\ServerEventArgs.cs" />
|
||||||
<Compile Include="Configuration\AutoOrganize.cs" />
|
<Compile Include="Configuration\AutoOrganize.cs" />
|
||||||
<Compile Include="Configuration\BaseApplicationConfiguration.cs" />
|
<Compile Include="Configuration\BaseApplicationConfiguration.cs" />
|
||||||
|
<Compile Include="Configuration\DlnaOptions.cs" />
|
||||||
<Compile Include="Configuration\ManualLoginCategory.cs" />
|
<Compile Include="Configuration\ManualLoginCategory.cs" />
|
||||||
<Compile Include="Configuration\MetadataPlugin.cs" />
|
<Compile Include="Configuration\MetadataPlugin.cs" />
|
||||||
<Compile Include="Configuration\MetadataOptions.cs" />
|
<Compile Include="Configuration\MetadataOptions.cs" />
|
||||||
|
@ -73,6 +74,7 @@
|
||||||
<Compile Include="Dto\ItemByNameCounts.cs" />
|
<Compile Include="Dto\ItemByNameCounts.cs" />
|
||||||
<Compile Include="Dto\ItemCounts.cs" />
|
<Compile Include="Dto\ItemCounts.cs" />
|
||||||
<Compile Include="Dto\ItemIndex.cs" />
|
<Compile Include="Dto\ItemIndex.cs" />
|
||||||
|
<Compile Include="Dto\RecommendationDto.cs" />
|
||||||
<Compile Include="Entities\PackageReviewInfo.cs" />
|
<Compile Include="Entities\PackageReviewInfo.cs" />
|
||||||
<Compile Include="FileOrganization\FileOrganizationResult.cs" />
|
<Compile Include="FileOrganization\FileOrganizationResult.cs" />
|
||||||
<Compile Include="FileOrganization\FileOrganizationQuery.cs" />
|
<Compile Include="FileOrganization\FileOrganizationQuery.cs" />
|
||||||
|
|
|
@ -218,6 +218,18 @@ namespace MediaBrowser.Model.Querying
|
||||||
/// <value>The name starts with or greater.</value>
|
/// <value>The name starts with or greater.</value>
|
||||||
public string NameStartsWithOrGreater { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets or sets the album artist starts with or greater.
|
/// Gets or sets the album artist starts with or greater.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -75,16 +75,12 @@ namespace MediaBrowser.Model.Querying
|
||||||
public const string IsFolder = "IsFolder";
|
public const string IsFolder = "IsFolder";
|
||||||
public const string IsUnplayed = "IsUnplayed";
|
public const string IsUnplayed = "IsUnplayed";
|
||||||
public const string IsPlayed = "IsPlayed";
|
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 SeriesSortName = "SeriesSortName";
|
||||||
public const string VideoBitRate = "VideoBitRate";
|
public const string VideoBitRate = "VideoBitRate";
|
||||||
public const string AirTime = "AirTime";
|
public const string AirTime = "AirTime";
|
||||||
public const string Metascore = "Metascore";
|
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; }
|
public string NameStartsWithOrGreater { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <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.
|
/// Gets or sets the name less than.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The name less than.</value>
|
/// <value>The name less than.</value>
|
||||||
|
|
|
@ -33,4 +33,32 @@ namespace MediaBrowser.Model.Querying
|
||||||
/// <value>The fields.</value>
|
/// <value>The fields.</value>
|
||||||
public ItemFields[] Fields { get; set; }
|
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 IncludeStudios { get; set; }
|
||||||
public bool IncludeArtists { get; set; }
|
public bool IncludeArtists { get; set; }
|
||||||
|
|
||||||
|
public string[] IncludeItemTypes { get; set; }
|
||||||
|
|
||||||
public SearchQuery()
|
public SearchQuery()
|
||||||
{
|
{
|
||||||
IncludeArtists = true;
|
IncludeArtists = true;
|
||||||
|
@ -40,6 +42,8 @@ namespace MediaBrowser.Model.Search
|
||||||
IncludeMedia = true;
|
IncludeMedia = true;
|
||||||
IncludePeople = true;
|
IncludePeople = true;
|
||||||
IncludeStudios = true;
|
IncludeStudios = true;
|
||||||
|
|
||||||
|
IncludeItemTypes = new string[] { };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace MediaBrowser.Model.Session
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The name of the item.</value>
|
/// <value>The name of the item.</value>
|
||||||
public string ItemName { get; set; }
|
public string ItemName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the context (Movies, Music, Tv, etc)
|
/// 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.
|
/// 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 TvShows = "TvShows";
|
||||||
public const string Games = "Games";
|
public const string Games = "Games";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,9 +4,9 @@ namespace MediaBrowser.Model.Session
|
||||||
public class MessageCommand
|
public class MessageCommand
|
||||||
{
|
{
|
||||||
public string Header { get; set; }
|
public string Header { get; set; }
|
||||||
|
|
||||||
public string Text { get; set; }
|
public string Text { get; set; }
|
||||||
|
|
||||||
public long? TimeoutMs { get; set; }
|
public long? TimeoutMs { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -43,4 +43,4 @@ namespace MediaBrowser.Model.Session
|
||||||
/// </summary>
|
/// </summary>
|
||||||
PlayLast
|
PlayLast
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -38,4 +38,4 @@ namespace MediaBrowser.Model.Session
|
||||||
|
|
||||||
public long? SeekPositionTicks { get; set; }
|
public long? SeekPositionTicks { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -23,7 +23,7 @@ namespace MediaBrowser.Providers.AdultVideos
|
||||||
new MovieXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
|
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);
|
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, "background", "background-", ImageType.Backdrop);
|
||||||
PopulateBackdrops(images, files, imagePrefix, "art", "art-", 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));
|
.FirstOrDefault(i => string.Equals(i.Name, "extrafanart", StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
if (extraFanartFolder != null)
|
if (extraFanartFolder != null)
|
||||||
|
|
|
@ -59,7 +59,7 @@ namespace MediaBrowser.Providers
|
||||||
FileSystem = fileSystem;
|
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)
|
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Movies;
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
using MediaBrowser.Controller.Library;
|
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)
|
protected override void MergeData(BoxSet source, BoxSet target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
|
||||||
{
|
{
|
||||||
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, 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)
|
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)
|
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"));
|
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);
|
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"));
|
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);
|
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"));
|
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);
|
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);
|
var fileInfo = FileSystem.GetFileSystemInfo(info.Path);
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.LiveTv
|
||||||
new BaseItemXmlParser<LiveTvChannel>(_logger).Fetch(result.Item, path, cancellationToken);
|
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"));
|
return directoryService.GetFile(Path.Combine(info.Path, "channel.xml"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -305,22 +305,20 @@ namespace MediaBrowser.Providers.Manager
|
||||||
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.ImageUpdate;
|
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);
|
localItem.Item.Name = item.Name ?? Path.GetFileNameWithoutExtension(item.Path);
|
||||||
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;
|
|
||||||
|
|
||||||
// Only one local provider allowed per item
|
|
||||||
hasLocalMetadata = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Error("Invalid local metadata found for: " + item.Path);
|
MergeData(localItem.Item, temp, new List<MetadataFields>(), !options.ReplaceAllMetadata, true);
|
||||||
}
|
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;
|
||||||
else
|
|
||||||
{
|
// Only one local provider allowed per item
|
||||||
Logger.Debug("{0} returned no metadata for {1}", providerName, item.Path ?? item.Name);
|
hasLocalMetadata = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.Debug("{0} returned no metadata for {1}", providerName, item.Path ?? item.Name);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
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