Merge remote-tracking branch 'upstream/master'

pull/702/head
Tim Hobbs 11 years ago
commit 9e33966ee1

@ -164,7 +164,10 @@ namespace MediaBrowser.Api
return name;
}
return libraryManager.GetAllArtists()
return libraryManager.RootFolder.RecursiveChildren
.OfType<Audio>()
.SelectMany(i => i.AllArtists)
.Distinct(StringComparer.OrdinalIgnoreCase)
.FirstOrDefault(i =>
{
i = _dashReplaceChars.Aggregate(i, (current, c) => current.Replace(c, SlugChar));

@ -194,8 +194,10 @@ namespace MediaBrowser.Api.DefaultTheme
.Select(i => _dtoService.GetBaseItemDto(i, fields, user))
.ToList();
var artists = _libraryManager.GetAllArtists(allItems)
.Randomize()
var artists = allItems.OfType<Audio>()
.SelectMany(i => i.AllArtists)
.Distinct(StringComparer.OrdinalIgnoreCase)
.Randomize()
.Select(i =>
{
try

@ -155,7 +155,7 @@ namespace MediaBrowser.Api
var games = items.OfType<Game>().ToList();
summary.ClientInstalledGameCount = games.Count(i => !i.IsPlaceHolder);
summary.ClientInstalledGameCount = games.Count(i => i.IsPlaceHolder);
summary.GameCount = games.Count;

@ -98,7 +98,7 @@ namespace MediaBrowser.Api
UpdateItem(request, item);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
if (dontFetchMetaChanged && item.IsFolder)
{
@ -107,7 +107,7 @@ namespace MediaBrowser.Api
foreach (var child in folder.RecursiveChildren.ToList())
{
child.DontFetchMeta = newLockData;
await _libraryManager.UpdateItem(child, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
await child.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
}
}
@ -125,7 +125,7 @@ namespace MediaBrowser.Api
UpdateItem(request, item);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
public void Post(UpdateArtist request)
@ -141,7 +141,7 @@ namespace MediaBrowser.Api
UpdateItem(request, item);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
public void Post(UpdateStudio request)
@ -157,7 +157,7 @@ namespace MediaBrowser.Api
UpdateItem(request, item);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
public void Post(UpdateMusicGenre request)
@ -173,7 +173,7 @@ namespace MediaBrowser.Api
UpdateItem(request, item);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
public void Post(UpdateGameGenre request)
@ -189,7 +189,7 @@ namespace MediaBrowser.Api
UpdateItem(request, item);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
public void Post(UpdateGenre request)
@ -205,7 +205,7 @@ namespace MediaBrowser.Api
UpdateItem(request, item);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
private void UpdateItem(BaseItemDto request, BaseItem item)

@ -66,7 +66,8 @@
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
<Compile Include="AlbumsService.cs" />
<Compile Include="Movies\CollectionService.cs" />
<Compile Include="Music\AlbumsService.cs" />
<Compile Include="AppThemeService.cs" />
<Compile Include="BaseApiService.cs" />
<Compile Include="ConfigurationService.cs" />
@ -81,7 +82,7 @@
<Compile Include="Images\ImageRequest.cs" />
<Compile Include="Images\ImageService.cs" />
<Compile Include="Images\ImageWriter.cs" />
<Compile Include="InstantMixService.cs" />
<Compile Include="Music\InstantMixService.cs" />
<Compile Include="ItemLookupService.cs" />
<Compile Include="ItemRefreshService.cs" />
<Compile Include="ItemUpdateService.cs" />
@ -91,7 +92,7 @@
<Compile Include="Library\LibraryStructureService.cs" />
<Compile Include="LiveTv\LiveTvService.cs" />
<Compile Include="LocalizationService.cs" />
<Compile Include="MoviesService.cs" />
<Compile Include="Movies\MoviesService.cs" />
<Compile Include="NewsService.cs" />
<Compile Include="NotificationsService.cs" />
<Compile Include="PackageReviewService.cs" />
@ -118,7 +119,7 @@
<Compile Include="SessionsService.cs" />
<Compile Include="SimilarItemsHelper.cs" />
<Compile Include="SystemService.cs" />
<Compile Include="TrailersService.cs" />
<Compile Include="Movies\TrailersService.cs" />
<Compile Include="TvShowsService.cs" />
<Compile Include="UserLibrary\ArtistsService.cs" />
<Compile Include="UserLibrary\BaseItemsByNameService.cs" />

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

@ -0,0 +1,324 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MediaBrowser.Api.Movies
{
/// <summary>
/// Class GetSimilarMovies
/// </summary>
[Route("/Movies/{Id}/Similar", "GET")]
[Api(Description = "Finds movies and trailers similar to a given movie.")]
public class GetSimilarMovies : BaseGetSimilarItemsFromItem
{
[ApiMember(Name = "IncludeTrailers", Description = "Whether or not to include trailers within the results. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool IncludeTrailers { get; set; }
public GetSimilarMovies()
{
IncludeTrailers = true;
}
}
[Route("/Movies/Recommendations", "GET")]
[Api(Description = "Gets movie recommendations")]
public class GetMovieRecommendations : IReturn<RecommendationDto[]>, IHasItemFields
{
[ApiMember(Name = "CategoryLimit", Description = "The max number of categories to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int CategoryLimit { get; set; }
[ApiMember(Name = "ItemLimit", Description = "The max number of items to return per category", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int ItemLimit { get; set; }
/// <summary>
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public Guid? UserId { get; set; }
public GetMovieRecommendations()
{
CategoryLimit = 5;
ItemLimit = 8;
}
public string Fields { get; set; }
}
/// <summary>
/// Class MoviesService
/// </summary>
public class MoviesService : BaseApiService
{
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// The _user data repository
/// </summary>
private readonly IUserDataManager _userDataRepository;
/// <summary>
/// The _library manager
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo;
private readonly IDtoService _dtoService;
/// <summary>
/// Initializes a new instance of the <see cref="MoviesService"/> class.
/// </summary>
/// <param name="userManager">The user manager.</param>
/// <param name="userDataRepository">The user data repository.</param>
/// <param name="libraryManager">The library manager.</param>
public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService)
{
_userManager = userManager;
_userDataRepository = userDataRepository;
_libraryManager = libraryManager;
_itemRepo = itemRepo;
_dtoService = dtoService;
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetSimilarMovies request)
{
var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager,
_itemRepo,
_libraryManager,
_userDataRepository,
_dtoService,
Logger,
request, item => item is Movie || (item is Trailer && request.IncludeTrailers),
SimilarItemsHelper.GetSimiliarityScore);
return ToOptimizedSerializedResultUsingCache(result);
}
public object Get(GetMovieRecommendations request)
{
var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
var folder = user.RootFolder;
var movies = folder.RecursiveChildren.OfType<Movie>().ToList();
var result = GetRecommendationCategories(user, movies, request.CategoryLimit, request.ItemLimit, request.GetItemFields().ToList());
return ToOptimizedResult(result);
}
private IEnumerable<RecommendationDto> GetRecommendationCategories(User user, List<Movie> allMovies, int categoryLimit, int itemLimit, List<ItemFields> fields)
{
var categories = new List<RecommendationDto>();
var recentlyPlayedMovies = allMovies
.Select(i =>
{
var userdata = _userDataRepository.GetUserData(user.Id, i.GetUserDataKey());
return new Tuple<Movie, bool, DateTime>(i, userdata.Played, userdata.LastPlayedDate ?? DateTime.MinValue);
})
.Where(i => i.Item2)
.OrderByDescending(i => i.Item3)
.Select(i => i.Item1)
.ToList();
var excludeFromLiked = recentlyPlayedMovies.Take(10);
var likedMovies = allMovies
.Select(i =>
{
var score = 0;
var userData = _userDataRepository.GetUserData(user.Id, i.GetUserDataKey());
if (userData.IsFavorite)
{
score = 2;
}
else
{
score = userData.Likes.HasValue ? userData.Likes.Value ? 1 : -1 : 0;
}
return new Tuple<Movie, int>(i, score);
})
.OrderByDescending(i => i.Item2)
.ThenBy(i => Guid.NewGuid())
.Where(i => i.Item2 > 0)
.Select(i => i.Item1)
.Where(i => !excludeFromLiked.Contains(i));
var mostRecentMovies = recentlyPlayedMovies.Take(6).ToList();
// Get recently played directors
var recentDirectors = GetDirectors(mostRecentMovies)
.OrderBy(i => Guid.NewGuid())
.ToList();
// Get recently played actors
var recentActors = GetActors(mostRecentMovies)
.OrderBy(i => Guid.NewGuid())
.ToList();
var similarToRecentlyPlayed = GetSimilarTo(user, allMovies, recentlyPlayedMovies.Take(7).OrderBy(i => Guid.NewGuid()), itemLimit, fields, RecommendationType.SimilarToRecentlyPlayed).GetEnumerator();
var similarToLiked = GetSimilarTo(user, allMovies, likedMovies, itemLimit, fields, RecommendationType.SimilarToLikedItem).GetEnumerator();
var hasDirectorFromRecentlyPlayed = GetWithDirector(user, allMovies, recentDirectors, itemLimit, fields, RecommendationType.HasDirectorFromRecentlyPlayed).GetEnumerator();
var hasActorFromRecentlyPlayed = GetWithActor(user, allMovies, recentActors, itemLimit, fields, RecommendationType.HasActorFromRecentlyPlayed).GetEnumerator();
var categoryTypes = new List<IEnumerator<RecommendationDto>>
{
// Give this extra weight
similarToRecentlyPlayed,
similarToRecentlyPlayed,
// Give this extra weight
similarToLiked,
similarToLiked,
hasDirectorFromRecentlyPlayed,
hasActorFromRecentlyPlayed
};
while (categories.Count < categoryLimit)
{
var allEmpty = true;
foreach (var category in categoryTypes)
{
if (category.MoveNext())
{
categories.Add(category.Current);
allEmpty = false;
if (categories.Count >= categoryLimit)
{
break;
}
}
}
if (allEmpty)
{
break;
}
}
//// Get the lead actor for all movies
//var allActors = GetActors(allMovies)
// .ToList();
//foreach (var actor in recentActors)
//{
//}
return categories.OrderBy(i => i.RecommendationType).ThenBy(i => Guid.NewGuid());
}
private IEnumerable<RecommendationDto> GetWithDirector(User user, List<Movie> allMovies, IEnumerable<string> directors, int itemLimit, List<ItemFields> fields, RecommendationType type)
{
var userId = user.Id;
foreach (var director in directors)
{
var items = allMovies
.Where(i => !_userDataRepository.GetUserData(userId, i.GetUserDataKey()).Played && i.People.Any(p => string.Equals(p.Type, PersonType.Director, StringComparison.OrdinalIgnoreCase) && string.Equals(p.Name, director, StringComparison.OrdinalIgnoreCase)))
.Take(itemLimit)
.ToList();
if (items.Count > 0)
{
yield return new RecommendationDto
{
BaselineItemName = director,
CategoryId = director.GetMD5().ToString("N"),
RecommendationType = type,
Items = items.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray()
};
}
}
}
private IEnumerable<RecommendationDto> GetWithActor(User user, List<Movie> allMovies, IEnumerable<string> names, int itemLimit, List<ItemFields> fields, RecommendationType type)
{
var userId = user.Id;
foreach (var name in names)
{
var items = allMovies
.Where(i => !_userDataRepository.GetUserData(userId, i.GetUserDataKey()).Played && i.People.Any(p => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase)))
.Take(itemLimit)
.ToList();
if (items.Count > 0)
{
yield return new RecommendationDto
{
BaselineItemName = name,
CategoryId = name.GetMD5().ToString("N"),
RecommendationType = type,
Items = items.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray()
};
}
}
}
private IEnumerable<RecommendationDto> GetSimilarTo(User user, List<Movie> allMovies, IEnumerable<Movie> baselineItems, int itemLimit, List<ItemFields> fields, RecommendationType type)
{
var userId = user.Id;
foreach (var item in baselineItems)
{
var similar = SimilarItemsHelper
.GetSimilaritems(item, allMovies, SimilarItemsHelper.GetSimiliarityScore)
.Where(i => !_userDataRepository.GetUserData(userId, i.GetUserDataKey()).Played)
.Take(itemLimit)
.ToList();
if (similar.Count > 0)
{
yield return new RecommendationDto
{
BaselineItemName = item.Name,
CategoryId = item.Id.ToString("N"),
RecommendationType = type,
Items = similar.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray()
};
}
}
}
private IEnumerable<string> GetActors(IEnumerable<BaseItem> items)
{
// Get the two leading actors for all movies
return items
.SelectMany(i => i.People.Where(p => !string.Equals(PersonType.Director, p.Type, StringComparison.OrdinalIgnoreCase)).Take(2))
.Select(i => i.Name)
.Distinct(StringComparer.OrdinalIgnoreCase);
}
private IEnumerable<string> GetDirectors(IEnumerable<BaseItem> items)
{
return items
.Select(i => i.People.FirstOrDefault(p => string.Equals(PersonType.Director, p.Type, StringComparison.OrdinalIgnoreCase)))
.Where(i => i != null)
.Select(i => i.Name)
.Distinct(StringComparer.OrdinalIgnoreCase);
}
}
}

@ -5,7 +5,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using ServiceStack;
namespace MediaBrowser.Api
namespace MediaBrowser.Api.Movies
{
/// <summary>
/// Class GetSimilarTrailers

@ -1,82 +0,0 @@
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using ServiceStack;
namespace MediaBrowser.Api
{
/// <summary>
/// Class GetSimilarMovies
/// </summary>
[Route("/Movies/{Id}/Similar", "GET")]
[Api(Description = "Finds movies and trailers similar to a given movie.")]
public class GetSimilarMovies : BaseGetSimilarItemsFromItem
{
[ApiMember(Name = "IncludeTrailers", Description = "Whether or not to include trailers within the results. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool IncludeTrailers { get; set; }
public GetSimilarMovies()
{
IncludeTrailers = true;
}
}
/// <summary>
/// Class MoviesService
/// </summary>
public class MoviesService : BaseApiService
{
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// The _user data repository
/// </summary>
private readonly IUserDataManager _userDataRepository;
/// <summary>
/// The _library manager
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo;
private readonly IDtoService _dtoService;
/// <summary>
/// Initializes a new instance of the <see cref="MoviesService"/> class.
/// </summary>
/// <param name="userManager">The user manager.</param>
/// <param name="userDataRepository">The user data repository.</param>
/// <param name="libraryManager">The library manager.</param>
public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService)
{
_userManager = userManager;
_userDataRepository = userDataRepository;
_libraryManager = libraryManager;
_itemRepo = itemRepo;
_dtoService = dtoService;
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetSimilarMovies request)
{
var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager,
_itemRepo,
_libraryManager,
_userDataRepository,
_dtoService,
Logger,
request, item => item is Movie || (item is Trailer && request.IncludeTrailers),
SimilarItemsHelper.GetSimiliarityScore);
return ToOptimizedSerializedResultUsingCache(result);
}
}
}

@ -8,7 +8,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace MediaBrowser.Api
namespace MediaBrowser.Api.Music
{
[Route("/Albums/{Id}/Similar", "GET")]
[Api(Description = "Finds albums similar to a given album.")]

@ -7,7 +7,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace MediaBrowser.Api
namespace MediaBrowser.Api.Music
{
[Route("/Songs/{Id}/InstantMix", "GET")]
[Api(Description = "Creates an instant playlist based on a given song")]

@ -279,8 +279,19 @@ namespace MediaBrowser.Api.Playback
/// </summary>
/// <returns>System.Int32.</returns>
/// <exception cref="System.Exception">Unrecognized MediaEncodingQuality value.</exception>
protected int GetNumberOfThreads(bool isWebm)
protected int GetNumberOfThreads(StreamState state, bool isWebm)
{
// Use more when this is true. -re will keep cpu usage under control
if (state.ReadInputAtNativeFramerate)
{
if (isWebm)
{
return Math.Max(Environment.ProcessorCount - 1, 1);
}
return 0;
}
// Webm: http://www.webmproject.org/docs/encoder-parameters/
// The decoder will usually automatically use an appropriate number of threads according to how many cores are available but it can only use multiple threads
// for the coefficient data if the encoder selected --token-parts > 0 at encode time.
@ -491,16 +502,16 @@ namespace MediaBrowser.Api.Playback
return string.Format("{4} -vf \"{0}scale=trunc({1}/2)*2:trunc({2}/2)*2{3}\"", yadifParam, widthParam, heightParam, assSubtitleParam, copyTsParam);
}
// If Max dimensions were supplied
//this makes my brain hurt. For width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
{
var MaxwidthParam = request.MaxWidth.Value.ToString(UsCulture);
var MaxheightParam = request.MaxHeight.Value.ToString(UsCulture);
return string.Format("{4} -vf \"{0}scale=trunc(min(iw\\,{1})/2)*2:trunc(min((iw/dar)\\,{2})/2)*2{3}\"", yadifParam, MaxwidthParam, MaxheightParam, assSubtitleParam, copyTsParam);
}
// If Max dimensions were supplied
//this makes my brain hurt. For width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
{
var MaxwidthParam = request.MaxWidth.Value.ToString(UsCulture);
var MaxheightParam = request.MaxHeight.Value.ToString(UsCulture);
return string.Format("{4} -vf \"{0}scale=trunc(min(iw\\,{1})/2)*2:trunc(min((iw/dar)\\,{2})/2)*2{3}\"", yadifParam, MaxwidthParam, MaxheightParam, assSubtitleParam, copyTsParam);
}
var isH264Output = outputVideoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase);
@ -603,7 +614,7 @@ namespace MediaBrowser.Api.Playback
private string GetExtractedAssPath(StreamState state, bool performConversion)
{
var path = EncodingManager.GetSubtitleCachePath(state.MediaPath, state.SubtitleStream.Index, ".ass");
if (performConversion)
{
InputType type;
@ -987,20 +998,15 @@ namespace MediaBrowser.Api.Playback
if (state.VideoStream != null)
{
var isUpscaling = false;
if (state.VideoRequest.Height.HasValue && state.VideoStream.Height.HasValue &&
state.VideoRequest.Height.Value > state.VideoStream.Height.Value)
{
isUpscaling = true;
}
var isUpscaling = state.VideoRequest.Height.HasValue && state.VideoStream.Height.HasValue &&
state.VideoRequest.Height.Value > state.VideoStream.Height.Value;
if (state.VideoRequest.Width.HasValue && state.VideoStream.Width.HasValue &&
state.VideoRequest.Width.Value > state.VideoStream.Width.Value)
{
isUpscaling = true;
}
// Don't allow bitrate increases unless upscaling
if (!isUpscaling)
{
@ -1198,66 +1204,74 @@ namespace MediaBrowser.Api.Playback
request.DeviceId = val;
}
else if (i == 1)
{
request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
}
else if (i == 2)
{
if (videoRequest != null)
{
videoRequest.VideoCodec = (VideoCodecs)Enum.Parse(typeof(VideoCodecs), val, true);
}
}
else if (i == 2)
else if (i == 3)
{
request.AudioCodec = (AudioCodecs)Enum.Parse(typeof(AudioCodecs), val, true);
}
else if (i == 3)
else if (i == 4)
{
if (videoRequest != null)
{
videoRequest.AudioStreamIndex = int.Parse(val, UsCulture);
}
}
else if (i == 4)
else if (i == 5)
{
if (videoRequest != null)
{
videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture);
}
}
else if (i == 5)
else if (i == 6)
{
if (videoRequest != null)
{
videoRequest.VideoBitRate = int.Parse(val, UsCulture);
}
}
else if (i == 6)
else if (i == 7)
{
request.AudioBitRate = int.Parse(val, UsCulture);
}
else if (i == 7)
else if (i == 8)
{
request.AudioChannels = int.Parse(val, UsCulture);
}
else if (i == 8)
else if (i == 9)
{
if (videoRequest != null)
{
request.StartTimeTicks = long.Parse(val, UsCulture);
}
}
else if (i == 9)
else if (i == 10)
{
if (videoRequest != null)
{
videoRequest.Profile = val;
}
}
else if (i == 10)
else if (i == 11)
{
if (videoRequest != null)
{
videoRequest.Level = val;
}
}
else if (i == 12)
{
request.ForcedMimeType = val;
}
}
}
@ -1309,37 +1323,39 @@ namespace MediaBrowser.Api.Playback
state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
state.PlayableStreamFileNames = new List<string>();
if (!string.IsNullOrEmpty(recording.RecordingInfo.Path) && File.Exists(recording.RecordingInfo.Path))
var path = recording.RecordingInfo.Path;
var mediaUrl = recording.RecordingInfo.Url;
if (string.IsNullOrWhiteSpace(path) && string.IsNullOrWhiteSpace(mediaUrl))
{
state.MediaPath = recording.RecordingInfo.Path;
var streamInfo = await LiveTvManager.GetRecordingStream(request.Id, cancellationToken).ConfigureAwait(false);
state.LiveTvStreamId = streamInfo.Id;
path = streamInfo.Path;
mediaUrl = streamInfo.Url;
}
if (!string.IsNullOrEmpty(path) && File.Exists(path))
{
state.MediaPath = path;
state.IsRemote = false;
state.SendInputOverStandardInput = recording.RecordingInfo.Status == RecordingStatus.InProgress;
}
else if (!string.IsNullOrEmpty(recording.RecordingInfo.Url))
else if (!string.IsNullOrEmpty(mediaUrl))
{
state.MediaPath = recording.RecordingInfo.Url;
state.MediaPath = mediaUrl;
state.IsRemote = true;
}
else
{
var streamInfo = await LiveTvManager.GetRecordingStream(request.Id, cancellationToken).ConfigureAwait(false);
state.LiveTvStreamId = streamInfo.Id;
if (!string.IsNullOrEmpty(streamInfo.Path) && File.Exists(streamInfo.Path))
{
state.MediaPath = streamInfo.Path;
state.IsRemote = false;
}
else if (!string.IsNullOrEmpty(streamInfo.Url))
{
state.MediaPath = streamInfo.Url;
state.IsRemote = true;
}
//state.RunTimeTicks = recording.RunTimeTicks;
if (recording.RecordingInfo.Status == RecordingStatus.InProgress && !state.IsRemote)
{
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
}
//state.RunTimeTicks = recording.RunTimeTicks;
state.ReadInputAtNativeFramerate = recording.RecordingInfo.Status == RecordingStatus.InProgress;
state.SendInputOverStandardInput = recording.RecordingInfo.Status == RecordingStatus.InProgress;
state.AudioSync = "1000";
state.DeInterlace = true;
}
@ -1359,6 +1375,8 @@ namespace MediaBrowser.Api.Playback
{
state.MediaPath = streamInfo.Path;
state.IsRemote = false;
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
}
else if (!string.IsNullOrEmpty(streamInfo.Url))
{
@ -1366,7 +1384,6 @@ namespace MediaBrowser.Api.Playback
state.IsRemote = true;
}
state.SendInputOverStandardInput = true;
state.ReadInputAtNativeFramerate = true;
state.AudioSync = "1000";
state.DeInterlace = true;
@ -1411,6 +1428,11 @@ namespace MediaBrowser.Api.Playback
state.SubtitleStream = GetMediaStream(mediaStreams, videoRequest.SubtitleStreamIndex, MediaStreamType.Subtitle, false);
state.AudioStream = GetMediaStream(mediaStreams, videoRequest.AudioStreamIndex, MediaStreamType.Audio);
if (state.VideoStream != null && state.VideoStream.IsInterlaced)
{
state.DeInterlace = true;
}
EnforceResolutionLimit(state, videoRequest);
}
else
@ -1420,8 +1442,8 @@ namespace MediaBrowser.Api.Playback
state.HasMediaStreams = mediaStreams.Count > 0;
state.SegmentLength = state.ReadInputAtNativeFramerate ? 3 : 10;
state.HlsListSize = state.ReadInputAtNativeFramerate ? 20 : 1440;
state.SegmentLength = state.ReadInputAtNativeFramerate ? 5 : 10;
state.HlsListSize = state.ReadInputAtNativeFramerate ? 100 : 1440;
return state;
}

@ -278,7 +278,7 @@ namespace MediaBrowser.Api.Playback.Hls
var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds);
var threads = GetNumberOfThreads(false);
var threads = GetNumberOfThreads(state, false);
var inputModifier = GetInputModifier(state);

@ -102,7 +102,7 @@ namespace MediaBrowser.Api.Playback.Progressive
const string vn = " -vn";
var threads = GetNumberOfThreads(false);
var threads = GetNumberOfThreads(state, false);
var inputModifier = GetInputModifier(state);

@ -214,12 +214,16 @@ namespace MediaBrowser.Api.Playback.Progressive
if (request.Static)
{
return ResultFactory.GetStaticFileResult(Request, state.MediaPath, FileShare.Read, responseHeaders, isHeadRequest);
var contentType = state.GetMimeType(state.MediaPath);
return ResultFactory.GetStaticFileResult(Request, state.MediaPath, contentType, FileShare.Read, responseHeaders, isHeadRequest);
}
if (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive))
{
return ResultFactory.GetStaticFileResult(Request, outputPath, FileShare.Read, responseHeaders, isHeadRequest);
var contentType = state.GetMimeType(outputPath);
return ResultFactory.GetStaticFileResult(Request, outputPath, contentType, FileShare.Read, responseHeaders, isHeadRequest);
}
return GetStreamResult(state, responseHeaders, isHeadRequest).Result;
@ -287,7 +291,7 @@ namespace MediaBrowser.Api.Playback.Progressive
responseHeaders["Accept-Ranges"] = "none";
var contentType = MimeTypes.GetMimeType(outputPath);
var contentType = state.GetMimeType(outputPath);
// Headers only
if (isHeadRequest)

@ -102,7 +102,7 @@ namespace MediaBrowser.Api.Playback.Progressive
format = " -f mp4 -movflags frag_keyframe+empty_moov";
}
var threads = GetNumberOfThreads(string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase));
var threads = GetNumberOfThreads(state, string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase));
var inputModifier = GetInputModifier(state);

@ -66,6 +66,8 @@ namespace MediaBrowser.Api.Playback
public bool ThrowDebugError { get; set; }
public string Params { get; set; }
public string ForcedMimeType { get; set; }
}
public class VideoStreamRequest : StreamRequest

@ -1,4 +1,5 @@
using MediaBrowser.Model.Entities;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using System.Collections.Generic;
using System.IO;
@ -72,5 +73,20 @@ namespace MediaBrowser.Api.Playback
public string InputVideoCodec { get; set; }
public string InputAudioCodec { get; set; }
public string GetMimeType(string outputPath)
{
if (!string.IsNullOrWhiteSpace(Request.ForcedMimeType))
{
if (VideoRequest == null)
{
return "audio/" + Request.ForcedMimeType;
}
return "video/" + Request.ForcedMimeType;
}
return MimeTypes.GetMimeType(outputPath);
}
}
}

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Drawing;
using System;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@ -63,6 +64,9 @@ namespace MediaBrowser.Api
[ApiMember(Name = "IncludeArtists", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool IncludeArtists { get; set; }
[ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string IncludeItemTypes { get; set; }
public GetSearchHints()
{
IncludeArtists = true;
@ -130,7 +134,8 @@ namespace MediaBrowser.Api
IncludePeople = request.IncludePeople,
IncludeStudios = request.IncludeStudios,
StartIndex = request.StartIndex,
UserId = request.UserId
UserId = request.UserId,
IncludeItemTypes = (request.IncludeItemTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray()
}).ConfigureAwait(false);
@ -206,7 +211,8 @@ namespace MediaBrowser.Api
result.SongCount = songs.Count;
result.Artists = _libraryManager.GetAllArtists(songs)
result.Artists = songs.SelectMany(i => i.AllArtists)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
result.AlbumArtist = songs.Select(i => i.AlbumArtist).FirstOrDefault(i => !string.IsNullOrEmpty(i));

@ -211,7 +211,7 @@ namespace MediaBrowser.Api
[ApiMember(Name = "PlayableMediaTypes", Description = "A list of playable media types, comma delimited. Audio, Video, Book, Game, Photo.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string PlayableMediaTypes { get; set; }
}
/// <summary>
/// Class SessionsService
/// </summary>
@ -368,4 +368,4 @@ namespace MediaBrowser.Api
.ToList();
}
}
}
}

@ -73,7 +73,7 @@ namespace MediaBrowser.Api
var item = string.IsNullOrEmpty(request.Id) ?
(request.UserId.HasValue ? user.RootFolder :
(Folder)libraryManager.RootFolder) : dtoService.GetItemByDtoId(request.Id, request.UserId);
libraryManager.RootFolder) : dtoService.GetItemByDtoId(request.Id, request.UserId);
var fields = request.GetItemFields().ToList();
@ -81,7 +81,7 @@ namespace MediaBrowser.Api
? libraryManager.RootFolder.GetRecursiveChildren(i => i.Id != item.Id)
: user.RootFolder.GetRecursiveChildren(user, i => i.Id != item.Id);
var items = GetSimilaritems(item, inputItems, includeInSearch, getSimilarityScore)
var items = GetSimilaritems(item, inputItems.Where(includeInSearch), getSimilarityScore)
.ToList();
IEnumerable<BaseItem> returnItems = items;
@ -106,12 +106,12 @@ namespace MediaBrowser.Api
/// </summary>
/// <param name="item">The item.</param>
/// <param name="inputItems">The input items.</param>
/// <param name="includeInSearch">The include in search.</param>
/// <param name="getSimilarityScore">The get similarity score.</param>
/// <returns>IEnumerable{BaseItem}.</returns>
internal static IEnumerable<BaseItem> GetSimilaritems(BaseItem item, IEnumerable<BaseItem> inputItems, Func<BaseItem, bool> includeInSearch, Func<BaseItem, BaseItem, int> getSimilarityScore)
internal static IEnumerable<BaseItem> GetSimilaritems(BaseItem item, IEnumerable<BaseItem> inputItems, Func<BaseItem, BaseItem, int> getSimilarityScore)
{
inputItems = inputItems.Where(includeInSearch);
var itemId = item.Id;
inputItems = inputItems.Where(i => i.Id != itemId);
return inputItems.Select(i => new Tuple<BaseItem, int>(i, getSimilarityScore(item, i)))
.Where(i => i.Item2 > 2)
@ -153,7 +153,7 @@ namespace MediaBrowser.Api
if (!string.IsNullOrEmpty(item1.OfficialRating) && string.Equals(item1.OfficialRating, item2.OfficialRating, StringComparison.OrdinalIgnoreCase))
{
points += 1;
points += 10;
}
// Find common genres

@ -18,7 +18,7 @@ namespace MediaBrowser.Api
/// Class GetNextUpEpisodes
/// </summary>
[Route("/Shows/NextUp", "GET")]
[Api(("Gets a list of currently installed plugins"))]
[Api(("Gets a list of next up episodes"))]
public class GetNextUpEpisodes : IReturn<ItemsResult>, IHasItemFields
{
/// <summary>
@ -53,6 +53,39 @@ namespace MediaBrowser.Api
public string SeriesId { get; set; }
}
[Route("/Shows/Upcoming", "GET")]
[Api(("Gets a list of upcoming episodes"))]
public class GetUpcomingEpisodes : IReturn<ItemsResult>, IHasItemFields
{
/// <summary>
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public Guid UserId { get; set; }
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
/// </summary>
/// <value>The start index.</value>
[ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? StartIndex { get; set; }
/// <summary>
/// The maximum number of items to return
/// </summary>
/// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? Limit { get; set; }
/// <summary>
/// Fields to return within the items, in addition to basic information
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, OverviewHtml, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Fields { get; set; }
}
[Route("/Shows/{Id}/Similar", "GET")]
[Api(Description = "Finds tv shows similar to a given one.")]
public class GetSimilarShows : BaseGetSimilarItemsFromItem
@ -85,7 +118,7 @@ namespace MediaBrowser.Api
[ApiMember(Name = "SeasonId", Description = "Optional. Filter by season id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string SeasonId { get; set; }
[ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsMissing { get; set; }
@ -186,6 +219,39 @@ namespace MediaBrowser.Api
return ToOptimizedSerializedResultUsingCache(result);
}
public object Get(GetUpcomingEpisodes request)
{
var user = _userManager.GetUserById(request.UserId);
var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager)
.OfType<Episode>();
var itemsList = _libraryManager.Sort(items, user, new[] { "PremiereDate", "AirTime", "SortName" }, SortOrder.Ascending)
.Cast<Episode>()
.ToList();
var unairedEpisodes = itemsList.Where(i => i.IsUnaired).ToList();
var minPremiereDate = DateTime.Now.Date.AddDays(-1).ToUniversalTime();
var previousEpisodes = itemsList.Where(i => !i.IsUnaired && (i.PremiereDate ?? DateTime.MinValue) >= minPremiereDate).ToList();
previousEpisodes.AddRange(unairedEpisodes);
var pagedItems = ApplyPaging(previousEpisodes, request.StartIndex, request.Limit);
var fields = request.GetItemFields().ToList();
var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray();
var result = new ItemsResult
{
TotalRecordCount = itemsList.Count,
Items = returnItems
};
return ToOptimizedSerializedResultUsingCache(result);
}
/// <summary>
/// Gets the specified request.
/// </summary>
@ -198,7 +264,7 @@ namespace MediaBrowser.Api
var itemsList = GetNextUpEpisodes(request)
.ToList();
var pagedItems = ApplyPaging(request, itemsList);
var pagedItems = ApplyPaging(itemsList, request.StartIndex, request.Limit);
var fields = request.GetItemFields().ToList();
@ -234,11 +300,13 @@ namespace MediaBrowser.Api
return FilterSeries(request, series)
.AsParallel()
.Select(i => GetNextUp(i, currentUser, request).Item1)
.Where(i => i != null)
.Select(i => GetNextUp(i, currentUser))
.Where(i => i.Item1 != null)
.OrderByDescending(i =>
{
var seriesUserData = _userDataManager.GetUserData(user.Id, i.Series.GetUserDataKey());
var episode = i.Item1;
var seriesUserData = _userDataManager.GetUserData(user.Id, episode.Series.GetUserDataKey());
if (seriesUserData.IsFavorite)
{
@ -252,7 +320,9 @@ namespace MediaBrowser.Api
return 0;
})
.ThenByDescending(i => i.PremiereDate ?? DateTime.MinValue);
.ThenByDescending(i =>i.Item2)
.ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue)
.Select(i => i.Item1);
}
/// <summary>
@ -260,9 +330,8 @@ namespace MediaBrowser.Api
/// </summary>
/// <param name="series">The series.</param>
/// <param name="user">The user.</param>
/// <param name="request">The request.</param>
/// <returns>Task{Episode}.</returns>
private Tuple<Episode, DateTime> GetNextUp(Series series, User user, GetNextUpEpisodes request)
private Tuple<Episode, DateTime> GetNextUp(Series series, User user)
{
// Get them in display order, then reverse
var allEpisodes = series.GetSeasons(user, true, true)
@ -321,21 +390,22 @@ namespace MediaBrowser.Api
/// <summary>
/// Applies the paging.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="items">The items.</param>
/// <param name="startIndex">The start index.</param>
/// <param name="limit">The limit.</param>
/// <returns>IEnumerable{BaseItem}.</returns>
private IEnumerable<BaseItem> ApplyPaging(GetNextUpEpisodes request, IEnumerable<BaseItem> items)
private IEnumerable<BaseItem> ApplyPaging(IEnumerable<BaseItem> items, int? startIndex, int? limit)
{
// Start at
if (request.StartIndex.HasValue)
if (startIndex.HasValue)
{
items = items.Skip(request.StartIndex.Value);
items = items.Skip(startIndex.Value);
}
// Return limit
if (request.Limit.HasValue)
if (limit.HasValue)
{
items = items.Take(request.Limit.Value);
items = items.Take(limit.Value);
}
return items;
@ -409,7 +479,7 @@ namespace MediaBrowser.Api
return items;
}
public object Get(GetEpisodes request)
{
var user = _userManager.GetUserById(request.UserId);
@ -435,7 +505,7 @@ namespace MediaBrowser.Api
{
throw new ResourceNotFoundException("No season exists with Id " + request.SeasonId);
}
episodes = season.GetEpisodes(user);
}

@ -85,10 +85,10 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = UserManager.GetUserById(request.UserId.Value);
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
return DtoService.GetItemByNameDto(item, fields.ToList(), user);
}
return DtoService.GetBaseItemDto(item, fields.ToList());
return DtoService.GetItemByNameDto(item, fields.ToList());
}
/// <summary>
@ -111,7 +111,10 @@ namespace MediaBrowser.Api.UserLibrary
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
protected override IEnumerable<MusicArtist> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
{
return LibraryManager.GetAllArtists(items)
return items
.OfType<Audio>()
.SelectMany(i => i.AllArtists)
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(name =>
{
try
@ -126,10 +129,5 @@ namespace MediaBrowser.Api.UserLibrary
}).Where(i => i != null);
}
protected override IEnumerable<BaseItem> GetLibraryItems(MusicArtist item, IEnumerable<BaseItem> libraryItems)
{
return libraryItems.OfType<IHasArtist>().Where(i => i.HasArtist(item.Name)).Cast<BaseItem>();
}
}
}

@ -56,15 +56,21 @@ namespace MediaBrowser.Api.UserLibrary
{
User user = null;
BaseItem item;
List<BaseItem> libraryItems;
if (request.UserId.HasValue)
{
user = UserManager.GetUserById(request.UserId.Value);
item = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : DtoService.GetItemByDtoId(request.ParentId, user.Id);
libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
}
else
{
item = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : DtoService.GetItemByDtoId(request.ParentId);
libraryItems = LibraryManager.RootFolder.RecursiveChildren.ToList();
}
IEnumerable<BaseItem> items;
@ -93,7 +99,7 @@ namespace MediaBrowser.Api.UserLibrary
var filteredItems = FilterItems(request, extractedItems, user);
filteredItems = FilterByLibraryItems(request, filteredItems, user);
filteredItems = FilterByLibraryItems(request, filteredItems, user, libraryItems);
filteredItems = ItemsService.ApplySortOrder(request, filteredItems, user, LibraryManager).Cast<TItemType>();
@ -122,45 +128,39 @@ namespace MediaBrowser.Api.UserLibrary
var fields = request.GetItemFields().ToList();
var dtos = ibnItems.Select(i => GetDto(i, user, fields));
var tuples = ibnItems.Select(i => new Tuple<TItemType, List<BaseItem>>(i, i.GetTaggedItems(libraryItems).ToList()));
var dtos = tuples.Select(i => GetDto(i.Item1, user, fields, i.Item2));
result.Items = dtos.Where(i => i != null).ToArray();
return result;
}
private IEnumerable<TItemType> FilterByLibraryItems(GetItemsByName request, IEnumerable<TItemType> items, User user)
private IEnumerable<TItemType> FilterByLibraryItems(GetItemsByName request, IEnumerable<TItemType> items, User user, IEnumerable<BaseItem> libraryItems)
{
var filters = request.GetFilters().ToList();
if (filters.Contains(ItemFilter.IsPlayed))
{
var libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
items = items.Where(i => GetLibraryItems(i, libraryItems).All(l => l.IsPlayed(user)));
items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsPlayed(user)));
}
if (filters.Contains(ItemFilter.IsUnplayed))
{
var libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
items = items.Where(i => GetLibraryItems(i, libraryItems).All(l => l.IsUnplayed(user)));
items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsUnplayed(user)));
}
if (request.IsPlayed.HasValue)
{
var val = request.IsPlayed.Value;
var libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
items = items.Where(i => GetLibraryItems(i, libraryItems).All(l => l.IsPlayed(user)) == val);
items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsPlayed(user)) == val);
}
return items;
}
protected abstract IEnumerable<BaseItem> GetLibraryItems(TItemType item, IEnumerable<BaseItem> libraryItems);
/// <summary>
/// Filters the items.
/// </summary>
@ -174,6 +174,10 @@ namespace MediaBrowser.Api.UserLibrary
{
items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1);
}
if (!string.IsNullOrEmpty(request.NameStartsWith))
{
items = items.Where(i => string.Compare(request.NameStartsWith, i.SortName.Substring(0, 1), StringComparison.CurrentCultureIgnoreCase) == 0);
}
if (!string.IsNullOrEmpty(request.NameLessThan))
{
@ -288,11 +292,11 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <param name="fields">The fields.</param>
/// <param name="libraryItems">The library items.</param>
/// <returns>Task{DtoBaseItem}.</returns>
private BaseItemDto GetDto(TItemType item, User user, List<ItemFields> fields)
private BaseItemDto GetDto(TItemType item, User user, List<ItemFields> fields, List<BaseItem> libraryItems)
{
var dto = user == null ? DtoService.GetBaseItemDto(item, fields) :
DtoService.GetBaseItemDto(item, fields, user);
var dto = DtoService.GetItemByNameDto(item, fields, libraryItems, user);
return dto;
}
@ -313,9 +317,12 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "NameStartsWithOrGreater", Description = "Optional filter by items whose name is sorted equally or greater than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string NameStartsWithOrGreater { get; set; }
[ApiMember(Name = "NameStartsWith", Description = "Optional filter by items whose name is sorted equally than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string NameStartsWith { get; set; }
[ApiMember(Name = "NameLessThan", Description = "Optional filter by items whose name is sorted less than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string NameLessThan { get; set; }
public GetItemsByName()
{
Recursive = true;

@ -76,10 +76,10 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = UserManager.GetUserById(request.UserId.Value);
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
return DtoService.GetItemByNameDto(item, fields.ToList(), user);
}
return DtoService.GetBaseItemDto(item, fields.ToList());
return DtoService.GetItemByNameDto(item, fields.ToList());
}
/// <summary>
@ -109,10 +109,5 @@ namespace MediaBrowser.Api.UserLibrary
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(name => LibraryManager.GetGameGenre(name));
}
protected override IEnumerable<BaseItem> GetLibraryItems(GameGenre item, IEnumerable<BaseItem> libraryItems)
{
return libraryItems.Where(i => (i is Game) && i.Genres.Contains(item.Name, StringComparer.OrdinalIgnoreCase));
}
}
}

@ -81,10 +81,10 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = UserManager.GetUserById(request.UserId.Value);
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
return DtoService.GetItemByNameDto(item, fields.ToList(), user);
}
return DtoService.GetBaseItemDto(item, fields.ToList());
return DtoService.GetItemByNameDto(item, fields.ToList());
}
/// <summary>
@ -112,10 +112,5 @@ namespace MediaBrowser.Api.UserLibrary
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(name => LibraryManager.GetGenre(name));
}
protected override IEnumerable<BaseItem> GetLibraryItems(Genre item, IEnumerable<BaseItem> libraryItems)
{
return libraryItems.Where(i => !(i is Game) && !(i is IHasMusicGenres) && i.Genres.Contains(item.Name, StringComparer.OrdinalIgnoreCase));
}
}
}

@ -111,6 +111,12 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "NameStartsWithOrGreater", Description = "Optional filter by items whose name is sorted equally or greater than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string NameStartsWithOrGreater { get; set; }
[ApiMember(Name = "NameStartsWith", Description = "Optional filter by items whose name is sorted equally than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string NameStartsWith { get; set; }
[ApiMember(Name = "NameLessThan", Description = "Optional filter by items whose name is equally or lesser than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string NameLessThan { get; set; }
[ApiMember(Name = "AlbumArtistStartsWithOrGreater", Description = "Optional filter by items whose album artist is sorted equally or greater than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string AlbumArtistStartsWithOrGreater { get; set; }
@ -768,6 +774,15 @@ namespace MediaBrowser.Api.UserLibrary
{
items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1);
}
if (!string.IsNullOrEmpty(request.NameStartsWith))
{
items = items.Where(i => string.Compare(request.NameStartsWith, i.SortName.Substring(0, 1), StringComparison.CurrentCultureIgnoreCase) == 0);
}
if (!string.IsNullOrEmpty(request.NameLessThan))
{
items = items.Where(i => string.Compare(request.NameLessThan, i.SortName, StringComparison.CurrentCultureIgnoreCase) == 1);
}
if (!string.IsNullOrEmpty(request.AlbumArtistStartsWithOrGreater))
{

@ -76,10 +76,10 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = UserManager.GetUserById(request.UserId.Value);
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
return DtoService.GetItemByNameDto(item, fields.ToList(), user);
}
return DtoService.GetBaseItemDto(item, fields.ToList());
return DtoService.GetItemByNameDto(item, fields.ToList());
}
/// <summary>
@ -109,10 +109,5 @@ namespace MediaBrowser.Api.UserLibrary
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(name => LibraryManager.GetMusicGenre(name));
}
protected override IEnumerable<BaseItem> GetLibraryItems(MusicGenre item, IEnumerable<BaseItem> libraryItems)
{
return libraryItems.Where(i => (i is IHasMusicGenres) && i.Genres.Contains(item.Name, StringComparer.OrdinalIgnoreCase));
}
}
}

@ -93,10 +93,10 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = UserManager.GetUserById(request.UserId.Value);
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
return DtoService.GetItemByNameDto(item, fields.ToList(), user);
}
return DtoService.GetBaseItemDto(item, fields.ToList());
return DtoService.GetItemByNameDto(item, fields.ToList());
}
/// <summary>
@ -163,10 +163,5 @@ namespace MediaBrowser.Api.UserLibrary
people.Where(p => personTypes.Contains(p.Type ?? string.Empty, StringComparer.OrdinalIgnoreCase) || personTypes.Contains(p.Role ?? string.Empty, StringComparer.OrdinalIgnoreCase));
}
protected override IEnumerable<BaseItem> GetLibraryItems(Person item, IEnumerable<BaseItem> libraryItems)
{
return libraryItems.Where(i => i.People.Any(p => string.Equals(p.Name, item.Name, StringComparison.OrdinalIgnoreCase)));
}
}
}

@ -81,10 +81,10 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = UserManager.GetUserById(request.UserId.Value);
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
return DtoService.GetItemByNameDto(item, fields.ToList(), user);
}
return DtoService.GetBaseItemDto(item, fields.ToList());
return DtoService.GetItemByNameDto(item, fields.ToList());
}
/// <summary>
@ -114,10 +114,5 @@ namespace MediaBrowser.Api.UserLibrary
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(name => LibraryManager.GetStudio(name));
}
protected override IEnumerable<BaseItem> GetLibraryItems(Studio item, IEnumerable<BaseItem> libraryItems)
{
return libraryItems.Where(i => i.Studios.Contains(item.Name, StringComparer.OrdinalIgnoreCase));
}
}
}

@ -679,19 +679,35 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <param name="request">The request.</param>
public object Post(MarkPlayedItem request)
{
var result = MarkPlayed(request).Result;
return ToOptimizedResult(result);
}
private async Task<UserItemDataDto> MarkPlayed(MarkPlayedItem request)
{
var user = _userManager.GetUserById(request.UserId);
DateTime? datePlayed = null;
if (!string.IsNullOrEmpty(request.DatePlayed))
{
datePlayed = DateTime.ParseExact(request.DatePlayed, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
}
var task = UpdatePlayedStatus(user, request.Id, true, datePlayed);
var session = GetSession();
var dto = await UpdatePlayedStatus(user, request.Id, true, datePlayed).ConfigureAwait(false);
return ToOptimizedResult(task.Result);
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()
@ -779,12 +795,28 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <param name="request">The request.</param>
public object Delete(MarkUnplayedItem request)
{
var task = MarkUnplayed(request);
return ToOptimizedResult(task.Result);
}
private async Task<UserItemDataDto> MarkUnplayed(MarkUnplayedItem request)
{
var user = _userManager.GetUserById(request.UserId);
var task = UpdatePlayedStatus(user, request.Id, false, null);
var session = GetSession();
var dto = await UpdatePlayedStatus(user, request.Id, false, null).ConfigureAwait(false);
return ToOptimizedResult(task.Result);
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>

@ -7,7 +7,6 @@ using MediaBrowser.Model.Querying;
using ServiceStack;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace MediaBrowser.Api.UserLibrary
@ -81,10 +80,10 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = UserManager.GetUserById(request.UserId.Value);
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
return DtoService.GetItemByNameDto(item, fields.ToList(), user);
}
return DtoService.GetBaseItemDto(item, fields.ToList());
return DtoService.GetItemByNameDto(item, fields.ToList());
}
/// <summary>
@ -115,19 +114,5 @@ namespace MediaBrowser.Api.UserLibrary
.Distinct()
.Select(year => LibraryManager.GetYear(year));
}
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
protected override IEnumerable<BaseItem> GetLibraryItems(Year item, IEnumerable<BaseItem> libraryItems)
{
int year;
if (!int.TryParse(item.Name, NumberStyles.Integer, UsCulture, out year))
{
return libraryItems;
}
return libraryItems.Where(i => i.ProductionYear.HasValue && i.ProductionYear.Value == year);
}
}
}

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

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

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Channels
{
public interface IChannelManager
{
}
}

@ -0,0 +1,22 @@
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Collections
{
public class CollectionCreationOptions : IHasProviderIds
{
public string Name { get; set; }
public Guid? ParentId { get; set; }
public bool IsLocked { get; set; }
public Dictionary<string, string> ProviderIds { get; set; }
public CollectionCreationOptions()
{
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
}
}

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Collections
{
public interface ICollectionManager
{
/// <summary>
/// Creates the collection.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>Task.</returns>
Task CreateCollection(CollectionCreationOptions options);
/// <summary>
/// Adds to collection.
/// </summary>
/// <param name="collectionId">The collection identifier.</param>
/// <param name="itemIds">The item ids.</param>
/// <returns>Task.</returns>
Task AddToCollection(Guid collectionId, IEnumerable<Guid> itemIds);
/// <summary>
/// Removes from collection.
/// </summary>
/// <param name="collectionId">The collection identifier.</param>
/// <param name="itemIds">The item ids.</param>
/// <returns>Task.</returns>
Task RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds);
}
}

@ -73,5 +73,26 @@ namespace MediaBrowser.Controller.Dto
/// <param name="owner">The owner.</param>
/// <returns>Task{BaseItemDto}.</returns>
BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null);
/// <summary>
/// Gets the item by name dto.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="fields">The fields.</param>
/// <param name="user">The user.</param>
/// <returns>BaseItemDto.</returns>
BaseItemDto GetItemByNameDto<T>(T item, List<ItemFields> fields, User user = null)
where T : BaseItem, IItemByName;
/// <summary>
/// Gets the item by name dto.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="fields">The fields.</param>
/// <param name="taggedItems">The tagged items.</param>
/// <param name="user">The user.</param>
/// <returns>BaseItemDto.</returns>
BaseItemDto GetItemByNameDto<T>(T item, List<ItemFields> fields, List<BaseItem> taggedItems, User user = null)
where T : BaseItem, IItemByName;
}
}

@ -66,6 +66,24 @@ namespace MediaBrowser.Controller.Entities.Audio
/// <value>The artist.</value>
public List<string> Artists { get; set; }
[IgnoreDataMember]
public List<string> AllArtists
{
get
{
var list = new List<string>();
if (!string.IsNullOrEmpty(AlbumArtist))
{
list.Add(AlbumArtist);
}
list.AddRange(Artists);
return list;
}
}
/// <summary>
/// Gets or sets the album.
/// </summary>

@ -1,12 +1,10 @@
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
@ -17,9 +15,6 @@ namespace MediaBrowser.Controller.Entities.Audio
/// </summary>
public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasTags, IHasProductionLocations, IHasLookupInfo<ArtistInfo>
{
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
public bool IsAccessedByName { get; set; }
/// <summary>
@ -65,7 +60,6 @@ namespace MediaBrowser.Controller.Entities.Audio
public MusicArtist()
{
UserItemCountList = new List<ItemByNameCounts>();
Tags = new List<string>();
ProductionLocations = new List<string>();
}
@ -230,5 +224,10 @@ namespace MediaBrowser.Controller.Entities.Audio
return info;
}
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
{
return inputItems.OfType<IHasArtist>().Where(i => i.HasArtist(Name)).Cast<BaseItem>();
}
}
}

@ -1,7 +1,6 @@
using System.Runtime.Serialization;
using MediaBrowser.Model.Dto;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MediaBrowser.Controller.Entities.Audio
{
@ -10,11 +9,6 @@ namespace MediaBrowser.Controller.Entities.Audio
/// </summary>
public class MusicGenre : BaseItem, IItemByName
{
public MusicGenre()
{
UserItemCountList = new List<ItemByNameCounts>();
}
/// <summary>
/// Gets the user data key.
/// </summary>
@ -24,9 +18,6 @@ namespace MediaBrowser.Controller.Entities.Audio
return "MusicGenre-" + Name;
}
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
/// <summary>
/// Returns the folder containing the item.
/// If the item is a folder, it returns the folder itself
@ -51,5 +42,10 @@ namespace MediaBrowser.Controller.Entities.Audio
return false;
}
}
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
{
return inputItems.Where(i => (i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase));
}
}
}

@ -124,6 +124,15 @@ namespace MediaBrowser.Controller.Entities
}
}
[IgnoreDataMember]
public virtual bool IsHidden
{
get
{
return false;
}
}
[IgnoreDataMember]
public virtual bool IsOwnedItem
{
@ -1175,7 +1184,7 @@ namespace MediaBrowser.Controller.Entities
return GetImageInfo(type, imageIndex) != null;
}
public void SetImagePath(ImageType type, int index, FileInfo file)
public void SetImagePath(ImageType type, int index, FileSystemInfo file)
{
if (type == ImageType.Chapter)
{
@ -1330,7 +1339,7 @@ namespace MediaBrowser.Controller.Entities
/// <param name="images">The images.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
/// <exception cref="System.ArgumentException">Cannot call AddImages with chapter images</exception>
public bool AddImages(ImageType imageType, IEnumerable<FileInfo> images)
public bool AddImages(ImageType imageType, IEnumerable<FileSystemInfo> images)
{
if (imageType == ImageType.Chapter)
{

@ -1,5 +1,4 @@
using MediaBrowser.Model.Entities;

namespace MediaBrowser.Controller.Entities
{
/// <summary>
@ -8,18 +7,6 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public abstract class BasePluginFolder : Folder, ICollectionFolder, IByReferenceItem
{
/// <summary>
/// Gets or sets the type of the location.
/// </summary>
/// <value>The type of the location.</value>
public override LocationType LocationType
{
get
{
return LocationType.Virtual;
}
}
protected BasePluginFolder()
{
DisplayMediaType = "CollectionFolder";

@ -264,7 +264,7 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public IEnumerable<BaseItem> Children
{
get { return ActualChildren; }
get { return ActualChildren.Where(i => !i.IsHidden); }
}
/// <summary>
@ -745,9 +745,9 @@ namespace MediaBrowser.Controller.Entities
var list = new List<BaseItem>();
AddChildrenToList(user, includeLinkedChildren, list, false, null);
var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, false, null);
return list;
return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list;
}
/// <summary>
@ -905,13 +905,6 @@ namespace MediaBrowser.Controller.Entities
/// <returns>BaseItem.</returns>
private BaseItem GetLinkedChild(LinkedChild info)
{
if (string.IsNullOrEmpty(info.Path))
{
throw new ArgumentException("Encountered linked child with empty path.");
}
BaseItem item = null;
// First get using the cached Id
if (info.ItemId.HasValue)
{
@ -920,20 +913,19 @@ namespace MediaBrowser.Controller.Entities
return null;
}
item = LibraryManager.GetItemById(info.ItemId.Value);
}
var itemById = LibraryManager.GetItemById(info.ItemId.Value);
// If still null, search by path
if (item == null)
{
item = LibraryManager.RootFolder.FindByPath(info.Path);
if (itemById != null)
{
return itemById;
}
}
var item = FindLinkedChild(info);
// If still null, log
if (item == null)
{
Logger.Warn("Unable to find linked item at {0}", info.Path);
// Don't keep searching over and over
info.ItemId = Guid.Empty;
}
@ -946,6 +938,43 @@ namespace MediaBrowser.Controller.Entities
return item;
}
private BaseItem FindLinkedChild(LinkedChild info)
{
if (!string.IsNullOrEmpty(info.Path))
{
var itemByPath = LibraryManager.RootFolder.FindByPath(info.Path);
if (itemByPath == null)
{
Logger.Warn("Unable to find linked item at path {0}", info.Path);
}
return itemByPath;
}
if (!string.IsNullOrWhiteSpace(info.ItemName) && !string.IsNullOrWhiteSpace(info.ItemType))
{
return LibraryManager.RootFolder.RecursiveChildren.FirstOrDefault(i =>
{
if (string.Equals(i.Name, info.ItemName, StringComparison.OrdinalIgnoreCase))
{
if (string.Equals(i.GetType().Name, info.ItemType, StringComparison.OrdinalIgnoreCase))
{
if (info.ItemYear.HasValue)
{
return info.ItemYear.Value == (i.ProductionYear ?? -1);
}
return true;
}
}
return false;
});
}
return null;
}
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
var changesFound = false;
@ -1106,5 +1135,10 @@ namespace MediaBrowser.Controller.Entities
return GetRecursiveChildren(user).Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual)
.All(i => i.IsUnplayed(user));
}
public IEnumerable<BaseItem> GetHiddenChildren()
{
return ActualChildren.Where(i => i.IsHidden);
}
}
}

@ -1,16 +1,11 @@
using MediaBrowser.Model.Dto;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Linq;
namespace MediaBrowser.Controller.Entities
{
public class GameGenre : BaseItem, IItemByName
{
public GameGenre()
{
UserItemCountList = new List<ItemByNameCounts>();
}
/// <summary>
/// Gets the user data key.
/// </summary>
@ -20,9 +15,6 @@ namespace MediaBrowser.Controller.Entities
return "GameGenre-" + Name;
}
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
/// <summary>
/// Returns the folder containing the item.
/// If the item is a folder, it returns the folder itself
@ -47,5 +39,10 @@ namespace MediaBrowser.Controller.Entities
return false;
}
}
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
{
return inputItems.Where(i => (i is Game) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase));
}
}
}

@ -1,6 +1,7 @@
using MediaBrowser.Model.Dto;
using MediaBrowser.Controller.Entities.Audio;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Linq;
namespace MediaBrowser.Controller.Entities
{
@ -9,11 +10,6 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public class Genre : BaseItem, IItemByName
{
public Genre()
{
UserItemCountList = new List<ItemByNameCounts>();
}
/// <summary>
/// Gets the user data key.
/// </summary>
@ -23,9 +19,6 @@ namespace MediaBrowser.Controller.Entities
return "Genre-" + Name;
}
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
/// <summary>
/// Returns the folder containing the item.
/// If the item is a folder, it returns the folder itself
@ -50,5 +43,10 @@ namespace MediaBrowser.Controller.Entities
return false;
}
}
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
{
return inputItems.Where(i => !(i is Game) && !(i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase));
}
}
}

@ -62,7 +62,7 @@ namespace MediaBrowser.Controller.Entities
/// <param name="type">The type.</param>
/// <param name="index">The index.</param>
/// <param name="file">The file.</param>
void SetImagePath(ImageType type, int index, FileInfo file);
void SetImagePath(ImageType type, int index, FileSystemInfo file);
/// <summary>
/// Determines whether the specified type has image.
@ -129,7 +129,7 @@ namespace MediaBrowser.Controller.Entities
/// <param name="imageType">Type of the image.</param>
/// <param name="images">The images.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
bool AddImages(ImageType imageType, IEnumerable<FileInfo> images);
bool AddImages(ImageType imageType, IEnumerable<FileSystemInfo> images);
/// <summary>
/// Determines whether [is save local metadata enabled].
@ -180,7 +180,7 @@ namespace MediaBrowser.Controller.Entities
/// <param name="item">The item.</param>
/// <param name="imageType">Type of the image.</param>
/// <param name="file">The file.</param>
public static void SetImagePath(this IHasImages item, ImageType imageType, FileInfo file)
public static void SetImagePath(this IHasImages item, ImageType imageType, FileSystemInfo file)
{
item.SetImagePath(imageType, 0, file);
}

@ -1,7 +1,4 @@
using MediaBrowser.Model.Dto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
@ -10,37 +7,16 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public interface IItemByName
{
List<ItemByNameCounts> UserItemCountList { get; set; }
/// <summary>
/// Gets the tagged items.
/// </summary>
/// <param name="inputItems">The input items.</param>
/// <returns>IEnumerable{BaseItem}.</returns>
IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems);
}
public interface IHasDualAccess : IItemByName
{
bool IsAccessedByName { get; }
}
public static class ItemByNameExtensions
{
public static ItemByNameCounts GetItemByNameCounts(this IItemByName item, Guid userId)
{
if (userId == Guid.Empty)
{
throw new ArgumentNullException("userId");
}
return item.UserItemCountList.FirstOrDefault(i => i.UserId == userId);
}
public static void SetItemByNameCounts(this IItemByName item, Guid userId, ItemByNameCounts counts)
{
var current = item.UserItemCountList.FirstOrDefault(i => i.UserId == userId);
if (current != null)
{
item.UserItemCountList.Remove(current);
}
counts.UserId = userId;
item.UserItemCountList.Add(counts);
}
}
}

@ -9,6 +9,10 @@ namespace MediaBrowser.Controller.Entities
public string Path { get; set; }
public LinkedChildType Type { get; set; }
public string ItemName { get; set; }
public string ItemType { get; set; }
public int? ItemYear { get; set; }
/// <summary>
/// Serves as a cache
/// </summary>
@ -18,8 +22,8 @@ namespace MediaBrowser.Controller.Entities
public enum LinkedChildType
{
Manual = 1,
Shortcut = 2
Manual = 0,
Shortcut = 1
}
public class LinkedChildComparer : IEqualityComparer<LinkedChild>
@ -35,7 +39,7 @@ namespace MediaBrowser.Controller.Entities
public int GetHashCode(LinkedChild obj)
{
return (obj.Path + obj.Type.ToString()).GetHashCode();
return (obj.Path + obj.Type).GetHashCode();
}
}
}

@ -1,7 +1,7 @@
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Linq;
namespace MediaBrowser.Controller.Entities
{
@ -10,19 +10,11 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public class Person : BaseItem, IItemByName, IHasLookupInfo<PersonLookupInfo>
{
public Person()
{
UserItemCountList = new List<ItemByNameCounts>();
}
/// <summary>
/// Gets or sets the place of birth.
/// </summary>
/// <value>The place of birth.</value>
public string PlaceOfBirth { get; set; }
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
/// <summary>
/// Gets the user data key.
@ -62,6 +54,11 @@ namespace MediaBrowser.Controller.Entities
return false;
}
}
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
{
return inputItems.Where(i => i.People.Any(p => string.Equals(p.Name, Name, StringComparison.OrdinalIgnoreCase)));
}
}
/// <summary>

@ -1,7 +1,6 @@
using System.Runtime.Serialization;
using MediaBrowser.Model.Dto;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MediaBrowser.Controller.Entities
{
@ -10,11 +9,6 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public class Studio : BaseItem, IItemByName
{
public Studio()
{
UserItemCountList = new List<ItemByNameCounts>();
}
/// <summary>
/// Gets the user data key.
/// </summary>
@ -24,9 +18,6 @@ namespace MediaBrowser.Controller.Entities
return "Studio-" + Name;
}
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
/// <summary>
/// Returns the folder containing the item.
/// If the item is a folder, it returns the folder itself
@ -51,5 +42,10 @@ namespace MediaBrowser.Controller.Entities
return false;
}
}
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
{
return inputItems.Where(i => i.Studios.Contains(Name, StringComparer.OrdinalIgnoreCase));
}
}
}

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Providers;
using System;
using System.Collections.Generic;
using System.Linq;
@ -23,7 +24,7 @@ namespace MediaBrowser.Controller.Entities
{
var hasChanges = base.BeforeMetadataRefresh();
if (string.Equals("default", Name, System.StringComparison.OrdinalIgnoreCase))
if (string.Equals("default", Name, StringComparison.OrdinalIgnoreCase))
{
Name = "Media Folders";
hasChanges = true;

@ -1,7 +1,6 @@
using System.Runtime.Serialization;
using MediaBrowser.Model.Dto;
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace MediaBrowser.Controller.Entities
{
@ -10,14 +9,6 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public class Year : BaseItem, IItemByName
{
public Year()
{
UserItemCountList = new List<ItemByNameCounts>();
}
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
/// <summary>
/// Gets the user data key.
/// </summary>
@ -51,5 +42,19 @@ namespace MediaBrowser.Controller.Entities
return false;
}
}
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
{
int year;
var usCulture = new CultureInfo("en-US");
if (!int.TryParse(Name, NumberStyles.Integer, usCulture, out year))
{
return inputItems;
}
return inputItems.Where(i => i.ProductionYear.HasValue && i.ProductionYear.Value == year);
}
}
}

@ -304,19 +304,6 @@ namespace MediaBrowser.Controller.Library
/// <returns>System.String.</returns>
string FindCollectionType(BaseItem item);
/// <summary>
/// Gets all artists.
/// </summary>
/// <returns>IEnumerable{System.String}.</returns>
IEnumerable<string> GetAllArtists();
/// <summary>
/// Gets all artists.
/// </summary>
/// <param name="items">The items.</param>
/// <returns>IEnumerable{System.String}.</returns>
IEnumerable<string> GetAllArtists(IEnumerable<BaseItem> items);
/// <summary>
/// Normalizes the root path list.
/// </summary>

@ -51,9 +51,8 @@ namespace MediaBrowser.Controller.Library
/// Refreshes metadata for each user
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="force">if set to <c>true</c> [force].</param>
/// <returns>Task.</returns>
Task RefreshUsersMetadata(CancellationToken cancellationToken, bool force = false);
Task RefreshUsersMetadata(CancellationToken cancellationToken);
/// <summary>
/// Renames the user.

@ -27,94 +27,94 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// A season folder must contain one of these somewhere in the name
/// </summary>
private static readonly string[] SeasonFolderNames = new[]
{
"season",
"sæson",
"temporada",
"saison",
"staffel",
"series",
"сезон"
};
private static readonly string[] SeasonFolderNames =
{
"season",
"sæson",
"temporada",
"saison",
"staffel",
"series",
"сезон"
};
/// <summary>
/// Used to detect paths that represent episodes, need to make sure they don't also
/// match movie titles like "2001 A Space..."
/// Currently we limit the numbers here to 2 digits to try and avoid this
/// </summary>
private static readonly Regex[] EpisodeExpressions = new[]
{
new Regex(
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)[sS](?<seasonnumber>\d{1,4})[x,X]?[eE](?<epnumber>\d{1,3})[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})[^\\\/]*$",
RegexOptions.Compiled)
};
private static readonly Regex[] MultipleEpisodeExpressions = new[]
{
new Regex(
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[eExX](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})(-[xE]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )\d{1,4}[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled)
};
private static readonly Regex[] EpisodeExpressions =
{
new Regex(
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)[sS](?<seasonnumber>\d{1,4})[x,X]?[eE](?<epnumber>\d{1,3})[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})[^\\\/]*$",
RegexOptions.Compiled)
};
private static readonly Regex[] MultipleEpisodeExpressions =
{
new Regex(
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[eExX](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})(-[xE]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )\d{1,4}[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled),
new Regex(
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
RegexOptions.Compiled)
};
/// <summary>
/// To avoid the following matching movies they are only valid when contained in a folder which has been matched as a being season
/// </summary>
private static readonly Regex[] EpisodeExpressionsInASeasonFolder = new[]
{
new Regex(
@".*(\\|\/)(?<epnumber>\d{1,2})\s?-\s?[^\\\/]*$",
RegexOptions.Compiled),
// 01 - blah.avi, 01-blah.avi
new Regex(
@".*(\\|\/)(?<epnumber>\d{1,2})[^\d\\]*[^\\\/]*$",
RegexOptions.Compiled),
// 01.avi, 01.blah.avi "01 - 22 blah.avi"
new Regex(
@".*(\\|\/)(?<seasonnumber>\d)(?<epnumber>\d{1,2})[^\d\\]+[^\\\/]*$",
RegexOptions.Compiled),
// 01.avi, 01.blah.avi
new Regex(
@".*(\\|\/)\D*\d+(?<epnumber>\d{2})",
RegexOptions.Compiled)
// hell0 - 101 - hello.avi
};
private static readonly Regex[] EpisodeExpressionsInASeasonFolder =
{
new Regex(
@".*(\\|\/)(?<epnumber>\d{1,2})\s?-\s?[^\\\/]*$",
RegexOptions.Compiled),
// 01 - blah.avi, 01-blah.avi
new Regex(
@".*(\\|\/)(?<epnumber>\d{1,2})[^\d\\]*[^\\\/]*$",
RegexOptions.Compiled),
// 01.avi, 01.blah.avi "01 - 22 blah.avi"
new Regex(
@".*(\\|\/)(?<seasonnumber>\d)(?<epnumber>\d{1,2})[^\d\\]+[^\\\/]*$",
RegexOptions.Compiled),
// 01.avi, 01.blah.avi
new Regex(
@".*(\\|\/)\D*\d+(?<epnumber>\d{2})",
RegexOptions.Compiled)
// hell0 - 101 - hello.avi
};
/// <summary>
/// Gets the season number from path.
@ -151,8 +151,8 @@ namespace MediaBrowser.Controller.Library
/// <returns>System.Nullable{System.Int32}.</returns>
private static int? GetSeasonNumberFromPathSubstring(string path)
{
int numericStart = -1;
int length = 0;
var numericStart = -1;
var length = 0;
// Find out where the numbers start, and then keep going until they end
for (var i = 0; i < path.Length; i++)

@ -138,13 +138,6 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="id">The identifier.</param>
/// <returns>Channel.</returns>
LiveTvChannel GetInternalChannel(string id);
/// <summary>
/// Gets the internal program.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>LiveTvProgram.</returns>
LiveTvProgram GetInternalProgram(string id);
/// <summary>
/// Gets the recording.

@ -1,20 +1,13 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Linq;
namespace MediaBrowser.Controller.LiveTv
{
public class LiveTvChannel : BaseItem, IItemByName
{
public LiveTvChannel()
{
UserItemCountList = new List<ItemByNameCounts>();
}
/// <summary>
/// Gets the user data key.
/// </summary>
@ -24,9 +17,6 @@ namespace MediaBrowser.Controller.LiveTv
return GetClientTypeName() + "-" + Name;
}
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
/// <summary>
/// Returns the folder containing the item.
/// If the item is a folder, it returns the folder itself
@ -119,5 +109,10 @@ namespace MediaBrowser.Controller.LiveTv
{
return "Channel";
}
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
{
return new List<BaseItem>();
}
}
}

@ -68,6 +68,11 @@
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
<Compile Include="Channels\ChannelItemInfo.cs" />
<Compile Include="Channels\IChannel.cs" />
<Compile Include="Channels\IChannelManager.cs" />
<Compile Include="Collections\CollectionCreationOptions.cs" />
<Compile Include="Collections\ICollectionManager.cs" />
<Compile Include="Drawing\IImageProcessor.cs" />
<Compile Include="Drawing\ImageFormat.cs" />
<Compile Include="Drawing\ImageProcessingOptions.cs" />

@ -95,6 +95,18 @@ namespace MediaBrowser.Controller.Net
/// <returns>System.Object.</returns>
object GetStaticFileResult(IRequest requestContext, string path, FileShare fileShare = FileShare.Read, IDictionary<string, string> responseHeaders = null, bool isHeadRequest = false);
/// <summary>
/// Gets the static file result.
/// </summary>
/// <param name="requestContext">The request context.</param>
/// <param name="path">The path.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="fileShare">The file share.</param>
/// <param name="responseHeaders">The response headers.</param>
/// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
/// <returns>System.Object.</returns>
object GetStaticFileResult(IRequest requestContext, string path, string contentType, FileShare fileShare = FileShare.Read, IDictionary<string, string> responseHeaders = null, bool isHeadRequest = false);
/// <summary>
/// Gets the optimized serialized result using cache.
/// </summary>

@ -1,6 +1,6 @@
using System.Collections.Concurrent;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -10,10 +10,8 @@ namespace MediaBrowser.Controller.Providers
public interface IDirectoryService
{
List<FileSystemInfo> GetFileSystemEntries(string path);
IEnumerable<FileInfo> GetFiles(string path);
IEnumerable<DirectoryInfo> GetDirectories(string path);
FileInfo GetFile(string path);
DirectoryInfo GetDirectory(string path);
IEnumerable<FileSystemInfo> GetFiles(string path);
FileSystemInfo GetFile(string path);
}
public class DirectoryService : IDirectoryService
@ -50,31 +48,17 @@ namespace MediaBrowser.Controller.Providers
return entries;
}
public IEnumerable<FileInfo> GetFiles(string path)
public IEnumerable<FileSystemInfo> GetFiles(string path)
{
return GetFileSystemEntries(path).OfType<FileInfo>();
return GetFileSystemEntries(path).Where(i => (i.Attributes & FileAttributes.Directory) != FileAttributes.Directory);
}
public IEnumerable<DirectoryInfo> GetDirectories(string path)
{
return GetFileSystemEntries(path).OfType<DirectoryInfo>();
}
public FileInfo GetFile(string path)
public FileSystemInfo GetFile(string path)
{
var directory = Path.GetDirectoryName(path);
var filename = Path.GetFileName(path);
return GetFiles(directory).FirstOrDefault(i => string.Equals(i.Name, filename, StringComparison.OrdinalIgnoreCase));
}
public DirectoryInfo GetDirectory(string path)
{
var directory = Path.GetDirectoryName(path);
var name = Path.GetFileName(path);
return GetDirectories(directory).FirstOrDefault(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase));
}
}
}

@ -23,7 +23,7 @@ namespace MediaBrowser.Controller.Providers
public class LocalImageInfo
{
public FileInfo FileInfo { get; set; }
public FileSystemInfo FileInfo { get; set; }
public ImageType Type { get; set; }
}

@ -76,6 +76,13 @@ namespace MediaBrowser.Controller.Session
/// <exception cref="System.ArgumentNullException"></exception>
Task OnPlaybackStopped(PlaybackStopInfo info);
/// <summary>
/// Reports the session ended.
/// </summary>
/// <param name="sessionId">The session identifier.</param>
/// <returns>Task.</returns>
Task ReportSessionEnded(Guid sessionId);
/// <summary>
/// Sends the system command.
/// </summary>

@ -53,7 +53,7 @@
</Compile>
<Compile Include="PlayTo\Argument.cs" />
<Compile Include="PlayTo\Configuration\DlnaProfile.cs" />
<Compile Include="PlayTo\Configuration\PluginConfiguration.cs" />
<Compile Include="PlayTo\Configuration\PlayToConfiguration.cs" />
<Compile Include="PlayTo\Configuration\TranscodeSetting.cs" />
<Compile Include="PlayTo\CurrentIdEventArgs.cs" />
<Compile Include="PlayTo\Device.cs">

@ -29,7 +29,12 @@
FriendlyName = "^TV$",
ModelNumber = @"1\.0",
ModelName = "Samsung DTV DMR",
TranscodeSettings = TranscodeSettings.GetDefaultTranscodingSettings()
TranscodeSettings = new[]
{
new TranscodeSettings {Container = "mkv", MimeType = "x-mkv"},
new TranscodeSettings {Container = "flac", TargetContainer = "mp3"},
new TranscodeSettings {Container = "m4a", TargetContainer = "mp3"}
}
};
var profile1 = new DlnaProfile
@ -38,7 +43,12 @@
ClientType = "DLNA",
FriendlyName = @"(^\[TV\][A-Z]{2}\d{2}(E|F)[A-Z]?\d{3,4}.*)|^\[TV\] Samsung",
ModelNumber = @"(1\.0)|(AllShare1\.0)",
TranscodeSettings = TranscodeSettings.GetDefaultTranscodingSettings()
TranscodeSettings = new[]
{
new TranscodeSettings {Container = "mkv", MimeType = "x-mkv"},
new TranscodeSettings {Container = "flac", TargetContainer = "mp3"},
new TranscodeSettings {Container = "m4a", TargetContainer = "mp3"}
}
};
var profile2 = new DlnaProfile
@ -47,7 +57,12 @@
ClientType = "DLNA",
FriendlyName = @"(^TV-\d{2}C\d{3}.*)|(^\[TV\][A-Z]{2}\d{2}(D)[A-Z]?\d{3,4}.*)|^\[TV\] Samsung",
ModelNumber = @"(1\.0)|(AllShare1\.0)",
TranscodeSettings = TranscodeSettings.GetDefaultTranscodingSettings()
TranscodeSettings = new[]
{
new TranscodeSettings {Container = "mkv", MimeType = "x-mkv"},
new TranscodeSettings {Container = "flac", TargetContainer = "mp3"},
new TranscodeSettings {Container = "m4a", TargetContainer = "mp3"}
}
};
var profile3 = new DlnaProfile

@ -1,4 +1,5 @@
using System;
using System.Text.RegularExpressions;
namespace MediaBrowser.Dlna.PlayTo.Configuration
{
@ -20,6 +21,14 @@ namespace MediaBrowser.Dlna.PlayTo.Configuration
/// </value>
public string TargetContainer { get; set; }
/// <summary>
/// Gets or sets the Mimetype to enforce
/// </summary>
/// <value>
/// The MimeType.
/// </value>
public string MimeType { get; set; }
/// <summary>
/// The default transcoding settings
/// </summary>
@ -46,19 +55,19 @@ namespace MediaBrowser.Dlna.PlayTo.Configuration
{
if (!string.IsNullOrEmpty(profile.FriendlyName))
{
if (!string.Equals(deviceProperties.Name, profile.FriendlyName, StringComparison.OrdinalIgnoreCase))
if (!Regex.IsMatch(deviceProperties.Name, profile.FriendlyName))
continue;
}
if (!string.IsNullOrEmpty(profile.ModelNumber))
{
if (!string.Equals(deviceProperties.ModelNumber, profile.ModelNumber, StringComparison.OrdinalIgnoreCase))
if (!Regex.IsMatch(deviceProperties.ModelNumber, profile.ModelNumber))
continue;
}
if (!string.IsNullOrEmpty(profile.ModelName))
{
if (!string.Equals(deviceProperties.ModelName, profile.ModelName, StringComparison.OrdinalIgnoreCase))
if (!Regex.IsMatch(deviceProperties.ModelName, profile.ModelName))
continue;
}

@ -76,8 +76,8 @@ namespace MediaBrowser.Dlna.PlayTo
_transportState = value;
if (value == "PLAYING" || value == "STOPPED")
NotifyPlaybackChanged(value == "STOPPED");
if (value == TRANSPORTSTATE.PLAYING || value == TRANSPORTSTATE.STOPPED)
NotifyPlaybackChanged(value == TRANSPORTSTATE.STOPPED);
}
}
@ -85,7 +85,7 @@ namespace MediaBrowser.Dlna.PlayTo
{
get
{
return TransportState == "PLAYING";
return TransportState == TRANSPORTSTATE.PLAYING;
}
}
@ -93,7 +93,7 @@ namespace MediaBrowser.Dlna.PlayTo
{
get
{
return (TransportState == "TRANSITIONING");
return (TransportState == TRANSPORTSTATE.TRANSITIONING);
}
}
@ -101,7 +101,7 @@ namespace MediaBrowser.Dlna.PlayTo
{
get
{
return TransportState == "PAUSED" || TransportState == "PAUSED_PLAYBACK";
return TransportState == TRANSPORTSTATE.PAUSED || TransportState == TRANSPORTSTATE.PAUSED_PLAYBACK;
}
}
@ -109,7 +109,7 @@ namespace MediaBrowser.Dlna.PlayTo
{
get
{
return (TransportState == "STOPPED");
return TransportState == TRANSPORTSTATE.STOPPED;
}
}
@ -127,23 +127,39 @@ namespace MediaBrowser.Dlna.PlayTo
_logger = logger;
}
private int GetTimerIntervalMs()
private int GetPlaybackTimerIntervalMs()
{
return 10000;
return 2000;
}
private int GetInactiveTimerIntervalMs()
{
return 20000;
}
public void Start()
{
UpdateTime = DateTime.UtcNow;
var interval = GetTimerIntervalMs();
var interval = GetPlaybackTimerIntervalMs();
_timer = new Timer(TimerCallback, null, interval, interval);
}
private void RestartTimer()
{
var interval = GetTimerIntervalMs();
var interval = GetPlaybackTimerIntervalMs();
_timer.Change(interval, interval);
}
/// <summary>
/// Restarts the timer in inactive mode.
/// </summary>
private void RestartTimerInactive()
{
var interval = GetInactiveTimerIntervalMs();
_timer.Change(interval, interval);
}
@ -230,11 +246,9 @@ namespace MediaBrowser.Dlna.PlayTo
{
StopTimer();
TransportState = "STOPPED";
await SetStop().ConfigureAwait(false);
CurrentId = "0";
await Task.Delay(50).ConfigureAwait(false);
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
if (command == null)
return false;
@ -261,7 +275,7 @@ namespace MediaBrowser.Dlna.PlayTo
await SetPlay().ConfigureAwait(false);
}
_count = 5;
_lapsCount = GetLapsCount();
RestartTimer();
return true;
@ -322,7 +336,7 @@ namespace MediaBrowser.Dlna.PlayTo
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false);
_count = 5;
_lapsCount = GetLapsCount();
return true;
}
@ -338,7 +352,6 @@ namespace MediaBrowser.Dlna.PlayTo
.ConfigureAwait(false);
await Task.Delay(50).ConfigureAwait(false);
_count = 4;
return true;
}
@ -362,8 +375,13 @@ namespace MediaBrowser.Dlna.PlayTo
#region Get data
// TODO: What is going on here
int _count = 5;
private int GetLapsCount()
{
// No need to get all data every lap, just every X time.
return 10;
}
int _lapsCount = 0;
private async void TimerCallback(object sender)
{
@ -374,18 +392,24 @@ namespace MediaBrowser.Dlna.PlayTo
try
{
var hasTrack = await GetPositionInfo().ConfigureAwait(false);
await GetTransportInfo().ConfigureAwait(false);
// TODO: Why make these requests if hasTrack==false?
if (_count > 5)
//If we're not playing anything no need to get additional data
if (TransportState != TRANSPORTSTATE.STOPPED)
{
await GetTransportInfo().ConfigureAwait(false);
if (!hasTrack)
var hasTrack = await GetPositionInfo().ConfigureAwait(false);
// TODO: Why make these requests if hasTrack==false?
// TODO ANSWER Some vendors don't include track in GetPositionInfo, use GetMediaInfo instead.
if (_lapsCount > GetLapsCount())
{
await GetMediaInfo().ConfigureAwait(false);
if (!hasTrack)
{
await GetMediaInfo().ConfigureAwait(false);
}
await GetVolume().ConfigureAwait(false);
_lapsCount = 0;
}
await GetVolume().ConfigureAwait(false);
_count = 0;
}
}
catch (Exception ex)
@ -393,11 +417,16 @@ namespace MediaBrowser.Dlna.PlayTo
_logger.ErrorException("Error updating device info", ex);
}
_count++;
_lapsCount++;
if (_disposed)
return;
RestartTimer();
//If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive
if (TransportState != TRANSPORTSTATE.STOPPED)
RestartTimer();
else
RestartTimerInactive();
}
private async Task GetVolume()
@ -747,5 +776,16 @@ namespace MediaBrowser.Dlna.PlayTo
{
return String.Format("{0} - {1}", Properties.Name, Properties.BaseUrl);
}
private class TRANSPORTSTATE
{
public const string STOPPED = "STOPPED";
public const string PLAYING = "PLAYING";
public const string TRANSITIONING = "TRANSITIONING";
public const string PAUSED_PLAYBACK = "PAUSED_PLAYBACK";
public const string PAUSED = "PAUSED";
}
}
}

@ -17,7 +17,7 @@ using Timer = System.Timers.Timer;
namespace MediaBrowser.Dlna.PlayTo
{
public class PlayToController : ISessionController
public class PlayToController : ISessionController, IDisposable
{
private Device _device;
private BaseItem _currentItem = null;
@ -131,6 +131,14 @@ namespace MediaBrowser.Dlna.PlayTo
((Timer)sender).Stop();
if (!IsSessionActive)
{
//Session is inactive, mark it for Disposal and don't start the elapsed timer.
await _sessionManager.ReportSessionEnded(this._session.Id);
return;
}
await ReportProgress().ConfigureAwait(false);
if (!_disposed && IsSessionActive)
@ -479,3 +487,4 @@ namespace MediaBrowser.Dlna.PlayTo
}
}
}

@ -1,40 +1,107 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
using System;
namespace MediaBrowser.Dlna.PlayTo
{
public class PlayToServerEntryPoint : IServerEntryPoint
{
private bool _disposed;
private PlayToManager _manager;
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
private readonly ISessionManager _sessionManager;
private readonly IHttpClient _httpClient;
private readonly IItemRepository _itemRepo;
private readonly ILibraryManager _libraryManager;
private readonly INetworkManager _networkManager;
private readonly IUserManager _userManager;
private readonly PlayToManager _manager;
public PlayToServerEntryPoint(ILogManager logManager, ISessionManager sessionManager, IUserManager userManager, IHttpClient httpClient, INetworkManager networkManager, IItemRepository itemRepository, ILibraryManager libraryManager)
public PlayToServerEntryPoint(ILogManager logManager, IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepo, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager)
{
_manager = new PlayToManager(logManager.GetLogger("PlayTo"), sessionManager, httpClient, itemRepository, libraryManager, networkManager, userManager);
_config = config;
_sessionManager = sessionManager;
_httpClient = httpClient;
_itemRepo = itemRepo;
_libraryManager = libraryManager;
_networkManager = networkManager;
_userManager = userManager;
_logger = logManager.GetLogger("PlayTo");
}
public void Run()
{
//_manager.Start();
_config.ConfigurationUpdated += ConfigurationUpdated;
ReloadPlayToManager();
}
#region Dispose
void ConfigurationUpdated(object sender, EventArgs e)
{
ReloadPlayToManager();
}
public void Dispose()
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()
{
if (!_disposed)
lock (_syncLock)
{
_disposed = true;
_manager.Stop();
_manager.Dispose();
if (_manager != null)
{
try
{
_manager.Stop();
_manager.Dispose();
}
catch (Exception ex)
{
_logger.ErrorException("Error disposing PlayTo manager", ex);
}
_manager = null;
}
}
}
#region Dispose
public void Dispose()
{
DisposePlayToManager();
}
#endregion
}
}

@ -16,6 +16,8 @@ namespace MediaBrowser.Dlna.PlayTo
public string FileFormat { get; set; }
public string MimeType { get; set; }
public int PlayState { get; set; }
public string StreamUrl { get; set; }
@ -51,10 +53,21 @@ namespace MediaBrowser.Dlna.PlayTo
{
if (string.IsNullOrWhiteSpace(transcodeSetting.Container))
continue;
if (path.EndsWith(transcodeSetting.Container))
if (path.EndsWith(transcodeSetting.Container) && !string.IsNullOrWhiteSpace(transcodeSetting.TargetContainer))
{
playlistItem.Transcode = true;
playlistItem.FileFormat = transcodeSetting.TargetContainer;
if (string.IsNullOrWhiteSpace(transcodeSetting.MimeType))
playlistItem.MimeType = transcodeSetting.MimeType;
return playlistItem;
}
if (path.EndsWith(transcodeSetting.Container) && !string.IsNullOrWhiteSpace(transcodeSetting.MimeType))
{
playlistItem.Transcode = false;
playlistItem.FileFormat = transcodeSetting.Container;
playlistItem.MimeType = transcodeSetting.MimeType;
return playlistItem;
}
}

@ -96,9 +96,12 @@ namespace MediaBrowser.Dlna.PlayTo
/// <returns>The url to send to the device</returns>
internal static string GetVideoUrl(DeviceProperties deviceProperties, PlaylistItem item, List<MediaStream> streams, string serverAddress)
{
string dlnaCommand = string.Empty;
if (!item.Transcode)
return string.Format("{0}/Videos/{1}/stream.{2}?Static=True", serverAddress, item.ItemId, item.FileFormat);
{
dlnaCommand = BuildDlnaUrl(deviceProperties.UUID, !item.Transcode, null, null, null, null, null, null, null, null, null, null, item.MimeType);
return string.Format("{0}/Videos/{1}/stream.{2}?{3}", serverAddress, item.ItemId, item.FileFormat, dlnaCommand);
}
var videostream = streams.Where(m => m.Type == MediaStreamType.Video).OrderBy(m => m.IsDefault).FirstOrDefault();
var audiostream = streams.Where(m => m.Type == MediaStreamType.Audio).OrderBy(m => m.IsDefault).FirstOrDefault();
@ -117,7 +120,7 @@ namespace MediaBrowser.Dlna.PlayTo
audioChannels = 2;
}
string dlnaCommand = BuildDlnaUrl(deviceProperties.UUID, videoCodec, audioCodec, null, null, videoBitrate, audioChannels, audioBitrate, item.StartPositionTicks, "baseline", "3");
dlnaCommand = BuildDlnaUrl(deviceProperties.UUID, !item.Transcode, videoCodec, audioCodec, null, null, videoBitrate, audioChannels, audioBitrate, item.StartPositionTicks, "baseline", "3", item.MimeType);
return string.Format("{0}/Videos/{1}/stream.{2}?{3}", serverAddress, item.ItemId, item.FileFormat, dlnaCommand);
}
@ -162,12 +165,12 @@ namespace MediaBrowser.Dlna.PlayTo
/// <summary>
/// Builds the dlna URL.
/// </summary>
private static string BuildDlnaUrl(string deviceID, VideoCodecs? videoCodec, AudioCodecs? audioCodec, int? subtitleIndex, int? audiostreamIndex, int? videoBitrate, int? audiochannels, int? audioBitrate, long? startPositionTicks, string profile, string videoLevel)
private static string BuildDlnaUrl(string deviceID, bool isStatic, VideoCodecs? videoCodec, AudioCodecs? audioCodec, int? subtitleIndex, int? audiostreamIndex, int? videoBitrate, int? audiochannels, int? audioBitrate, long? startPositionTicks, string profile, string videoLevel, string mimeType)
{
var usCulture = new CultureInfo("en-US");
var dlnaparam = string.Format("Params={0};", deviceID);
dlnaparam += isStatic ? "true;" : "false;";
dlnaparam += videoCodec.HasValue ? videoCodec.Value + ";" : ";";
dlnaparam += audioCodec.HasValue ? audioCodec.Value + ";" : ";";
dlnaparam += audiostreamIndex.HasValue ? audiostreamIndex.Value.ToString(usCulture) + ";" : ";";
@ -178,6 +181,7 @@ namespace MediaBrowser.Dlna.PlayTo
dlnaparam += startPositionTicks.HasValue ? startPositionTicks.Value.ToString(usCulture) + ";" : ";";
dlnaparam += profile + ";";
dlnaparam += videoLevel + ";";
dlnaparam += mimeType + ";";
return dlnaparam;
}

@ -80,6 +80,9 @@
<Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
<Link>Configuration\BaseApplicationConfiguration.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\DlnaOptions.cs">
<Link>Configuration\DlnaOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\ManualLoginCategory.cs">
<Link>Configuration\ManualLoginCategory.cs</Link>
</Compile>
@ -131,6 +134,9 @@
<Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
<Link>Dto\ItemIndex.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dto\RecommendationDto.cs">
<Link>Dto\RecommendationDto.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dto\StreamOptions.cs">
<Link>Dto\StreamOptions.cs</Link>
</Compile>

@ -67,6 +67,9 @@
<Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
<Link>Configuration\BaseApplicationConfiguration.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\DlnaOptions.cs">
<Link>Configuration\DlnaOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\ManualLoginCategory.cs">
<Link>Configuration\ManualLoginCategory.cs</Link>
</Compile>
@ -118,6 +121,9 @@
<Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
<Link>Dto\ItemIndex.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dto\RecommendationDto.cs">
<Link>Dto\RecommendationDto.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dto\StreamOptions.cs">
<Link>Dto\StreamOptions.cs</Link>
</Compile>

@ -244,7 +244,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="query">The query.</param>
/// <returns>Task{ItemsResult}.</returns>
Task<ItemsResult> GetSeasonsAsync(SeasonQuery query);
/// <summary>
/// Queries for items
/// </summary>
@ -346,7 +346,14 @@ namespace MediaBrowser.Model.ApiClient
/// </summary>
/// <param name="query">The query.</param>
/// <returns>Task{ItemsResult}.</returns>
Task<ItemsResult> GetNextUpAsync(NextUpQuery query);
Task<ItemsResult> GetNextUpEpisodesAsync(NextUpQuery query);
/// <summary>
/// Gets the upcoming episodes asynchronous.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>Task{ItemsResult}.</returns>
Task<ItemsResult> GetUpcomingEpisodesAsync(NextUpQuery query);
/// <summary>
/// Gets a genre
@ -772,7 +779,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="options">The options.</param>
/// <returns>System.String.</returns>
string GetImageUrl(ProgramInfoDto item, ImageOptions options);
/// <summary>
/// Gets an image url that can be used to download an image from the api
/// </summary>
@ -902,7 +909,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="options">The options.</param>
/// <returns>System.String.</returns>
string GetThumbImageUrl(BaseItemDto item, ImageOptions options);
/// <summary>
/// Gets the url needed to stream an audio file
/// </summary>
@ -958,7 +965,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{ChannelInfoDto}.</returns>
Task<ChannelInfoDto> GetLiveTvChannelAsync(string id, string userId, CancellationToken cancellationToken);
/// <summary>
/// Gets the live tv recordings asynchronous.
/// </summary>
@ -975,7 +982,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{RecordingInfoDto}.</returns>
Task<RecordingInfoDto> GetLiveTvRecordingAsync(string id, string userId, CancellationToken cancellationToken);
/// <summary>
/// Gets the live tv recording groups asynchronous.
/// </summary>
@ -992,7 +999,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{RecordingGroupDto}.</returns>
Task<RecordingGroupDto> GetLiveTvRecordingGroupAsync(string id, string userId, CancellationToken cancellationToken);
/// <summary>
/// Gets the live tv timers asynchronous.
/// </summary>
@ -1009,6 +1016,15 @@ namespace MediaBrowser.Model.ApiClient
/// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
Task<QueryResult<ProgramInfoDto>> GetLiveTvProgramsAsync(ProgramQuery query, CancellationToken cancellationToken);
/// <summary>
/// Gets the live tv program asynchronous.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="userId">The user identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{ProgramInfoDto}.</returns>
Task<ProgramInfoDto> GetLiveTvProgramAsync(string id, string userId, CancellationToken cancellationToken);
/// <summary>
/// Gets the recommended live tv programs asynchronous.
/// </summary>
@ -1016,7 +1032,39 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
Task<QueryResult<ProgramInfoDto>> GetRecommendedLiveTvProgramsAsync(RecommendedProgramQuery query, CancellationToken cancellationToken);
/// <summary>
/// Creates the live tv timer asynchronous.
/// </summary>
/// <param name="timer">The timer.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task CreateLiveTvTimerAsync(TimerInfoDto timer, CancellationToken cancellationToken);
/// <summary>
/// Updates the live tv timer asynchronous.
/// </summary>
/// <param name="timer">The timer.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task UpdateLiveTvTimerAsync(TimerInfoDto timer, CancellationToken cancellationToken);
/// <summary>
/// Creates the live tv series timer asynchronous.
/// </summary>
/// <param name="timer">The timer.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task CreateLiveTvSeriesTimerAsync(SeriesTimerInfoDto timer, CancellationToken cancellationToken);
/// <summary>
/// Updates the live tv series timer asynchronous.
/// </summary>
/// <param name="timer">The timer.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task UpdateLiveTvSeriesTimerAsync(SeriesTimerInfoDto timer, CancellationToken cancellationToken);
/// <summary>
/// Gets the live tv timer asynchronous.
/// </summary>
@ -1024,7 +1072,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{TimerInfoDto}.</returns>
Task<TimerInfoDto> GetLiveTvTimerAsync(string id, CancellationToken cancellationToken);
/// <summary>
/// Gets the live tv series timers asynchronous.
/// </summary>
@ -1056,7 +1104,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task CancelLiveTvSeriesTimerAsync(string id, CancellationToken cancellationToken);
/// <summary>
/// Deletes the live tv recording asynchronous.
/// </summary>
@ -1064,5 +1112,27 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task DeleteLiveTvRecordingAsync(string id, CancellationToken cancellationToken);
/// <summary>
/// Gets the default timer information.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{SeriesTimerInfoDto}.</returns>
Task<SeriesTimerInfoDto> GetDefaultLiveTvTimerInfo(CancellationToken cancellationToken);
/// <summary>
/// Gets the live tv guide information.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{GuideInfo}.</returns>
Task<GuideInfo> GetLiveTvGuideInfo(CancellationToken cancellationToken);
/// <summary>
/// Gets the default timer information.
/// </summary>
/// <param name="programId">The program identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{SeriesTimerInfoDto}.</returns>
Task<SeriesTimerInfoDto> GetDefaultLiveTvTimerInfo(string programId, CancellationToken cancellationToken);
}
}

@ -19,6 +19,8 @@ namespace MediaBrowser.Model.Configuration
public bool DeleteEmptyFolders { get; set; }
public bool CopyOriginalFile { get; set; }
public TvFileOrganizationOptions()
{
MinFileSizeMb = 50;
@ -31,6 +33,8 @@ namespace MediaBrowser.Model.Configuration
MultiEpisodeNamePattern = "%sn - %sx%0e-x%0ed - %en.%ext";
SeasonFolderPattern = "Season %s";
SeasonZeroFolderName = "Season 0";
CopyOriginalFile = false;
}
}
}

@ -0,0 +1,8 @@

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

@ -212,6 +212,8 @@ namespace MediaBrowser.Model.Configuration
public string ServerName { get; set; }
public string WanDdns { get; set; }
public DlnaOptions DlnaOptions { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
/// </summary>
@ -271,6 +273,8 @@ namespace MediaBrowser.Model.Configuration
};
MetadataOptions = options.ToArray();
DlnaOptions = new DlnaOptions();
}
}

@ -0,0 +1,29 @@

namespace MediaBrowser.Model.Dto
{
public class RecommendationDto
{
public BaseItemDto[] Items { get; set; }
public RecommendationType RecommendationType { get; set; }
public string BaselineItemName { get; set; }
public string CategoryId { get; set; }
}
public enum RecommendationType
{
SimilarToRecentlyPlayed = 0,
SimilarToLikedItem = 1,
HasDirectorFromRecentlyPlayed = 2,
HasActorFromRecentlyPlayed = 3,
HasLikedDirector = 4,
HasLikedActor = 5
}
}

@ -46,6 +46,30 @@ namespace MediaBrowser.Model.Entities
/// <value>The primary image tag.</value>
public Guid? PrimaryImageTag { get; set; }
/// <summary>
/// Gets or sets the thumb image tag.
/// </summary>
/// <value>The thumb image tag.</value>
public Guid? ThumbImageTag { get; set; }
/// <summary>
/// Gets or sets the thumb item identifier.
/// </summary>
/// <value>The thumb item identifier.</value>
public string ThumbItemId { get; set; }
/// <summary>
/// Gets or sets the thumb image tag.
/// </summary>
/// <value>The thumb image tag.</value>
public Guid? BackdropImageTag { get; set; }
/// <summary>
/// Gets or sets the thumb item identifier.
/// </summary>
/// <value>The thumb item identifier.</value>
public string BackdropItemId { get; set; }
/// <summary>
/// Gets a value indicating whether this instance has primary image.
/// </summary>

@ -60,6 +60,7 @@
<Compile Include="ApiClient\ServerEventArgs.cs" />
<Compile Include="Configuration\AutoOrganize.cs" />
<Compile Include="Configuration\BaseApplicationConfiguration.cs" />
<Compile Include="Configuration\DlnaOptions.cs" />
<Compile Include="Configuration\ManualLoginCategory.cs" />
<Compile Include="Configuration\MetadataPlugin.cs" />
<Compile Include="Configuration\MetadataOptions.cs" />
@ -73,6 +74,7 @@
<Compile Include="Dto\ItemByNameCounts.cs" />
<Compile Include="Dto\ItemCounts.cs" />
<Compile Include="Dto\ItemIndex.cs" />
<Compile Include="Dto\RecommendationDto.cs" />
<Compile Include="Entities\PackageReviewInfo.cs" />
<Compile Include="FileOrganization\FileOrganizationResult.cs" />
<Compile Include="FileOrganization\FileOrganizationQuery.cs" />

@ -218,6 +218,18 @@ namespace MediaBrowser.Model.Querying
/// <value>The name starts with or greater.</value>
public string NameStartsWithOrGreater { get; set; }
/// <summary>
/// Gets or sets the name starts with.
/// </summary>
/// <value>The name starts with or greater.</value>
public string NameStartsWith { get; set; }
/// <summary>
/// Gets or sets the name starts with.
/// </summary>
/// <value>The name lessthan.</value>
public string NameLessThan { get; set; }
/// <summary>
/// Gets or sets the album artist starts with or greater.
/// </summary>

@ -75,16 +75,12 @@ namespace MediaBrowser.Model.Querying
public const string IsFolder = "IsFolder";
public const string IsUnplayed = "IsUnplayed";
public const string IsPlayed = "IsPlayed";
public const string TrailerCount = "TrailerCount";
public const string MovieCount = "MovieCount";
public const string SeriesCount = "SeriesCount";
public const string EpisodeCount = "EpisodeCount";
public const string SongCount = "SongCount";
public const string AlbumCount = "AlbumCount";
public const string MusicVideoCount = "MusicVideoCount";
public const string SeriesSortName = "SeriesSortName";
public const string VideoBitRate = "VideoBitRate";
public const string AirTime = "AirTime";
public const string Metascore = "Metascore";
public const string Studio = "Studio";
public const string Players = "Players";
public const string GameSystem = "GameSystem";
}
}

@ -86,6 +86,10 @@ namespace MediaBrowser.Model.Querying
public string NameStartsWithOrGreater { get; set; }
/// <summary>
/// Gets or sets the name starts with
/// </summary>
/// <value>The name starts with or greater.</value>
public string NameStartsWith { get; set; }
/// Gets or sets the name less than.
/// </summary>
/// <value>The name less than.</value>

@ -33,4 +33,32 @@ namespace MediaBrowser.Model.Querying
/// <value>The fields.</value>
public ItemFields[] Fields { get; set; }
}
public class UpcomingEpisodesQuery
{
/// <summary>
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
public string UserId { get; set; }
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
/// </summary>
/// <value>The start index.</value>
public int? StartIndex { get; set; }
/// <summary>
/// The maximum number of items to return
/// </summary>
/// <value>The limit.</value>
public int? Limit { get; set; }
/// <summary>
/// Fields to return within the items, in addition to basic information
/// </summary>
/// <value>The fields.</value>
public ItemFields[] Fields { get; set; }
}
}

@ -33,6 +33,8 @@ namespace MediaBrowser.Model.Search
public bool IncludeStudios { get; set; }
public bool IncludeArtists { get; set; }
public string[] IncludeItemTypes { get; set; }
public SearchQuery()
{
IncludeArtists = true;
@ -40,6 +42,8 @@ namespace MediaBrowser.Model.Search
IncludeMedia = true;
IncludePeople = true;
IncludeStudios = true;
IncludeItemTypes = new string[] { };
}
}
}

@ -23,7 +23,7 @@ namespace MediaBrowser.Model.Session
/// </summary>
/// <value>The name of the item.</value>
public string ItemName { get; set; }
/// <summary>
/// Gets or sets the context (Movies, Music, Tv, etc)
/// Applicable to genres, studios and persons only because the context of items and artists can be inferred.
@ -40,4 +40,4 @@ namespace MediaBrowser.Model.Session
public const string TvShows = "TvShows";
public const string Games = "Games";
}
}
}

@ -4,9 +4,9 @@ namespace MediaBrowser.Model.Session
public class MessageCommand
{
public string Header { get; set; }
public string Text { get; set; }
public long? TimeoutMs { get; set; }
}
}
}

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

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

@ -23,7 +23,7 @@ namespace MediaBrowser.Providers.AdultVideos
new MovieXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
}
protected override FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
{
return MovieXmlProvider.GetXmlFileInfo(info, FileSystem);
}

@ -201,7 +201,7 @@ namespace MediaBrowser.Providers.All
PopulateBackdrops(images, files, imagePrefix, "background", "background-", ImageType.Backdrop);
PopulateBackdrops(images, files, imagePrefix, "art", "art-", ImageType.Backdrop);
var extraFanartFolder = files.OfType<DirectoryInfo>()
var extraFanartFolder = files
.FirstOrDefault(i => string.Equals(i.Name, "extrafanart", StringComparison.OrdinalIgnoreCase));
if (extraFanartFolder != null)

@ -59,7 +59,7 @@ namespace MediaBrowser.Providers
FileSystem = fileSystem;
}
protected abstract FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService);
protected abstract FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService);
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
{

@ -1,5 +1,6 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@ -37,6 +38,15 @@ namespace MediaBrowser.Providers.BoxSets
protected override void MergeData(BoxSet source, BoxSet target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
if (mergeMetadataSettings)
{
var list = source.LinkedChildren.ToList();
list.AddRange(target.LinkedChildren.Where(i => i.Type == LinkedChildType.Shortcut));
target.LinkedChildren = list;
}
}
protected override ItemUpdateType BeforeSave(BoxSet item)

@ -0,0 +1,129 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.Collections.Generic;
using System.Globalization;
using System.Xml;
namespace MediaBrowser.Providers.BoxSets
{
public class BoxSetXmlParser : BaseItemXmlParser<BoxSet>
{
private readonly CultureInfo UsCulture = new CultureInfo("en-US");
public BoxSetXmlParser(ILogger logger)
: base(logger)
{
}
protected override void FetchDataFromXmlNode(XmlReader reader, BoxSet item)
{
switch (reader.Name)
{
case "CollectionItems":
using (var subReader = reader.ReadSubtree())
{
FetchFromCollectionItemsNode(subReader, item);
}
break;
default:
base.FetchDataFromXmlNode(reader, item);
break;
}
}
private void FetchFromCollectionItemsNode(XmlReader reader, BoxSet item)
{
reader.MoveToContent();
var list = new List<LinkedChild>();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "CollectionItem":
{
using (var subReader = reader.ReadSubtree())
{
var child = GetLinkedChild(subReader);
if (child != null)
{
list.Add(child);
}
}
break;
}
default:
reader.Skip();
break;
}
}
}
item.LinkedChildren = list;
}
private LinkedChild GetLinkedChild(XmlReader reader)
{
reader.MoveToContent();
var linkedItem = new LinkedChild
{
Type = LinkedChildType.Manual
};
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Name":
{
linkedItem.ItemName = reader.ReadElementContentAsString();
break;
}
case "Type":
{
linkedItem.ItemType = reader.ReadElementContentAsString();
break;
}
case "Year":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
int rval;
if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval))
{
linkedItem.ItemYear = rval;
}
}
break;
}
default:
reader.Skip();
break;
}
}
}
return string.IsNullOrWhiteSpace(linkedItem.ItemName) || string.IsNullOrWhiteSpace(linkedItem.ItemType) ? null : linkedItem;
}
}
}

@ -22,10 +22,10 @@ namespace MediaBrowser.Providers.BoxSets
protected override void Fetch(LocalMetadataResult<BoxSet> result, string path, CancellationToken cancellationToken)
{
new BaseItemXmlParser<BoxSet>(_logger).Fetch(result.Item, path, cancellationToken);
new BoxSetXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
}
protected override FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
{
return directoryService.GetFile(Path.Combine(info.Path, "collection.xml"));
}

@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.Folders
new BaseItemXmlParser<Folder>(_logger).Fetch(result.Item, path, cancellationToken);
}
protected override FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
{
return new FileInfo(Path.Combine(info.Path, "folder.xml"));
}

@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Games
new GameSystemXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
}
protected override FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
{
return directoryService.GetFile(Path.Combine(info.Path, "gamesystem.xml"));
}

@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Games
new GameXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
}
protected override FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
{
var fileInfo = FileSystem.GetFileSystemInfo(info.Path);

@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.LiveTv
new BaseItemXmlParser<LiveTvChannel>(_logger).Fetch(result.Item, path, cancellationToken);
}
protected override FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
{
return directoryService.GetFile(Path.Combine(info.Path, "channel.xml"));
}

@ -305,22 +305,20 @@ namespace MediaBrowser.Providers.Manager
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.ImageUpdate;
}
if (!string.IsNullOrEmpty(localItem.Item.Name))
if (string.IsNullOrWhiteSpace(localItem.Item.Name))
{
MergeData(localItem.Item, temp, new List<MetadataFields>(), !options.ReplaceAllMetadata, true);
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;
// Only one local provider allowed per item
hasLocalMetadata = true;
break;
localItem.Item.Name = item.Name ?? Path.GetFileNameWithoutExtension(item.Path);
}
Logger.Error("Invalid local metadata found for: " + item.Path);
}
else
{
Logger.Debug("{0} returned no metadata for {1}", providerName, item.Path ?? item.Name);
MergeData(localItem.Item, temp, new List<MetadataFields>(), !options.ReplaceAllMetadata, true);
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;
// Only one local provider allowed per item
hasLocalMetadata = true;
break;
}
Logger.Debug("{0} returned no metadata for {1}", providerName, item.Path ?? item.Name);
}
catch (OperationCanceledException)
{

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

Loading…
Cancel
Save