Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Tim Hobbs 2014-03-13 06:26:20 -07:00
commit 9e33966ee1
160 changed files with 2743 additions and 1684 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

@ -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.")]

View File

@ -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")]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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
}
}

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

View 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
{
}
}

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,8 @@

namespace MediaBrowser.Model.Configuration
{
public class DlnaOptions
{
public bool EnablePlayTo { get; set; }
}
}

View File

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

View 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
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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[] { };
} }
} }
} }

View File

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

View File

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

View File

@ -43,4 +43,4 @@ namespace MediaBrowser.Model.Session
/// </summary> /// </summary>
PlayLast PlayLast
} }
} }

View File

@ -38,4 +38,4 @@ namespace MediaBrowser.Model.Session
public long? SeekPositionTicks { get; set; } public long? SeekPositionTicks { get; set; }
} }
} }

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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