Eric Reed 11 years ago
commit 6819be8160

@ -92,7 +92,7 @@ namespace MediaBrowser.Api
private readonly char[] _dashReplaceChars = new[] { '?', '/' };
private const char SlugChar = '-';
protected Artist GetArtist(string name, ILibraryManager libraryManager)
protected MusicArtist GetArtist(string name, ILibraryManager libraryManager)
{
return libraryManager.GetArtist(DeSlugArtistName(name, libraryManager));
}
@ -147,21 +147,7 @@ namespace MediaBrowser.Api
return name;
}
return libraryManager.RootFolder.GetRecursiveChildren()
.OfType<Audio>()
.SelectMany(i =>
{
var list = new List<string>();
if (!string.IsNullOrEmpty(i.AlbumArtist))
{
list.Add(i.AlbumArtist);
}
list.AddRange(i.Artists);
return list;
})
.Distinct(StringComparer.OrdinalIgnoreCase)
return libraryManager.GetAllArtists()
.FirstOrDefault(i =>
{
i = _dashReplaceChars.Aggregate(i, (current, c) => current.Replace(c, SlugChar));

@ -194,20 +194,7 @@ namespace MediaBrowser.Api.DefaultTheme
.Select(i => _dtoService.GetBaseItemDto(i, fields, user))
.ToList();
var artists = allItems.OfType<Audio>()
.SelectMany(i =>
{
var list = new List<string>();
if (!string.IsNullOrEmpty(i.AlbumArtist))
{
list.Add(i.AlbumArtist);
}
list.AddRange(i.Artists);
return list;
})
.Distinct(StringComparer.OrdinalIgnoreCase)
var artists = _libraryManager.GetAllArtists(allItems)
.Randomize()
.Select(i =>
{
@ -650,7 +637,7 @@ namespace MediaBrowser.Api.DefaultTheme
public static IEnumerable<T> Randomize<T>(this IEnumerable<T> sequence, string type = "none")
where T : BaseItem
{
var hour = DateTime.Now.Hour + 2;
var hour = DateTime.Now.Hour + DateTime.Now.Day + 2;
var typeCode = type.GetHashCode();

@ -0,0 +1,52 @@
using MediaBrowser.Model.Querying;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MediaBrowser.Api
{
/// <summary>
/// Interface IHasItemFields
/// </summary>
public interface IHasItemFields
{
/// <summary>
/// Gets or sets the fields.
/// </summary>
/// <value>The fields.</value>
string Fields { get; set; }
}
/// <summary>
/// Class ItemFieldsExtensions.
/// </summary>
public static class ItemFieldsExtensions
{
/// <summary>
/// Gets the item fields.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>IEnumerable{ItemFields}.</returns>
public static IEnumerable<ItemFields> GetItemFields(this IHasItemFields request)
{
var val = request.Fields;
if (string.IsNullOrEmpty(val))
{
return new ItemFields[] { };
}
return val.Split(',').Select(v =>
{
ItemFields value;
if (Enum.TryParse(v, true, out value))
{
return (ItemFields?)value;
}
return null;
}).Where(i => i.HasValue).Select(i => i.Value);
}
}
}

@ -5,6 +5,7 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing;
@ -37,6 +38,18 @@ namespace MediaBrowser.Api.Images
public string Id { get; set; }
}
[Route("/LiveTv/Channels/{Id}/Images", "GET")]
[Api(Description = "Gets information about an item's images")]
public class GetChannelImageInfos : IReturn<List<ImageInfo>>
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
[Route("/Artists/{Name}/Images", "GET")]
[Route("/Genres/{Name}/Images", "GET")]
[Route("/GameGenres/{Name}/Images", "GET")]
@ -67,6 +80,19 @@ namespace MediaBrowser.Api.Images
public string Id { get; set; }
}
[Route("/LiveTv/Channels/{Id}/Images/{Type}", "GET")]
[Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "GET")]
[Api(Description = "Gets an item image")]
public class GetChannelImage : ImageRequest
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
/// <summary>
/// Class UpdateItemImageIndex
/// </summary>
@ -243,6 +269,19 @@ namespace MediaBrowser.Api.Images
public Guid Id { get; set; }
}
[Route("/LiveTv/Channels/{Id}/Images/{Type}", "DELETE")]
[Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "DELETE")]
[Api(Description = "Deletes an item image")]
public class DeleteChannelImage : DeleteImageRequest, IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get; set; }
}
/// <summary>
/// Class PostUserImage
/// </summary>
@ -318,6 +357,25 @@ namespace MediaBrowser.Api.Images
public Stream RequestStream { get; set; }
}
[Route("/LiveTv/Channels/{Id}/Images/{Type}", "POST")]
[Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "POST")]
[Api(Description = "Posts an item image")]
public class PostChannelImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
/// <summary>
/// The raw Http Request Input Stream
/// </summary>
/// <value>The request stream.</value>
public Stream RequestStream { get; set; }
}
/// <summary>
/// Class ImageService
/// </summary>
@ -341,10 +399,12 @@ namespace MediaBrowser.Api.Images
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
private readonly ILiveTvManager _liveTv;
/// <summary>
/// Initializes a new instance of the <see cref="ImageService" /> class.
/// </summary>
public ImageService(IUserManager userManager, ILibraryManager libraryManager, IApplicationPaths appPaths, IProviderManager providerManager, IItemRepository itemRepo, IDtoService dtoService, IImageProcessor imageProcessor)
public ImageService(IUserManager userManager, ILibraryManager libraryManager, IApplicationPaths appPaths, IProviderManager providerManager, IItemRepository itemRepo, IDtoService dtoService, IImageProcessor imageProcessor, ILiveTvManager liveTv)
{
_userManager = userManager;
_libraryManager = libraryManager;
@ -353,6 +413,7 @@ namespace MediaBrowser.Api.Images
_itemRepo = itemRepo;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
_liveTv = liveTv;
}
/// <summary>
@ -369,6 +430,15 @@ namespace MediaBrowser.Api.Images
return ToOptimizedResult(result);
}
public object Get(GetChannelImageInfos request)
{
var item = _liveTv.GetChannel(request.Id);
var result = GetItemImageInfos(item);
return ToOptimizedResult(result);
}
public object Get(GetItemByNameImageInfos request)
{
var result = GetItemByNameImageInfos(request);
@ -484,7 +554,7 @@ namespace MediaBrowser.Api.Images
Height = Convert.ToInt32(size.Height)
};
}
catch (IOException ex)
catch (Exception ex)
{
Logger.ErrorException("Error getting image information for {0}", ex, path);
@ -492,6 +562,13 @@ namespace MediaBrowser.Api.Images
}
}
public object Get(GetChannelImage request)
{
var item = _liveTv.GetChannel(request.Id);
return GetImage(request, item);
}
/// <summary>
/// Gets the specified request.
/// </summary>
@ -577,6 +654,20 @@ namespace MediaBrowser.Api.Images
Task.WaitAll(task);
}
public void Post(PostChannelImage request)
{
var pathInfo = PathInfo.Parse(RequestContext.PathInfo);
var id = pathInfo.GetArgumentValue<string>(2);
request.Type = (ImageType)Enum.Parse(typeof(ImageType), pathInfo.GetArgumentValue<string>(4), true);
var item = _liveTv.GetChannel(id);
var task = PostImage(item, request.RequestStream, request.Type, RequestContext.ContentType);
Task.WaitAll(task);
}
/// <summary>
/// Deletes the specified request.
/// </summary>
@ -603,6 +694,15 @@ namespace MediaBrowser.Api.Images
Task.WaitAll(task);
}
public void Delete(DeleteChannelImage request)
{
var item = _liveTv.GetChannel(request.Id);
var task = item.DeleteImage(request.Type, request.Index);
Task.WaitAll(task);
}
/// <summary>
/// Deletes the specified request.
/// </summary>

@ -264,16 +264,14 @@ namespace MediaBrowser.Api
{
var item = _dtoService.GetItemByDtoId(request.Id);
var folder = item as Folder;
try
{
await item.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced).ConfigureAwait(false);
if (folder != null)
if (item.IsFolder)
{
// Collection folders don't validate their children so we'll have to simulate that here
var collectionFolder = folder as CollectionFolder;
var collectionFolder = item as CollectionFolder;
if (collectionFolder != null)
{
@ -281,6 +279,8 @@ namespace MediaBrowser.Api
}
else
{
var folder = (Folder)item;
await folder.ValidateChildren(new Progress<double>(), CancellationToken.None, request.Recursive, request.Forced).ConfigureAwait(false);
}
}
@ -303,10 +303,10 @@ namespace MediaBrowser.Api
{
await child.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced).ConfigureAwait(false);
var folder = child as Folder;
if (folder != null)
if (child.IsFolder)
{
var folder = (Folder)child;
await folder.ValidateChildren(new Progress<double>(), CancellationToken.None, request.Recursive, request.Forced).ConfigureAwait(false);
}
}

@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using ServiceStack.ServiceHost;
@ -13,6 +14,14 @@ using System.Threading.Tasks;
namespace MediaBrowser.Api
{
[Route("/LiveTv/Channels/{ChannelId}", "POST")]
[Api(("Updates an item"))]
public class UpdateChannel : BaseItemDto, IReturnVoid
{
[ApiMember(Name = "ChannelId", Description = "The id of the channel", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string ChannelId { get; set; }
}
[Route("/Items/{ItemId}", "POST")]
[Api(("Updates an item"))]
public class UpdateItem : BaseItemDto, IReturnVoid
@ -73,11 +82,13 @@ namespace MediaBrowser.Api
{
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
private readonly ILiveTvManager _liveTv;
public ItemUpdateService(ILibraryManager libraryManager, IDtoService dtoService)
public ItemUpdateService(ILibraryManager libraryManager, IDtoService dtoService, ILiveTvManager liveTv)
{
_libraryManager = libraryManager;
_dtoService = dtoService;
_liveTv = liveTv;
}
public void Post(UpdateItem request)
@ -87,13 +98,34 @@ namespace MediaBrowser.Api
Task.WaitAll(task);
}
private Task UpdateItem(UpdateItem request)
public void Post(UpdateChannel request)
{
var task = UpdateItem(request);
Task.WaitAll(task);
}
private async Task UpdateItem(UpdateItem request)
{
var item = _dtoService.GetItemByDtoId(request.ItemId);
var newEnableInternetProviders = request.EnableInternetProviders ?? true;
var dontFetchMetaChanged = item.DontFetchMeta != !newEnableInternetProviders;
UpdateItem(request, item);
return _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
if (dontFetchMetaChanged && item.IsFolder)
{
var folder = (Folder)item;
foreach (var child in folder.RecursiveChildren.ToList())
{
child.DontFetchMeta = !newEnableInternetProviders;
await _libraryManager.UpdateItem(child, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
}
}
public void Post(UpdatePerson request)
@ -112,6 +144,15 @@ namespace MediaBrowser.Api
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
private async Task UpdateItem(UpdateChannel request)
{
var item = _liveTv.GetChannel(request.Id);
UpdateItem(request, item);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
public void Post(UpdateArtist request)
{
var task = UpdateItem(request);
@ -126,15 +167,6 @@ namespace MediaBrowser.Api
UpdateItem(request, item);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
var musicArtist = Artist.FindMusicArtist(item, _libraryManager);
if (musicArtist != null)
{
UpdateItem(request, musicArtist);
await _libraryManager.UpdateItem(musicArtist, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
}
public void Post(UpdateStudio request)
@ -216,8 +248,12 @@ namespace MediaBrowser.Api
item.ForcedSortName = request.SortName;
}
item.Budget = request.Budget;
item.Revenue = request.Revenue;
var hasBudget = item as IHasBudget;
if (hasBudget != null)
{
hasBudget.Budget = request.Budget;
hasBudget.Revenue = request.Revenue;
}
var hasCriticRating = item as IHasCriticRating;
if (hasCriticRating != null)
@ -235,8 +271,16 @@ namespace MediaBrowser.Api
item.Overview = request.Overview;
item.Genres = request.Genres;
item.Tags = request.Tags;
item.Studios = request.Studios.Select(x => x.Name).ToList();
item.People = request.People.Select(x => new PersonInfo { Name = x.Name, Role = x.Role, Type = x.Type }).ToList();
if (request.Studios != null)
{
item.Studios = request.Studios.Select(x => x.Name).ToList();
}
if (request.People != null)
{
item.People = request.People.Select(x => new PersonInfo { Name = x.Name, Role = x.Role, Type = x.Type }).ToList();
}
if (request.DateCreated.HasValue)
{
@ -247,11 +291,16 @@ namespace MediaBrowser.Api
item.PremiereDate = request.PremiereDate.HasValue ? request.PremiereDate.Value.ToUniversalTime() : (DateTime?)null;
item.ProductionYear = request.ProductionYear;
item.ProductionLocations = request.ProductionLocations;
item.AspectRatio = request.AspectRatio;
item.Language = request.Language;
item.OfficialRating = request.OfficialRating;
item.CustomRating = request.CustomRating;
var hasAspectRatio = item as IHasAspectRatio;
if (hasAspectRatio != null)
{
hasAspectRatio.AspectRatio = request.AspectRatio;
}
item.DontFetchMeta = !(request.EnableInternetProviders ?? true);
if (request.EnableInternetProviders ?? true)
{

@ -286,7 +286,12 @@ namespace MediaBrowser.Api.Library
}
finally
{
_directoryWatchers.Start();
// No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary)
{
_directoryWatchers.Start();
}
_directoryWatchers.RemoveTempIgnore(virtualFolderPath);
}
@ -353,7 +358,12 @@ namespace MediaBrowser.Api.Library
}
finally
{
_directoryWatchers.Start();
// No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary)
{
_directoryWatchers.Start();
}
_directoryWatchers.RemoveTempIgnore(currentPath);
_directoryWatchers.RemoveTempIgnore(newPath);
}
@ -404,7 +414,12 @@ namespace MediaBrowser.Api.Library
}
finally
{
_directoryWatchers.Start();
// No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary)
{
_directoryWatchers.Start();
}
_directoryWatchers.RemoveTempIgnore(path);
}
@ -442,7 +457,11 @@ namespace MediaBrowser.Api.Library
}
finally
{
_directoryWatchers.Start();
// No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary)
{
_directoryWatchers.Start();
}
}
if (request.RefreshLibrary)
@ -479,7 +498,11 @@ namespace MediaBrowser.Api.Library
}
finally
{
_directoryWatchers.Start();
// No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary)
{
_directoryWatchers.Start();
}
}
if (request.RefreshLibrary)

@ -37,7 +37,7 @@ namespace MediaBrowser.Api
/// </summary>
[Route("/Items/{Id}/CriticReviews", "GET")]
[Api(Description = "Gets critic reviews for an item")]
public class GetCriticReviews : IReturn<ItemReviewsResult>
public class GetCriticReviews : IReturn<QueryResult<ItemReview>>
{
/// <summary>
/// Gets or sets the id.
@ -367,7 +367,7 @@ namespace MediaBrowser.Api
BoxSetCount = boxsets.Count,
BookCount = books.Count,
UniqueTypes = items.Select(i => i.GetType().Name).Distinct().ToList()
UniqueTypes = items.Select(i => i.GetClientTypeName()).Distinct().ToList()
};
var people = items.SelectMany(i => i.People)
@ -390,19 +390,7 @@ namespace MediaBrowser.Api
people = request.UserId.HasValue ? FilterItems(people, request, request.UserId.Value).ToList() : people;
counts.PersonCount = people.Count;
var artists = items.OfType<Audio>().SelectMany(i =>
{
var list = new List<string>();
if (!string.IsNullOrEmpty(i.AlbumArtist))
{
list.Add(i.AlbumArtist);
}
list.AddRange(i.Artists);
return list;
})
.Distinct(StringComparer.OrdinalIgnoreCase)
var artists = _libraryManager.GetAllArtists(items)
.Select(i =>
{
try
@ -477,13 +465,16 @@ namespace MediaBrowser.Api
if (item.LocationType == LocationType.FileSystem)
{
if (Directory.Exists(item.Path))
{
Directory.Delete(item.Path, true);
}
else if (File.Exists(item.Path))
foreach (var path in item.GetDeletePaths().ToList())
{
File.Delete(item.Path);
if (Directory.Exists(path))
{
Directory.Delete(path, true);
}
else if (File.Exists(path))
{
File.Delete(path);
}
}
if (parent != null)
@ -521,16 +512,16 @@ namespace MediaBrowser.Api
/// </summary>
/// <param name="request">The request.</param>
/// <returns>Task{ItemReviewsResult}.</returns>
private ItemReviewsResult GetCriticReviews(GetCriticReviews request)
private QueryResult<ItemReview> GetCriticReviews(GetCriticReviews request)
{
var reviews = _itemRepo.GetCriticReviews(new Guid(request.Id));
var reviewsArray = reviews.ToArray();
var result = new ItemReviewsResult
{
TotalRecordCount = reviewsArray.Length
};
var result = new QueryResult<ItemReview>
{
TotalRecordCount = reviewsArray.Length
};
if (request.StartIndex.HasValue)
{
@ -541,7 +532,7 @@ namespace MediaBrowser.Api
reviewsArray = reviewsArray.Take(request.Limit.Value).ToArray();
}
result.ItemReviews = reviewsArray;
result.Items = reviewsArray;
return result;
}
@ -681,6 +672,11 @@ namespace MediaBrowser.Api
{
var album = originalItem as MusicAlbum;
if (album == null)
{
album = originalItem.Parents.OfType<MusicAlbum>().FirstOrDefault();
}
if (album != null)
{
var linkedItemWithThemes = album.SoundtrackIds
@ -744,17 +740,12 @@ namespace MediaBrowser.Api
: (Folder)_libraryManager.RootFolder)
: _dtoService.GetItemByDtoId(id, userId);
while (GetSoundtrackSongIds(item).Count == 0 && inheritFromParent && item.Parent != null)
{
item = item.Parent;
}
// Get everything
var fields = Enum.GetNames(typeof(ItemFields))
.Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
.ToList();
var dtos = GetSoundtrackSongIds(item)
var dtos = GetSoundtrackSongIds(item, inheritFromParent)
.Select(_libraryManager.GetItemById)
.OfType<MusicAlbum>()
.SelectMany(i => i.RecursiveChildren)
@ -772,7 +763,7 @@ namespace MediaBrowser.Api
};
}
private List<Guid> GetSoundtrackSongIds(BaseItem item)
private IEnumerable<Guid> GetSoundtrackSongIds(BaseItem item, bool inherit)
{
var hasSoundtracks = item as IHasSoundtracks;
@ -781,7 +772,14 @@ namespace MediaBrowser.Api
return hasSoundtracks.SoundtrackIds;
}
return new List<Guid>();
if (!inherit)
{
return null;
}
hasSoundtracks = item.Parents.OfType<IHasSoundtracks>().FirstOrDefault();
return hasSoundtracks != null ? hasSoundtracks.SoundtrackIds : new List<Guid>();
}
}
}

@ -1,6 +1,8 @@
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying;
using ServiceStack.ServiceHost;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@ -18,31 +20,101 @@ namespace MediaBrowser.Api.LiveTv
[Route("/LiveTv/Channels", "GET")]
[Api(Description = "Gets available live tv channels.")]
public class GetChannels : IReturn<List<ChannelInfoDto>>
public class GetChannels : IReturn<QueryResult<ChannelInfoDto>>
{
[ApiMember(Name = "ServiceName", Description = "Optional filter by service.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ServiceName { get; set; }
[ApiMember(Name = "Type", Description = "Optional filter by channel type.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public ChannelType? Type { get; set; }
[ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string UserId { get; set; }
}
[Route("/LiveTv/Channels/{Id}", "GET")]
[Api(Description = "Gets a live tv channel")]
public class GetChannel : IReturn<ChannelInfoDto>
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
[ApiMember(Name = "UserId", Description = "Optional user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string UserId { get; set; }
}
[Route("/LiveTv/Recordings", "GET")]
[Api(Description = "Gets available live tv recordings.")]
public class GetRecordings : IReturn<List<RecordingInfo>>
[Api(Description = "Gets live tv recordings")]
public class GetRecordings : IReturn<QueryResult<RecordingInfoDto>>
{
[ApiMember(Name = "ServiceName", Description = "Optional filter by service.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ServiceName { get; set; }
[ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ChannelId { get; set; }
}
[Route("/LiveTv/Guide", "GET")]
[Route("/LiveTv/Recordings/{Id}", "GET")]
[Api(Description = "Gets a live tv recording")]
public class GetRecording : IReturn<RecordingInfoDto>
{
[ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
[Route("/LiveTv/Timers/{Id}", "GET")]
[Api(Description = "Gets a live tv timer")]
public class GetTimer : IReturn<TimerInfoDto>
{
[ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
[Route("/LiveTv/Timers", "GET")]
[Api(Description = "Gets live tv timers")]
public class GetTimers : IReturn<QueryResult<TimerInfoDto>>
{
[ApiMember(Name = "ServiceName", Description = "Optional filter by service.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ServiceName { get; set; }
[ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ChannelId { get; set; }
}
[Route("/LiveTv/Programs", "GET")]
[Api(Description = "Gets available live tv epgs..")]
public class GetGuide : IReturn<List<ChannelGuide>>
public class GetPrograms : IReturn<QueryResult<ProgramInfoDto>>
{
[ApiMember(Name = "ServiceName", Description = "Live tv service name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
[ApiMember(Name = "ServiceName", Description = "Live tv service name", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ServiceName { get; set; }
[ApiMember(Name = "ChannelIds", Description = "The channels to return guide information for.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
[ApiMember(Name = "ChannelIds", Description = "The channels to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ChannelIds { get; set; }
[ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string UserId { get; set; }
}
[Route("/LiveTv/Recordings/{Id}", "DELETE")]
[Api(Description = "Deletes a live tv recording")]
public class DeleteRecording : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
[Route("/LiveTv/Timers/{Id}", "DELETE")]
[Api(Description = "Cancels a live tv timer")]
public class CancelTimer : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
public class LiveTvService : BaseApiService
{
private readonly ILiveTvManager _liveTvManager;
@ -58,7 +130,7 @@ namespace MediaBrowser.Api.LiveTv
if (!string.IsNullOrEmpty(serviceName))
{
services = services.Where(i => string.Equals(i.Name, serviceName, System.StringComparison.OrdinalIgnoreCase));
services = services.Where(i => string.Equals(i.Name, serviceName, StringComparison.OrdinalIgnoreCase));
}
return services;
@ -83,62 +155,87 @@ namespace MediaBrowser.Api.LiveTv
public object Get(GetChannels request)
{
var result = GetChannelsAsync(request).Result;
var result = _liveTvManager.GetChannels(new ChannelQuery
{
ChannelType = request.Type,
ServiceName = request.ServiceName,
UserId = request.UserId
return ToOptimizedResult(result.ToList());
});
return ToOptimizedResult(result);
}
private async Task<IEnumerable<ChannelInfoDto>> GetChannelsAsync(GetChannels request)
public object Get(GetChannel request)
{
var services = GetServices(request.ServiceName);
var result = _liveTvManager.GetChannelInfoDto(request.Id, request.UserId);
return ToOptimizedResult(result);
}
var tasks = services.Select(i => i.GetChannelsAsync(CancellationToken.None));
public object Get(GetPrograms request)
{
var result = _liveTvManager.GetPrograms(new ProgramQuery
{
ServiceName = request.ServiceName,
ChannelIdList = (request.ChannelIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToArray(),
UserId = request.UserId
var channelLists = await Task.WhenAll(tasks).ConfigureAwait(false);
}, CancellationToken.None).Result;
// Aggregate all channels from all services
return channelLists.SelectMany(i => i)
.Select(_liveTvManager.GetChannelInfoDto);
return ToOptimizedResult(result);
}
public object Get(GetRecordings request)
{
var result = GetRecordingsAsync(request).Result;
var result = _liveTvManager.GetRecordings(new RecordingQuery
{
ChannelId = request.ChannelId,
ServiceName = request.ServiceName
return ToOptimizedResult(result.ToList());
}, CancellationToken.None).Result;
return ToOptimizedResult(result);
}
private async Task<IEnumerable<RecordingInfo>> GetRecordingsAsync(GetRecordings request)
public object Get(GetRecording request)
{
var services = GetServices(request.ServiceName);
var query = new RecordingQuery
{
};
var result = _liveTvManager.GetRecording(request.Id, CancellationToken.None).Result;
var tasks = services.Select(i => i.GetRecordingsAsync(query, CancellationToken.None));
return ToOptimizedResult(result);
}
var recordings = await Task.WhenAll(tasks).ConfigureAwait(false);
public object Get(GetTimer request)
{
var result = _liveTvManager.GetTimer(request.Id, CancellationToken.None).Result;
return recordings.SelectMany(i => i);
return ToOptimizedResult(result);
}
public object Get(GetGuide request)
public object Get(GetTimers request)
{
var result = GetGuideAsync(request).Result;
var result = _liveTvManager.GetTimers(new TimerQuery
{
ChannelId = request.ChannelId,
ServiceName = request.ServiceName
}, CancellationToken.None).Result;
return ToOptimizedResult(result);
}
private async Task<IEnumerable<ChannelGuide>> GetGuideAsync(GetGuide request)
public void Delete(DeleteRecording request)
{
var service = GetServices(request.ServiceName)
.First();
var task = _liveTvManager.DeleteRecording(request.Id);
var channels = request.ChannelIds.Split(',');
Task.WaitAll(task);
}
public void Delete(CancelTimer request)
{
var task = _liveTvManager.CancelTimer(request.Id);
return await service.GetChannelGuidesAsync(channels, CancellationToken.None).ConfigureAwait(false);
Task.WaitAll(task);
}
}
}

@ -38,6 +38,18 @@
<RunPostBuildEvent>Always</RunPostBuildEvent>
</PropertyGroup>
<ItemGroup>
<Reference Include="ServiceStack.Common, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Interfaces, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
@ -47,15 +59,6 @@
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Common">
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Interfaces">
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text">
<HintPath>..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll</HintPath>
</Reference>
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
@ -70,6 +73,7 @@
<Compile Include="EnvironmentService.cs" />
<Compile Include="AuthorizationRequestFilterAttribute.cs" />
<Compile Include="GamesService.cs" />
<Compile Include="IHasItemFields.cs" />
<Compile Include="Images\ImageByNameService.cs" />
<Compile Include="Images\ImageRequest.cs" />
<Compile Include="Images\ImageService.cs" />

@ -1,5 +1,4 @@
using System.Globalization;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.MediaInfo;
using MediaBrowser.Controller;
@ -11,15 +10,15 @@ using MediaBrowser.Controller.MediaInfo;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
namespace MediaBrowser.Api.Playback
{
@ -696,7 +695,7 @@ namespace MediaBrowser.Api.Playback
// This is arbitrary, but add a little buffer time when internet streaming
if (state.Item.LocationType == LocationType.Remote)
{
await Task.Delay(2000).ConfigureAwait(false);
await Task.Delay(4000).ConfigureAwait(false);
}
}

@ -88,7 +88,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
var volParam = string.Empty;
var AudioSampleRate = string.Empty;
var audioSampleRate = string.Empty;
// Boost volume to 200% when downsampling from 6ch to 2ch
if (channels.HasValue && channels.Value <= 2 && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5)
@ -98,10 +98,10 @@ namespace MediaBrowser.Api.Playback.Hls
if (state.Request.AudioSampleRate.HasValue)
{
AudioSampleRate= state.Request.AudioSampleRate.Value + ":";
audioSampleRate= state.Request.AudioSampleRate.Value + ":";
}
args += string.Format(" -af \"adelay=1,aresample={0}async=1000{1}\"",AudioSampleRate, volParam);
args += string.Format(" -af \"adelay=1,aresample={0}async=1000{1}\"",audioSampleRate, volParam);
return args;
}
@ -127,6 +127,10 @@ namespace MediaBrowser.Api.Playback.Hls
const string keyFrameArg = " -force_key_frames expr:if(isnan(prev_forced_t),gte(t,.1),gte(t,prev_forced_t+5))";
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsExternal &&
(state.SubtitleStream.Codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) != -1 ||
state.SubtitleStream.Codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1);
var args = "-codec:v:0 " + codec + " -preset superfast" + keyFrameArg;
var bitrate = GetVideoBitrateParam(state);
@ -137,9 +141,12 @@ namespace MediaBrowser.Api.Playback.Hls
}
// Add resolution params, if specified
if (state.VideoRequest.Width.HasValue || state.VideoRequest.Height.HasValue || state.VideoRequest.MaxHeight.HasValue || state.VideoRequest.MaxWidth.HasValue)
if (!hasGraphicalSubs)
{
args += GetOutputSizeParam(state, codec, performSubtitleConversion);
if (state.VideoRequest.Width.HasValue || state.VideoRequest.Height.HasValue || state.VideoRequest.MaxHeight.HasValue || state.VideoRequest.MaxWidth.HasValue)
{
args += GetOutputSizeParam(state, codec, performSubtitleConversion);
}
}
if (state.VideoRequest.Framerate.HasValue)
@ -158,14 +165,11 @@ namespace MediaBrowser.Api.Playback.Hls
{
args += " -level " + state.VideoRequest.Level;
}
if (state.SubtitleStream != null)
// This is for internal graphical subs
if (hasGraphicalSubs)
{
// This is for internal graphical subs
if (!state.SubtitleStream.IsExternal && (state.SubtitleStream.Codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) != -1 || state.SubtitleStream.Codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1))
{
args += GetInternalGraphicalSubtitleParam(state, codec);
}
args += GetInternalGraphicalSubtitleParam(state, codec);
}
return args;

@ -127,44 +127,44 @@ namespace MediaBrowser.Api.Playback.Progressive
const string dlnaflags = ";DLNA.ORG_FLAGS=01500000000000000000000000000000";
if (string.Equals(extension, ".mp3", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MP3";
}
else if (string.Equals(extension, ".aac", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=AAC_ISO";
}
else if (string.Equals(extension, ".wma", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=WMABASE";
}
else if (string.Equals(extension, ".avi", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=AVI";
}
else if (string.Equals(extension, ".mp4", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MPEG4_P2_SP_AAC";
}
else if (string.Equals(extension, ".mpeg", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
}
else if (string.Equals(extension, ".wmv", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE";
}
else if (string.Equals(extension, ".asf", StringComparison.OrdinalIgnoreCase))
{
// ??
contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE";
}
else if (string.Equals(extension, ".mkv", StringComparison.OrdinalIgnoreCase))
{
// ??
contentFeatures = "";
}
//if (string.Equals(extension, ".mp3", StringComparison.OrdinalIgnoreCase))
//{
// contentFeatures = "DLNA.ORG_PN=MP3";
//}
//else if (string.Equals(extension, ".aac", StringComparison.OrdinalIgnoreCase))
//{
// contentFeatures = "DLNA.ORG_PN=AAC_ISO";
//}
//else if (string.Equals(extension, ".wma", StringComparison.OrdinalIgnoreCase))
//{
// contentFeatures = "DLNA.ORG_PN=WMABASE";
//}
//else if (string.Equals(extension, ".avi", StringComparison.OrdinalIgnoreCase))
//{
// contentFeatures = "DLNA.ORG_PN=AVI";
//}
//else if (string.Equals(extension, ".mp4", StringComparison.OrdinalIgnoreCase))
//{
// contentFeatures = "DLNA.ORG_PN=MPEG4_P2_SP_AAC";
//}
//else if (string.Equals(extension, ".mpeg", StringComparison.OrdinalIgnoreCase))
//{
// contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
//}
//else if (string.Equals(extension, ".wmv", StringComparison.OrdinalIgnoreCase))
//{
// contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE";
//}
//else if (string.Equals(extension, ".asf", StringComparison.OrdinalIgnoreCase))
//{
// // ??
// contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE";
//}
//else if (string.Equals(extension, ".mkv", StringComparison.OrdinalIgnoreCase))
//{
// // ??
// contentFeatures = "";
//}
if (!string.IsNullOrEmpty(contentFeatures))
{
@ -206,10 +206,10 @@ namespace MediaBrowser.Api.Playback.Progressive
var outputPath = GetOutputFilePath(state);
var outputPathExists = File.Exists(outputPath);
//var isStatic = request.Static ||
// (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive));
var isStatic = request.Static ||
(outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive));
//AddDlnaHeaders(state, responseHeaders, isStatic);
AddDlnaHeaders(state, responseHeaders, isStatic);
if (request.Static)
{
@ -307,7 +307,7 @@ namespace MediaBrowser.Api.Playback.Progressive
}
}
return new ImageService(UserManager, LibraryManager, ApplicationPaths, null, ItemRepository, DtoService, ImageProcessor)
return new ImageService(UserManager, LibraryManager, ApplicationPaths, null, ItemRepository, DtoService, ImageProcessor, null)
{
Logger = Logger,
RequestContext = RequestContext,

@ -143,12 +143,19 @@ namespace MediaBrowser.Api.Playback.Progressive
args += keyFrameArg;
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsExternal &&
(state.SubtitleStream.Codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) != -1 ||
state.SubtitleStream.Codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1);
var request = state.VideoRequest;
// Add resolution params, if specified
if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
if (!hasGraphicalSubs)
{
args += GetOutputSizeParam(state, codec, performSubtitleConversion);
if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
{
args += GetOutputSizeParam(state, codec, performSubtitleConversion);
}
}
if (request.Framerate.HasValue)
@ -175,13 +182,10 @@ namespace MediaBrowser.Api.Playback.Progressive
args += " -level " + state.VideoRequest.Level;
}
if (state.SubtitleStream != null)
// This is for internal graphical subs
if (hasGraphicalSubs)
{
// This is for internal graphical subs
if (!state.SubtitleStream.IsExternal && (state.SubtitleStream.Codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) != -1 || state.SubtitleStream.Codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1))
{
args += GetInternalGraphicalSubtitleParam(state, codec);
}
args += GetInternalGraphicalSubtitleParam(state, codec);
}
return args;

@ -31,7 +31,8 @@ namespace MediaBrowser.Api.ScheduledTasks
[Api(Description = "Gets scheduled tasks")]
public class GetScheduledTasks : IReturn<List<TaskInfo>>
{
[ApiMember(Name = "IsHidden", Description = "Optional filter tasks that are hidden, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsHidden { get; set; }
}
/// <summary>
@ -112,10 +113,33 @@ namespace MediaBrowser.Api.ScheduledTasks
/// <returns>IEnumerable{TaskInfo}.</returns>
public object Get(GetScheduledTasks request)
{
var result = TaskManager.ScheduledTasks.OrderBy(i => i.Name)
.Select(ScheduledTaskHelpers.GetTaskInfo).ToList();
IEnumerable<IScheduledTaskWorker> result = TaskManager.ScheduledTasks
.OrderBy(i => i.Name);
return ToOptimizedResult(result);
if (request.IsHidden.HasValue)
{
var val = request.IsHidden.Value;
result = result.Where(i =>
{
var isHidden = false;
var configurableTask = i.ScheduledTask as IConfigurableScheduledTask;
if (configurableTask != null)
{
isHidden = configurableTask.IsHidden;
}
return isHidden == val;
});
}
var infos = result
.Select(ScheduledTaskHelpers.GetTaskInfo)
.ToList();
return ToOptimizedResult(infos);
}
/// <summary>

@ -46,8 +46,10 @@ namespace MediaBrowser.Api.ScheduledTasks
/// <returns>Task{IEnumerable{TaskInfo}}.</returns>
protected override Task<IEnumerable<TaskInfo>> GetDataToSend(object state)
{
return Task.FromResult(TaskManager.ScheduledTasks.OrderBy(i => i.Name)
.Select(ScheduledTaskHelpers.GetTaskInfo));
return Task.FromResult(TaskManager.ScheduledTasks
.OrderBy(i => i.Name)
.Select(ScheduledTaskHelpers.GetTaskInfo)
.Where(i => !i.IsHidden));
}
}
}

@ -144,7 +144,7 @@ namespace MediaBrowser.Api
IndexNumber = item.IndexNumber,
ParentIndexNumber = item.ParentIndexNumber,
ItemId = _dtoService.GetDtoId(item),
Type = item.GetType().Name,
Type = item.GetClientTypeName(),
MediaType = item.MediaType,
MatchedTerm = hintInfo.MatchedTerm,
DisplayMediaType = item.DisplayMediaType,
@ -187,9 +187,7 @@ namespace MediaBrowser.Api
result.SongCount = songs.Count;
result.Artists = songs
.SelectMany(i => i.Artists)
.Distinct(StringComparer.OrdinalIgnoreCase)
result.Artists = _libraryManager.GetAllArtists(songs)
.ToArray();
result.AlbumArtist = songs.Select(i => i.AlbumArtist).FirstOrDefault(i => !string.IsNullOrEmpty(i));

@ -25,7 +25,7 @@ namespace MediaBrowser.Api
public string Id { get; set; }
}
public class BaseGetSimilarItems : IReturn<ItemsResult>
public class BaseGetSimilarItems : IReturn<ItemsResult>, IHasItemFields
{
/// <summary>
/// Gets or sets the user id.
@ -47,32 +47,6 @@ namespace MediaBrowser.Api
/// <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; }
/// <summary>
/// Gets the item fields.
/// </summary>
/// <returns>IEnumerable{ItemFields}.</returns>
public IEnumerable<ItemFields> GetItemFields()
{
var val = Fields;
if (string.IsNullOrEmpty(val))
{
return new ItemFields[] { };
}
return val.Split(',').Select(v =>
{
ItemFields value;
if (Enum.TryParse(v, true, out value))
{
return (ItemFields?)value;
}
return null;
}).Where(i => i.HasValue).Select(i => i.Value);
}
}
/// <summary>

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Dto;
using MediaBrowser.Api.UserLibrary;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@ -17,7 +18,7 @@ namespace MediaBrowser.Api
/// </summary>
[Route("/Shows/NextUp", "GET")]
[Api(("Gets a list of currently installed plugins"))]
public class GetNextUpEpisodes : IReturn<ItemsResult>
public class GetNextUpEpisodes : IReturn<ItemsResult>, IHasItemFields
{
/// <summary>
/// Gets or sets the user id.
@ -49,38 +50,83 @@ namespace MediaBrowser.Api
[ApiMember(Name = "SeriesId", Description = "Optional. Filter by series id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string SeriesId { get; set; }
}
[Route("/Shows/{Id}/Similar", "GET")]
[Api(Description = "Finds tv shows similar to a given one.")]
public class GetSimilarShows : BaseGetSimilarItemsFromItem
{
}
[Route("/Shows/{Id}/Episodes", "GET")]
[Api(Description = "Gets episodes for a tv season")]
public class GetEpisodes : IReturn<ItemsResult>, IHasItemFields
{
/// <summary>
/// Gets the item fields.
/// Gets or sets the user id.
/// </summary>
/// <returns>IEnumerable{ItemFields}.</returns>
public IEnumerable<ItemFields> GetItemFields()
{
var val = Fields;
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public Guid UserId { get; set; }
if (string.IsNullOrEmpty(val))
{
return new ItemFields[] { };
}
/// <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; }
return val.Split(',').Select(v =>
{
ItemFields value;
[ApiMember(Name = "Id", Description = "The series id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public Guid Id { get; set; }
if (Enum.TryParse(v, true, out value))
{
return (ItemFields?)value;
}
return null;
[ApiMember(Name = "Season", Description = "Optional filter by season number.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public int? Season { get; set; }
}).Where(i => i.HasValue).Select(i => i.Value);
}
[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; }
[ApiMember(Name = "IsVirtualUnaired", Description = "Optional filter by items that are virtual unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsVirtualUnaired { get; set; }
[ApiMember(Name = "AdjacentTo", Description = "Optional. Return items that are siblings of a supplied item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string AdjacentTo { get; set; }
}
[Route("/Shows/{Id}/Similar", "GET")]
[Api(Description = "Finds tv shows similar to a given one.")]
public class GetSimilarShows : BaseGetSimilarItemsFromItem
[Route("/Shows/{Id}/Seasons", "GET")]
[Api(Description = "Gets seasons for a tv series")]
public class GetSeasons : 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>
/// 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; }
[ApiMember(Name = "Id", Description = "The series id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public Guid Id { get; set; }
[ApiMember(Name = "IsSpecialSeason", Description = "Optional. Filter by special season.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsSpecialSeason { 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; }
[ApiMember(Name = "IsVirtualUnaired", Description = "Optional filter by items that are virtual unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsVirtualUnaired { get; set; }
[ApiMember(Name = "AdjacentTo", Description = "Optional. Return items that are siblings of a supplied item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string AdjacentTo { get; set; }
}
/// <summary>
@ -311,5 +357,196 @@ namespace MediaBrowser.Api
return items;
}
public object Get(GetSeasons request)
{
var user = _userManager.GetUserById(request.UserId);
var series = _libraryManager.GetItemById(request.Id) as Series;
var fields = request.GetItemFields().ToList();
var seasons = series.GetChildren(user, true)
.OfType<Season>();
var sortOrder = ItemSortBy.SortName;
if (request.IsSpecialSeason.HasValue)
{
var val = request.IsSpecialSeason.Value;
seasons = seasons.Where(i => i.IsSpecialSeason == val);
}
var config = user.Configuration;
if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes)
{
seasons = seasons.Where(i => !i.IsMissingOrVirtualUnaired);
}
else
{
if (!config.DisplayMissingEpisodes)
{
seasons = seasons.Where(i => !i.IsMissingSeason);
}
if (!config.DisplayUnairedEpisodes)
{
seasons = seasons.Where(i => !i.IsVirtualUnaired);
}
}
seasons = FilterVirtualSeasons(request, seasons);
seasons = _libraryManager.Sort(seasons, user, new[] { sortOrder }, SortOrder.Ascending)
.Cast<Season>();
// This must be the last filter
if (!string.IsNullOrEmpty(request.AdjacentTo))
{
seasons = ItemsService.FilterForAdjacency(seasons, request.AdjacentTo)
.Cast<Season>();
}
var returnItems = seasons.Select(i => _dtoService.GetBaseItemDto(i, fields, user))
.ToArray();
return new ItemsResult
{
TotalRecordCount = returnItems.Length,
Items = returnItems
};
}
private IEnumerable<Season> FilterVirtualSeasons(GetSeasons request, IEnumerable<Season> items)
{
if (request.IsMissing.HasValue && request.IsVirtualUnaired.HasValue)
{
var isMissing = request.IsMissing.Value;
var isVirtualUnaired = request.IsVirtualUnaired.Value;
if (!isMissing && !isVirtualUnaired)
{
return items.Where(i => !i.IsMissingOrVirtualUnaired);
}
}
if (request.IsMissing.HasValue)
{
var val = request.IsMissing.Value;
items = items.Where(i => i.IsMissingSeason == val);
}
if (request.IsVirtualUnaired.HasValue)
{
var val = request.IsVirtualUnaired.Value;
items = items.Where(i => i.IsVirtualUnaired == val);
}
return items;
}
public object Get(GetEpisodes request)
{
var user = _userManager.GetUserById(request.UserId);
var series = _libraryManager.GetItemById(request.Id) as Series;
var fields = request.GetItemFields().ToList();
var episodes = series.GetRecursiveChildren(user)
.OfType<Episode>();
var sortOrder = ItemSortBy.SortName;
if (!string.IsNullOrEmpty(request.SeasonId))
{
var season = _libraryManager.GetItemById(new Guid(request.SeasonId)) as Season;
if (season.IndexNumber.HasValue)
{
episodes = FilterEpisodesBySeason(episodes, season.IndexNumber.Value, true);
sortOrder = ItemSortBy.AiredEpisodeOrder;
}
else
{
episodes = season.RecursiveChildren.OfType<Episode>();
sortOrder = ItemSortBy.SortName;
}
}
else if (request.Season.HasValue)
{
episodes = FilterEpisodesBySeason(episodes, request.Season.Value, true);
sortOrder = ItemSortBy.AiredEpisodeOrder;
}
var config = user.Configuration;
if (!config.DisplayMissingEpisodes)
{
episodes = episodes.Where(i => !i.IsMissingEpisode);
}
if (!config.DisplayUnairedEpisodes)
{
episodes = episodes.Where(i => !i.IsVirtualUnaired);
}
if (request.IsMissing.HasValue)
{
var val = request.IsMissing.Value;
episodes = episodes.Where(i => i.IsMissingEpisode == val);
}
if (request.IsVirtualUnaired.HasValue)
{
var val = request.IsVirtualUnaired.Value;
episodes = episodes.Where(i => i.IsVirtualUnaired == val);
}
episodes = _libraryManager.Sort(episodes, user, new[] { sortOrder }, SortOrder.Ascending)
.Cast<Episode>();
// This must be the last filter
if (!string.IsNullOrEmpty(request.AdjacentTo))
{
episodes = ItemsService.FilterForAdjacency(episodes, request.AdjacentTo)
.Cast<Episode>();
}
var returnItems = episodes.Select(i => _dtoService.GetBaseItemDto(i, fields, user))
.ToArray();
return new ItemsResult
{
TotalRecordCount = returnItems.Length,
Items = returnItems
};
}
internal static IEnumerable<Episode> FilterEpisodesBySeason(IEnumerable<Episode> episodes, int seasonNumber, bool includeSpecials)
{
if (!includeSpecials || seasonNumber < 1)
{
return episodes.Where(i => (i.PhysicalSeasonNumber ?? -1) == seasonNumber);
}
return episodes.Where(i =>
{
var episode = i;
if (episode != null)
{
var currentSeasonNumber = episode.AiredSeasonNumber;
return currentSeasonNumber.HasValue && currentSeasonNumber.Value == seasonNumber;
}
return false;
});
}
}
}

@ -43,7 +43,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <summary>
/// Class ArtistsService
/// </summary>
public class ArtistsService : BaseItemsByNameService<Artist>
public class ArtistsService : BaseItemsByNameService<MusicArtist>
{
/// <summary>
/// Initializes a new instance of the <see cref="ArtistsService" /> class.
@ -109,24 +109,9 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="request">The request.</param>
/// <param name="items">The items.</param>
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
protected override IEnumerable<Artist> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
protected override IEnumerable<MusicArtist> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
{
var itemsList = items.OfType<Audio>().ToList();
return itemsList
.SelectMany(i =>
{
var list = new List<string>();
if (!string.IsNullOrEmpty(i.AlbumArtist))
{
list.Add(i.AlbumArtist);
}
list.AddRange(i.Artists);
return list;
})
.Distinct(StringComparer.OrdinalIgnoreCase)
return LibraryManager.GetAllArtists(items)
.Select(name => LibraryManager.GetArtist(name));
}
}

@ -7,7 +7,7 @@ using System;
namespace MediaBrowser.Api.UserLibrary
{
public abstract class BaseItemsRequest
public abstract class BaseItemsRequest : IHasItemFields
{
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
@ -109,32 +109,6 @@ namespace MediaBrowser.Api.UserLibrary
return val.Split(',').Select(v => (ItemFilter)Enum.Parse(typeof(ItemFilter), v, true));
}
/// <summary>
/// Gets the item fields.
/// </summary>
/// <returns>IEnumerable{ItemFields}.</returns>
public IEnumerable<ItemFields> GetItemFields()
{
var val = Fields;
if (string.IsNullOrEmpty(val))
{
return new ItemFields[] { };
}
return val.Split(',').Select(v =>
{
ItemFields value;
if (Enum.TryParse(v, true, out value))
{
return (ItemFields?)value;
}
return null;
}).Where(i => i.HasValue).Select(i => i.Value);
}
/// <summary>
/// Gets the image types.
/// </summary>

@ -1,10 +1,13 @@
using MediaBrowser.Controller.Dto;
using System.Globalization;
using System.IO;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using ServiceStack.ServiceHost;
@ -205,6 +208,27 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "AiredDuringSeason", Description = "Gets all episodes that aired during a season, including specials.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? AiredDuringSeason { get; set; }
[ApiMember(Name = "MinPremiereDate", Description = "Optional. The minimum premiere date. Format = yyyyMMddHHmmss", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string MinPremiereDate { get; set; }
[ApiMember(Name = "MaxPremiereDate", Description = "Optional. The maximum premiere date. Format = yyyyMMddHHmmss", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string MaxPremiereDate { get; set; }
[ApiMember(Name = "HasOverview", Description = "Optional filter by items that have an overview or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? HasOverview { get; set; }
[ApiMember(Name = "HasImdbId", Description = "Optional filter by items that have an imdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? HasImdbId { get; set; }
[ApiMember(Name = "HasTmdbId", Description = "Optional filter by items that have a tmdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? HasTmdbId { get; set; }
[ApiMember(Name = "HasTvdbId", Description = "Optional filter by items that have a tvdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? HasTvdbId { get; set; }
[ApiMember(Name = "IsYearMismatched", Description = "Optional filter by items that are potentially misidentified.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsYearMismatched { get; set; }
}
/// <summary>
@ -286,6 +310,12 @@ namespace MediaBrowser.Api.UserLibrary
items = ApplySortOrder(request, items, user, _libraryManager);
// This must be the last filter
if (!string.IsNullOrEmpty(request.AdjacentTo))
{
items = FilterForAdjacency(items, request.AdjacentTo);
}
var itemsArray = items.ToList();
var pagedItems = ApplyPaging(request, itemsArray);
@ -642,30 +672,6 @@ namespace MediaBrowser.Api.UserLibrary
});
}
if (!string.IsNullOrEmpty(request.AdjacentTo))
{
var item = _dtoService.GetItemByDtoId(request.AdjacentTo);
var allSiblings = item.Parent.GetChildren(user, true).OrderBy(i => i.SortName).ToList();
var index = allSiblings.IndexOf(item);
var previousId = Guid.Empty;
var nextId = Guid.Empty;
if (index > 0)
{
previousId = allSiblings[index - 1].Id;
}
if (index < allSiblings.Count - 1)
{
nextId = allSiblings[index + 1].Id;
}
items = items.Where(i => i.Id == previousId || i.Id == nextId);
}
// Min index number
if (request.MinIndexNumber.HasValue)
{
@ -861,7 +867,19 @@ namespace MediaBrowser.Api.UserLibrary
if (request.HasTrailer.HasValue)
{
items = items.Where(i => request.HasTrailer.Value ? i.LocalTrailerIds.Count > 0 : i.LocalTrailerIds.Count == 0);
var val = request.HasTrailer.Value;
items = items.Where(i =>
{
var trailerCount = 0;
var hasTrailers = i as IHasTrailers;
if (hasTrailers != null)
{
trailerCount = hasTrailers.LocalTrailerIds.Count;
}
return val ? trailerCount > 0 : trailerCount == 0;
});
}
if (request.HasThemeSong.HasValue)
@ -1005,26 +1023,134 @@ namespace MediaBrowser.Api.UserLibrary
if (request.AiredDuringSeason.HasValue)
{
var val = request.AiredDuringSeason.Value;
items = TvShowsService.FilterEpisodesBySeason(items.OfType<Episode>(), request.AiredDuringSeason.Value, true);
}
if (!string.IsNullOrEmpty(request.MinPremiereDate))
{
var date = DateTime.ParseExact(request.MinPremiereDate, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
items = items.Where(i => i.PremiereDate.HasValue && i.PremiereDate.Value >= date);
}
if (!string.IsNullOrEmpty(request.MaxPremiereDate))
{
var date = DateTime.ParseExact(request.MaxPremiereDate, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
items = items.Where(i => i.PremiereDate.HasValue && i.PremiereDate.Value <= date);
}
if (request.HasOverview.HasValue)
{
var filterValue = request.HasOverview.Value;
items = items.Where(i =>
{
var episode = i as Episode;
var hasValue = !string.IsNullOrEmpty(i.Overview);
if (episode != null)
{
var seasonNumber = episode.AirsAfterSeasonNumber ?? episode.AirsBeforeEpisodeNumber ?? episode.ParentIndexNumber;
return hasValue == filterValue;
});
}
return episode.PremiereDate.HasValue && seasonNumber.HasValue && seasonNumber.Value == val;
}
if (request.HasImdbId.HasValue)
{
var filterValue = request.HasImdbId.Value;
return false;
items = items.Where(i =>
{
var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Imdb));
return hasValue == filterValue;
});
}
if (request.HasTmdbId.HasValue)
{
var filterValue = request.HasTmdbId.Value;
items = items.Where(i =>
{
var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tmdb));
return hasValue == filterValue;
});
}
if (request.HasTvdbId.HasValue)
{
var filterValue = request.HasTvdbId.Value;
items = items.Where(i =>
{
var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb));
return hasValue == filterValue;
});
}
if (request.IsYearMismatched.HasValue)
{
var filterValue = request.IsYearMismatched.Value;
items = items.Where(i => IsYearMismatched(i) == filterValue);
}
return items;
}
private bool IsYearMismatched(BaseItem item)
{
if (item.ProductionYear.HasValue)
{
var path = item.Path;
if (!string.IsNullOrEmpty(path))
{
int? yearInName;
string name;
NameParser.ParseName(Path.GetFileName(path), out name, out yearInName);
// Go up a level if we didn't get a year
if (!yearInName.HasValue)
{
NameParser.ParseName(Path.GetFileName(Path.GetDirectoryName(path)), out name, out yearInName);
}
if (yearInName.HasValue)
{
return yearInName.Value != item.ProductionYear.Value;
}
}
}
return false;
}
internal static IEnumerable<BaseItem> FilterForAdjacency(IEnumerable<BaseItem> items, string adjacentToId)
{
var list = items.ToList();
var adjacentToIdGuid = new Guid(adjacentToId);
var adjacentToItem = list.FirstOrDefault(i => i.Id == adjacentToIdGuid);
var index = list.IndexOf(adjacentToItem);
var previousId = Guid.Empty;
var nextId = Guid.Empty;
if (index > 0)
{
previousId = list[index - 1].Id;
}
if (index < list.Count - 1)
{
nextId = list[index + 1].Id;
}
return list.Where(i => i.Id == previousId || i.Id == nextId);
}
/// <summary>
/// Determines whether the specified item has image.
/// </summary>

@ -155,7 +155,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <returns>IEnumerable{PersonInfo}.</returns>
private IEnumerable<PersonInfo> GetAllPeople(IEnumerable<BaseItem> itemsList, string[] personTypes)
{
var people = itemsList.SelectMany(i => i.People.OrderBy(p => p.Type));
var people = itemsList.SelectMany(i => i.People.OrderBy(p => p.SortOrder ?? int.MaxValue).ThenBy(p => p.Type));
return personTypes.Length == 0 ?

@ -489,7 +489,15 @@ namespace MediaBrowser.Api.UserLibrary
// Get everything
var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList();
var dtos = item.LocalTrailerIds
var trailerIds = new List<Guid>();
var hasTrailers = item as IHasTrailers;
if (hasTrailers != null)
{
trailerIds = hasTrailers.LocalTrailerIds;
}
var dtos = trailerIds
.Select(_libraryManager.GetItemById)
.OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, fields, user, item));

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="morelinq" version="1.0.16006" targetFramework="net45" />
<package id="ServiceStack.Common" version="3.9.62" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.62" targetFramework="net45" />
<package id="ServiceStack.Common" version="3.9.70" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.70" targetFramework="net45" />
</packages>

@ -22,7 +22,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
@ -34,7 +33,7 @@ namespace MediaBrowser.Common.Implementations
/// </summary>
/// <typeparam name="TApplicationPathsType">The type of the T application paths type.</typeparam>
public abstract class BaseApplicationHost<TApplicationPathsType> : IApplicationHost
where TApplicationPathsType : class, IApplicationPaths, new()
where TApplicationPathsType : class, IApplicationPaths
{
/// <summary>
/// Occurs when [has pending restart changed].
@ -84,7 +83,7 @@ namespace MediaBrowser.Common.Implementations
/// <summary>
/// The json serializer
/// </summary>
public readonly IJsonSerializer JsonSerializer = new JsonSerializer();
public IJsonSerializer JsonSerializer { get; private set; }
/// <summary>
/// The _XML serializer
@ -154,7 +153,7 @@ namespace MediaBrowser.Common.Implementations
protected IInstallationManager InstallationManager { get; private set; }
protected IFileSystem FileSystemManager { get; private set; }
/// <summary>
/// Gets or sets the zip client.
/// </summary>
@ -182,6 +181,8 @@ namespace MediaBrowser.Common.Implementations
/// <returns>Task.</returns>
public virtual async Task Init()
{
JsonSerializer = CreateJsonSerializer();
IsFirstRun = !ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted;
Logger = LogManager.GetLogger("App");
@ -213,19 +214,24 @@ namespace MediaBrowser.Common.Implementations
}
protected virtual IJsonSerializer CreateJsonSerializer()
{
return new JsonSerializer();
}
private void SetHttpLimit()
{
try
{
// Increase the max http request limit
ServicePointManager.DefaultConnectionLimit = Math.Max(48, ServicePointManager.DefaultConnectionLimit);
ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit);
}
catch (Exception ex)
{
Logger.ErrorException("Error setting http limit", ex);
}
}
/// <summary>
/// Installs the iso mounters.
/// </summary>
@ -353,7 +359,7 @@ namespace MediaBrowser.Common.Implementations
FileSystemManager = CreateFileSystemManager();
RegisterSingleInstance(FileSystemManager);
HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, Logger, CreateHttpClient, FileSystemManager);
HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, Logger, FileSystemManager);
RegisterSingleInstance(HttpClient);
NetworkManager = CreateNetworkManager();
@ -378,8 +384,6 @@ namespace MediaBrowser.Common.Implementations
return new CommonFileSystem(Logger, true);
}
protected abstract HttpClient CreateHttpClient(bool enableHttpCompression);
/// <summary>
/// Gets a list of types within an assembly
/// This will handle situations that would normally throw an exception - such as a type within the assembly that depends on some other non-existant reference

@ -20,21 +20,23 @@ namespace MediaBrowser.Common.Implementations
/// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationPaths" /> class.
/// </summary>
/// <param name="useDebugPath">if set to <c>true</c> [use debug paths].</param>
protected BaseApplicationPaths(bool useDebugPath)
protected BaseApplicationPaths(bool useDebugPath, string applicationPath)
{
_useDebugPath = useDebugPath;
ApplicationPath = applicationPath;
}
/// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationPaths"/> class.
/// </summary>
/// <param name="programDataPath">The program data path.</param>
protected BaseApplicationPaths(string programDataPath)
protected BaseApplicationPaths(string programDataPath, string applicationPath)
{
_programDataPath = programDataPath;
ApplicationPath = applicationPath;
}
public string ApplicationPath { get; private set; }
/// <summary>
/// The _program data path
/// </summary>

@ -1,5 +1,4 @@
using System;
using System.Net.Http;
namespace MediaBrowser.Common.Implementations.HttpClientManager
{
@ -8,11 +7,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
/// </summary>
public class HttpClientInfo
{
/// <summary>
/// Gets or sets the HTTP client.
/// </summary>
/// <value>The HTTP client.</value>
public HttpClient HttpClient { get; set; }
/// <summary>
/// Gets or sets the last timeout.
/// </summary>

@ -9,7 +9,10 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Cache;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -21,6 +24,11 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
/// </summary>
public class HttpClientManager : IHttpClient
{
/// <summary>
/// When one request to a host times out, we'll ban all other requests for this period of time, to prevent scans from stalling
/// </summary>
private const int TimeoutSeconds = 30;
/// <summary>
/// The _logger
/// </summary>
@ -31,23 +39,18 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
/// </summary>
private readonly IApplicationPaths _appPaths;
public delegate HttpClient GetHttpClientHandler(bool enableHttpCompression);
private readonly GetHttpClientHandler _getHttpClientHandler;
private readonly IFileSystem _fileSystem;
/// <summary>
/// Initializes a new instance of the <see cref="HttpClientManager"/> class.
/// Initializes a new instance of the <see cref="HttpClientManager" /> class.
/// </summary>
/// <param name="appPaths">The app paths.</param>
/// <param name="logger">The logger.</param>
/// <param name="getHttpClientHandler">The get HTTP client handler.</param>
/// <exception cref="System.ArgumentNullException">
/// appPaths
/// <param name="fileSystem">The file system.</param>
/// <exception cref="System.ArgumentNullException">appPaths
/// or
/// logger
/// </exception>
public HttpClientManager(IApplicationPaths appPaths, ILogger logger, GetHttpClientHandler getHttpClientHandler, IFileSystem fileSystem)
/// logger</exception>
public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem)
{
if (appPaths == null)
{
@ -59,7 +62,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
}
_logger = logger;
_getHttpClientHandler = getHttpClientHandler;
_fileSystem = fileSystem;
_appPaths = appPaths;
}
@ -91,111 +93,83 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
if (!_httpClients.TryGetValue(key, out client))
{
client = new HttpClientInfo
{
client = new HttpClientInfo();
HttpClient = _getHttpClientHandler(enableHttpCompression)
};
_httpClients.TryAdd(key, client);
}
return client;
}
public async Task<HttpResponseInfo> GetResponse(HttpRequestOptions options)
private WebRequest GetMonoRequest(HttpRequestOptions options, string method, bool enableHttpCompression)
{
ValidateParams(options.Url, options.CancellationToken);
var request = WebRequest.Create(options.Url);
options.CancellationToken.ThrowIfCancellationRequested();
var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression);
if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < 30)
if (!string.IsNullOrEmpty(options.AcceptHeader))
{
throw new HttpException(string.Format("Cancelling connection to {0} due to a previous timeout.", options.Url)) { IsTimedOut = true };
request.Headers.Add("Accept", options.AcceptHeader);
}
using (var message = GetHttpRequestMessage(options))
{
if (options.ResourcePool != null)
{
await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false);
}
if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < 30)
{
if (options.ResourcePool != null)
{
options.ResourcePool.Release();
}
throw new HttpException(string.Format("Connection to {0} timed out", options.Url)) { IsTimedOut = true };
}
_logger.Info("HttpClientManager.Get url: {0}", options.Url);
try
{
options.CancellationToken.ThrowIfCancellationRequested();
var response = await client.HttpClient.SendAsync(message, HttpCompletionOption.ResponseContentRead, options.CancellationToken).ConfigureAwait(false);
EnsureSuccessStatusCode(response);
options.CancellationToken.ThrowIfCancellationRequested();
return new HttpResponseInfo
{
Content = await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.Revalidate);
request.ConnectionGroupName = GetHostFromUrl(options.Url);
request.Method = method;
request.Timeout = 20000;
StatusCode = response.StatusCode,
if (!string.IsNullOrEmpty(options.UserAgent))
{
request.Headers.Add("User-Agent", options.UserAgent);
}
ContentType = response.Content.Headers.ContentType.MediaType
};
}
catch (OperationCanceledException ex)
{
var exception = GetCancellationException(options.Url, options.CancellationToken, ex);
return request;
}
var httpException = exception as HttpException;
private PropertyInfo _httpBehaviorPropertyInfo;
private WebRequest GetRequest(HttpRequestOptions options, string method, bool enableHttpCompression)
{
#if __MonoCS__
return GetMonoRequest(options, method, enableHttpCompression);
#endif
var request = HttpWebRequest.CreateHttp(options.Url);
if (httpException != null && httpException.IsTimedOut)
{
client.LastTimeout = DateTime.UtcNow;
}
if (!string.IsNullOrEmpty(options.AcceptHeader))
{
request.Accept = options.AcceptHeader;
}
throw exception;
}
catch (HttpRequestException ex)
{
_logger.ErrorException("Error getting response from " + options.Url, ex);
request.AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None;
request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.Revalidate);
request.ConnectionGroupName = GetHostFromUrl(options.Url);
request.KeepAlive = true;
request.Method = method;
request.Pipelined = true;
request.Timeout = 20000;
throw new HttpException(ex.Message, ex);
}
catch (Exception ex)
{
_logger.ErrorException("Error getting response from " + options.Url, ex);
if (!string.IsNullOrEmpty(options.UserAgent))
{
request.UserAgent = options.UserAgent;
}
throw;
}
finally
{
if (options.ResourcePool != null)
{
options.ResourcePool.Release();
}
}
// This is a hack to prevent KeepAlive from getting disabled internally by the HttpWebRequest
// May need to remove this for mono
var sp = request.ServicePoint;
if (_httpBehaviorPropertyInfo == null)
{
_httpBehaviorPropertyInfo = sp.GetType().GetProperty("HttpBehaviour", BindingFlags.Instance | BindingFlags.NonPublic);
}
_httpBehaviorPropertyInfo.SetValue(sp, (byte)0, null);
return request;
}
/// <summary>
/// Performs a GET request and returns the resulting stream
/// Gets the response internal.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>Task{Stream}.</returns>
/// <exception cref="HttpException"></exception>
/// <exception cref="MediaBrowser.Model.Net.HttpException"></exception>
public async Task<Stream> Get(HttpRequestOptions options)
/// <returns>Task{HttpResponseInfo}.</returns>
/// <exception cref="HttpException">
/// </exception>
public async Task<HttpResponseInfo> GetResponse(HttpRequestOptions options)
{
ValidateParams(options.Url, options.CancellationToken);
@ -203,77 +177,115 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression);
if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < 30)
if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < TimeoutSeconds)
{
throw new HttpException(string.Format("Cancelling connection to {0} due to a previous timeout.", options.Url)) { IsTimedOut = true };
}
using (var message = GetHttpRequestMessage(options))
var httpWebRequest = GetRequest(options, "GET", options.EnableHttpCompression);
if (options.ResourcePool != null)
{
await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false);
}
if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < TimeoutSeconds)
{
if (options.ResourcePool != null)
{
await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false);
options.ResourcePool.Release();
}
if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < 30)
{
if (options.ResourcePool != null)
{
options.ResourcePool.Release();
}
throw new HttpException(string.Format("Connection to {0} timed out", options.Url)) { IsTimedOut = true };
}
throw new HttpException(string.Format("Connection to {0} timed out", options.Url)) { IsTimedOut = true };
}
_logger.Info("HttpClientManager.GET url: {0}", options.Url);
_logger.Info("HttpClientManager.Get url: {0}", options.Url);
try
{
options.CancellationToken.ThrowIfCancellationRequested();
try
using (var response = await httpWebRequest.GetResponseAsync().ConfigureAwait(false))
{
var httpResponse = (HttpWebResponse)response;
EnsureSuccessStatusCode(httpResponse);
options.CancellationToken.ThrowIfCancellationRequested();
var response = await client.HttpClient.SendAsync(message, HttpCompletionOption.ResponseContentRead, options.CancellationToken).ConfigureAwait(false);
using (var stream = httpResponse.GetResponseStream())
{
var memoryStream = new MemoryStream();
EnsureSuccessStatusCode(response);
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
options.CancellationToken.ThrowIfCancellationRequested();
memoryStream.Position = 0;
return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
}
catch (OperationCanceledException ex)
{
var exception = GetCancellationException(options.Url, options.CancellationToken, ex);
return new HttpResponseInfo
{
Content = memoryStream,
var httpException = exception as HttpException;
StatusCode = httpResponse.StatusCode,
if (httpException != null && httpException.IsTimedOut)
{
client.LastTimeout = DateTime.UtcNow;
ContentType = httpResponse.ContentType
};
}
throw exception;
}
catch (HttpRequestException ex)
{
_logger.ErrorException("Error getting response from " + options.Url, ex);
}
catch (OperationCanceledException ex)
{
var exception = GetCancellationException(options.Url, options.CancellationToken, ex);
throw new HttpException(ex.Message, ex);
}
catch (Exception ex)
{
_logger.ErrorException("Error getting response from " + options.Url, ex);
var httpException = exception as HttpException;
throw;
if (httpException != null && httpException.IsTimedOut)
{
client.LastTimeout = DateTime.UtcNow;
}
finally
throw exception;
}
catch (HttpRequestException ex)
{
_logger.ErrorException("Error getting response from " + options.Url, ex);
throw new HttpException(ex.Message, ex);
}
catch (WebException ex)
{
_logger.ErrorException("Error getting response from " + options.Url, ex);
throw new HttpException(ex.Message, ex);
}
catch (Exception ex)
{
_logger.ErrorException("Error getting response from " + options.Url, ex);
throw;
}
finally
{
if (options.ResourcePool != null)
{
if (options.ResourcePool != null)
{
options.ResourcePool.Release();
}
options.ResourcePool.Release();
}
}
}
/// <summary>
/// Performs a GET request and returns the resulting stream
/// </summary>
/// <param name="options">The options.</param>
/// <returns>Task{Stream}.</returns>
/// <exception cref="HttpException"></exception>
/// <exception cref="MediaBrowser.Model.Net.HttpException"></exception>
public async Task<Stream> Get(HttpRequestOptions options)
{
var response = await GetResponse(options).ConfigureAwait(false);
return response.Content;
}
/// <summary>
/// Performs a GET request and returns the resulting stream
/// </summary>
@ -305,64 +317,112 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
/// <summary>
/// Performs a POST request
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="options">The options.</param>
/// <param name="postData">Params to add to the POST data.</param>
/// <param name="resourcePool">The resource pool.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>stream on success, null on failure</returns>
/// <exception cref="HttpException">
/// </exception>
/// <exception cref="System.ArgumentNullException">postData</exception>
/// <exception cref="MediaBrowser.Model.Net.HttpException"></exception>
public async Task<Stream> Post(string url, Dictionary<string, string> postData, SemaphoreSlim resourcePool, CancellationToken cancellationToken)
public async Task<Stream> Post(HttpRequestOptions options, Dictionary<string, string> postData)
{
ValidateParams(url, cancellationToken);
ValidateParams(options.Url, options.CancellationToken);
if (postData == null)
{
throw new ArgumentNullException("postData");
}
options.CancellationToken.ThrowIfCancellationRequested();
cancellationToken.ThrowIfCancellationRequested();
var httpWebRequest = GetRequest(options, "POST", options.EnableHttpCompression);
var strings = postData.Keys.Select(key => string.Format("{0}={1}", key, postData[key]));
var postContent = string.Join("&", strings.ToArray());
var content = new StringContent(postContent, Encoding.UTF8, "application/x-www-form-urlencoded");
var bytes = Encoding.UTF8.GetBytes(postContent);
if (resourcePool != null)
httpWebRequest.ContentType = "application/x-www-form-urlencoded";
httpWebRequest.ContentLength = bytes.Length;
httpWebRequest.GetRequestStream().Write(bytes, 0, bytes.Length);
if (options.ResourcePool != null)
{
await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false);
}
_logger.Info("HttpClientManager.Post url: {0}", url);
_logger.Info("HttpClientManager.POST url: {0}", options.Url);
try
{
cancellationToken.ThrowIfCancellationRequested();
options.CancellationToken.ThrowIfCancellationRequested();
var msg = await GetHttpClient(GetHostFromUrl(url), true).HttpClient.PostAsync(url, content, cancellationToken).ConfigureAwait(false);
using (var response = await httpWebRequest.GetResponseAsync().ConfigureAwait(false))
{
var httpResponse = (HttpWebResponse)response;
EnsureSuccessStatusCode(httpResponse);
EnsureSuccessStatusCode(msg);
options.CancellationToken.ThrowIfCancellationRequested();
return await msg.Content.ReadAsStreamAsync().ConfigureAwait(false);
using (var stream = httpResponse.GetResponseStream())
{
var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0;
return memoryStream;
}
}
}
catch (OperationCanceledException ex)
{
throw GetCancellationException(url, cancellationToken, ex);
var exception = GetCancellationException(options.Url, options.CancellationToken, ex);
throw exception;
}
catch (HttpRequestException ex)
{
_logger.ErrorException("Error getting response from " + url, ex);
_logger.ErrorException("Error getting response from " + options.Url, ex);
throw new HttpException(ex.Message, ex);
}
catch (WebException ex)
{
_logger.ErrorException("Error getting response from " + options.Url, ex);
throw new HttpException(ex.Message, ex);
}
catch (Exception ex)
{
_logger.ErrorException("Error getting response from " + options.Url, ex);
throw;
}
finally
{
if (resourcePool != null)
if (options.ResourcePool != null)
{
resourcePool.Release();
options.ResourcePool.Release();
}
}
}
/// <summary>
/// Performs a POST request
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="postData">Params to add to the POST data.</param>
/// <param name="resourcePool">The resource pool.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>stream on success, null on failure</returns>
public Task<Stream> Post(string url, Dictionary<string, string> postData, SemaphoreSlim resourcePool, CancellationToken cancellationToken)
{
return Post(new HttpRequestOptions
{
Url = url,
ResourcePool = resourcePool,
CancellationToken = cancellationToken
}, postData);
}
/// <summary>
/// Downloads the contents of a given url into a temporary location
/// </summary>
@ -391,6 +451,8 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
options.CancellationToken.ThrowIfCancellationRequested();
var httpWebRequest = GetRequest(options, "GET", options.EnableHttpCompression);
if (options.ResourcePool != null)
{
await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false);
@ -398,57 +460,68 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
options.Progress.Report(0);
_logger.Info("HttpClientManager.GetTempFile url: {0}, temp file: {1}", options.Url, tempFile);
_logger.Info("HttpClientManager.GetTempFileResponse url: {0}", options.Url);
try
{
options.CancellationToken.ThrowIfCancellationRequested();
using (var message = GetHttpRequestMessage(options))
using (var response = await httpWebRequest.GetResponseAsync().ConfigureAwait(false))
{
using (var response = await GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression).HttpClient.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false))
{
EnsureSuccessStatusCode(response);
var httpResponse = (HttpWebResponse)response;
options.CancellationToken.ThrowIfCancellationRequested();
EnsureSuccessStatusCode(httpResponse);
options.CancellationToken.ThrowIfCancellationRequested();
var contentLength = GetContentLength(response);
var contentLength = GetContentLength(httpResponse);
if (!contentLength.HasValue)
if (!contentLength.HasValue)
{
// We're not able to track progress
using (var stream = httpResponse.GetResponseStream())
{
// We're not able to track progress
using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true))
{
using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true))
{
await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
}
await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
}
}
else
}
else
{
using (var stream = ProgressStream.CreateReadProgressStream(httpResponse.GetResponseStream(), options.Progress.Report, contentLength.Value))
{
using (var stream = ProgressStream.CreateReadProgressStream(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), options.Progress.Report, contentLength.Value))
using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true))
{
using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true))
{
await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
}
await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
}
}
}
options.Progress.Report(100);
options.Progress.Report(100);
return new HttpResponseInfo
{
TempFilePath = tempFile,
return new HttpResponseInfo
{
TempFilePath = tempFile,
StatusCode = response.StatusCode,
StatusCode = httpResponse.StatusCode,
ContentType = response.Content.Headers.ContentType.MediaType
};
}
ContentType = httpResponse.ContentType
};
}
}
catch (OperationCanceledException ex)
{
throw GetTempFileException(ex, options, tempFile);
}
catch (HttpRequestException ex)
{
throw GetTempFileException(ex, options, tempFile);
}
catch (WebException ex)
{
throw GetTempFileException(ex, options, tempFile);
}
catch (Exception ex)
{
throw GetTempFileException(ex, options, tempFile);
@ -462,63 +535,16 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
}
}
/// <summary>
/// Gets the message.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>HttpResponseMessage.</returns>
private HttpRequestMessage GetHttpRequestMessage(HttpRequestOptions options)
private long? GetContentLength(HttpWebResponse response)
{
var message = new HttpRequestMessage(HttpMethod.Get, options.Url);
var length = response.ContentLength;
foreach (var pair in options.RequestHeaders.ToList())
{
if (!message.Headers.TryAddWithoutValidation(pair.Key, pair.Value))
{
_logger.Error("Unable to add request header {0} with value {1}", pair.Key, pair.Value);
}
}
return message;
}
/// <summary>
/// Gets the length of the content.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>System.Nullable{System.Int64}.</returns>
private long? GetContentLength(HttpResponseMessage response)
{
IEnumerable<string> lengthValues = null;
// Seeing some InvalidOperationException here under mono
try
{
response.Headers.TryGetValues("content-length", out lengthValues);
}
catch (InvalidOperationException ex)
{
_logger.ErrorException("Error accessing response.Headers.TryGetValues Content-Length", ex);
}
if (lengthValues == null)
{
try
{
response.Content.Headers.TryGetValues("content-length", out lengthValues);
}
catch (InvalidOperationException ex)
{
_logger.ErrorException("Error accessing response.Content.Headers.TryGetValues Content-Length", ex);
}
}
if (lengthValues == null)
if (length == 0)
{
return null;
}
return long.Parse(string.Join(string.Empty, lengthValues.ToArray()), UsCulture);
return length;
}
protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
@ -545,16 +571,23 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
_logger.ErrorException("Error getting response from " + options.Url, ex);
var httpRequestException = ex as HttpRequestException;
// Cleanup
DeleteTempFile(tempFile);
var httpRequestException = ex as HttpRequestException;
if (httpRequestException != null)
{
return new HttpException(ex.Message, ex);
}
var webException = ex as WebException;
if (webException != null)
{
return new HttpException(ex.Message, ex);
}
return ex;
}
@ -613,11 +646,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
{
if (dispose)
{
foreach (var client in _httpClients.Values.ToList())
{
client.HttpClient.Dispose();
}
_httpClients.Clear();
}
}
@ -645,16 +673,14 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
return exception;
}
/// <summary>
/// Ensures the success status code.
/// </summary>
/// <param name="response">The response.</param>
/// <exception cref="MediaBrowser.Model.Net.HttpException"></exception>
private void EnsureSuccessStatusCode(HttpResponseMessage response)
private void EnsureSuccessStatusCode(HttpWebResponse response)
{
if (!response.IsSuccessStatusCode)
var statusCode = response.StatusCode;
var isSuccessful = statusCode >= HttpStatusCode.OK && statusCode <= (HttpStatusCode)299;
if (!isSuccessful)
{
throw new HttpException(response.ReasonPhrase) { StatusCode = response.StatusCode };
throw new HttpException(response.StatusDescription) { StatusCode = response.StatusCode };
}
}

@ -41,6 +41,10 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.2.1.0\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll</HintPath>
</Reference>
<Reference Include="SharpCompress">
<HintPath>..\packages\sharpcompress.0.10.1.3\lib\net40\SharpCompress.dll</HintPath>
</Reference>
@ -55,9 +59,6 @@
<Reference Include="System.Net" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="ServiceStack.Text">
<HintPath>..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs">

@ -29,7 +29,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
/// <summary>
/// Initializes a new instance of the <see cref="DeleteCacheFileTask" /> class.
/// </summary>
/// <param name="appPaths">The app paths.</param>
public DeleteCacheFileTask(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem)
{
ApplicationPaths = appPaths;

@ -80,7 +80,7 @@ namespace MediaBrowser.Common.Implementations.Serialization
using (Stream stream = File.OpenRead(file))
{
return ServiceStack.Text.JsonSerializer.DeserializeFromStream(type, stream);
return DeserializeFromStream(stream, type);
}
}
@ -101,7 +101,7 @@ namespace MediaBrowser.Common.Implementations.Serialization
using (Stream stream = File.OpenRead(file))
{
return ServiceStack.Text.JsonSerializer.DeserializeFromStream<T>(stream);
return DeserializeFromStream<T>(stream);
}
}
@ -169,6 +169,8 @@ namespace MediaBrowser.Common.Implementations.Serialization
ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.JsonDateHandler.ISO8601;
ServiceStack.Text.JsConfig.ExcludeTypeInfo = true;
ServiceStack.Text.JsConfig.IncludeNullValues = false;
ServiceStack.Text.JsConfig.AlwaysUseUtc = true;
ServiceStack.Text.JsConfig.AssumeUtc = true;
}
/// <summary>

@ -168,7 +168,7 @@ namespace MediaBrowser.Common.Implementations.Updates
{
// Let dev users get results more often for testing purposes
var cacheLength = _config.CommonConfiguration.SystemUpdateLevel == PackageVersionClass.Dev
? TimeSpan.FromMinutes(15)
? TimeSpan.FromMinutes(5)
: TimeSpan.FromHours(12);
if ((DateTime.UtcNow - _lastPackageListResult.Item2) < cacheLength)

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="2.1.0" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.62" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.70" targetFramework="net45" />
<package id="sharpcompress" version="0.10.1.3" targetFramework="net45" />
<package id="SimpleInjector" version="2.3.6" targetFramework="net45" />
</packages>

@ -6,6 +6,12 @@ namespace MediaBrowser.Common.Configuration
/// </summary>
public interface IApplicationPaths
{
/// <summary>
/// Gets the application path.
/// </summary>
/// <value>The application path.</value>
string ApplicationPath { get; }
/// <summary>
/// Gets the path to the program data folder
/// </summary>

@ -35,18 +35,21 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="ServiceStack.Common">
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll</HintPath>
<Reference Include="ServiceStack.Common, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Interfaces">
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll</HintPath>
<Reference Include="ServiceStack.Interfaces, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text">
<HintPath>..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll</HintPath>
<Reference Include="ServiceStack.Text, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs">

@ -73,6 +73,11 @@ namespace MediaBrowser.Common.Net
/// <exception cref="MediaBrowser.Model.Net.HttpException"></exception>
Task<string> GetTempFile(HttpRequestOptions options);
/// <summary>
/// Gets the temporary file response.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>Task{HttpResponseInfo}.</returns>
Task<HttpResponseInfo> GetTempFileResponse(HttpRequestOptions options);
}
}

@ -42,4 +42,9 @@ namespace MediaBrowser.Common.ScheduledTasks
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
IEnumerable<ITaskTrigger> GetDefaultTriggers();
}
public interface IConfigurableScheduledTask
{
bool IsHidden { get; }
}
}

@ -16,6 +16,15 @@ namespace MediaBrowser.Common.ScheduledTasks
/// <returns>TaskInfo.</returns>
public static TaskInfo GetTaskInfo(IScheduledTaskWorker task)
{
var isHidden = false;
var configurableTask = task.ScheduledTask as IConfigurableScheduledTask;
if (configurableTask != null)
{
isHidden = configurableTask.IsHidden;
}
return new TaskInfo
{
Name = task.Name,
@ -25,7 +34,8 @@ namespace MediaBrowser.Common.ScheduledTasks
LastExecutionResult = task.LastExecutionResult,
Triggers = task.Triggers.Select(GetTriggerInfo).ToList(),
Description = task.Description,
Category = task.Category
Category = task.Category,
IsHidden = isHidden
};
}

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ServiceStack.Common" version="3.9.62" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.62" targetFramework="net45" />
<package id="ServiceStack.Common" version="3.9.70" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.70" targetFramework="net45" />
</packages>

@ -1,86 +0,0 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities.Audio
{
/// <summary>
/// Class Artist
/// </summary>
public class Artist : BaseItem, IItemByName, IHasMusicGenres
{
public Artist()
{
UserItemCounts = new Dictionary<Guid, ItemByNameCounts>();
}
public string LastFmImageUrl { get; set; }
public string LastFmImageSize { get; set; }
/// <summary>
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
public override string GetUserDataKey()
{
return GetUserDataKey(this);
}
[IgnoreDataMember]
public Dictionary<Guid, ItemByNameCounts> UserItemCounts { get; set; }
/// <summary>
/// Finds the music artist.
/// </summary>
/// <param name="artist">The artist.</param>
/// <param name="libraryManager">The library manager.</param>
/// <returns>MusicArtist.</returns>
public static MusicArtist FindMusicArtist(Artist artist, ILibraryManager libraryManager)
{
return FindMusicArtist(artist, libraryManager.RootFolder.RecursiveChildren.OfType<MusicArtist>());
}
/// <summary>
/// Finds the music artist.
/// </summary>
/// <param name="artist">The artist.</param>
/// <param name="allMusicArtists">All music artists.</param>
/// <returns>MusicArtist.</returns>
public static MusicArtist FindMusicArtist(Artist artist, IEnumerable<MusicArtist> allMusicArtists)
{
var musicBrainzId = artist.GetProviderId(MetadataProviders.Musicbrainz);
return allMusicArtists.FirstOrDefault(i =>
{
if (!string.IsNullOrWhiteSpace(musicBrainzId) && string.Equals(musicBrainzId, i.GetProviderId(MetadataProviders.Musicbrainz), StringComparison.OrdinalIgnoreCase))
{
return true;
}
return string.Compare(i.Name, artist.Name, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols) == 0;
});
}
/// <summary>
/// Gets the user data key.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
public static string GetUserDataKey(BaseItem item)
{
var id = item.GetProviderId(MetadataProviders.Musicbrainz);
if (!string.IsNullOrEmpty(id))
{
return "Artist-Musicbrainz-" + id;
}
return "Artist-" + item.Name;
}
}
}

@ -1,11 +1,65 @@

using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Entities.Audio
{
/// <summary>
/// Class MusicArtist
/// </summary>
public class MusicArtist : Folder
public class MusicArtist : Folder, IItemByName, IHasMusicGenres, IHasDualAccess
{
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
public bool IsAccessedByName { get; set; }
public override bool IsFolder
{
get
{
return !IsAccessedByName;
}
}
protected override IEnumerable<BaseItem> ActualChildren
{
get
{
if (IsAccessedByName)
{
return new List<BaseItem>();
}
return base.ActualChildren;
}
}
protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool? recursive = null, bool forceRefreshMetadata = false)
{
if (IsAccessedByName)
{
// Should never get in here anyway
return Task.FromResult(true);
}
return base.ValidateChildrenInternal(progress, cancellationToken, recursive, forceRefreshMetadata);
}
public override string GetClientTypeName()
{
if (IsAccessedByName)
{
//return "Artist";
}
return base.GetClientTypeName();
}
/// <summary>
/// Gets or sets the last fm image URL.
/// </summary>
@ -13,13 +67,35 @@ namespace MediaBrowser.Controller.Entities.Audio
public string LastFmImageUrl { get; set; }
public string LastFmImageSize { get; set; }
public MusicArtist()
{
UserItemCountList = new List<ItemByNameCounts>();
}
/// <summary>
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
public override string GetUserDataKey()
{
return Artist.GetUserDataKey(this);
return GetUserDataKey(this);
}
/// <summary>
/// Gets the user data key.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
public static string GetUserDataKey(BaseItem item)
{
var id = item.GetProviderId(MetadataProviders.Musicbrainz);
if (!string.IsNullOrEmpty(id))
{
return "Artist-Musicbrainz-" + id;
}
return "Artist-" + item.Name;
}
}
}

@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.Entities.Audio
{
public MusicGenre()
{
UserItemCounts = new Dictionary<Guid, ItemByNameCounts>();
UserItemCountList = new List<ItemByNameCounts>();
}
/// <summary>
@ -25,6 +25,6 @@ namespace MediaBrowser.Controller.Entities.Audio
}
[IgnoreDataMember]
public Dictionary<Guid, ItemByNameCounts> UserItemCounts { get; set; }
public List<ItemByNameCounts> UserItemCountList { get; set; }
}
}

@ -1,7 +1,6 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
@ -38,10 +37,8 @@ namespace MediaBrowser.Controller.Entities
Tags = new List<string>();
ThemeSongIds = new List<Guid>();
ThemeVideoIds = new List<Guid>();
LocalTrailerIds = new List<Guid>();
LockedFields = new List<MetadataFields>();
Taglines = new List<string>();
RemoteTrailers = new List<MediaUrl>();
ImageSources = new List<ImageSourceInfo>();
}
@ -87,30 +84,12 @@ namespace MediaBrowser.Controller.Entities
/// <value>The id.</value>
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the budget.
/// </summary>
/// <value>The budget.</value>
public double? Budget { get; set; }
/// <summary>
/// Gets or sets the taglines.
/// </summary>
/// <value>The taglines.</value>
public List<string> Taglines { get; set; }
/// <summary>
/// Gets or sets the revenue.
/// </summary>
/// <value>The revenue.</value>
public double? Revenue { get; set; }
/// <summary>
/// Gets or sets the trailer URL.
/// </summary>
/// <value>The trailer URL.</value>
public List<MediaUrl> RemoteTrailers { get; set; }
/// <summary>
/// Return the id that should be used to key display prefs for this item.
/// Default is based on the type for everything except actual generic folders.
@ -139,6 +118,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the type of the location.
/// </summary>
/// <value>The type of the location.</value>
[IgnoreDataMember]
public virtual LocationType LocationType
{
get
@ -483,6 +463,22 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public Folder Parent { get; set; }
[IgnoreDataMember]
public IEnumerable<Folder> Parents
{
get
{
var parent = Parent;
while (parent != null)
{
yield return parent;
parent = parent.Parent;
}
}
}
/// <summary>
/// When the item first debuted. For movies this could be premiere date, episodes would be first aired
/// </summary>
@ -630,11 +626,6 @@ namespace MediaBrowser.Controller.Entities
/// <value>The original run time ticks.</value>
public long? OriginalRunTimeTicks { get; set; }
/// <summary>
/// Gets or sets the aspect ratio.
/// </summary>
/// <value>The aspect ratio.</value>
public string AspectRatio { get; set; }
/// <summary>
/// Gets or sets the production year.
/// </summary>
/// <value>The production year.</value>
@ -655,7 +646,6 @@ namespace MediaBrowser.Controller.Entities
public List<Guid> ThemeSongIds { get; set; }
public List<Guid> ThemeVideoIds { get; set; }
public List<Guid> LocalTrailerIds { get; set; }
[IgnoreDataMember]
public virtual string OfficialRatingForComparison
@ -898,7 +888,11 @@ namespace MediaBrowser.Controller.Entities
themeVideosChanged = await RefreshThemeVideos(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
localTrailersChanged = await RefreshLocalTrailers(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
var hasTrailers = this as IHasTrailers;
if (hasTrailers != null)
{
localTrailersChanged = await RefreshLocalTrailers(hasTrailers, cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
}
}
cancellationToken.ThrowIfCancellationRequested();
@ -918,18 +912,18 @@ namespace MediaBrowser.Controller.Entities
return changed;
}
private async Task<bool> RefreshLocalTrailers(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
private async Task<bool> RefreshLocalTrailers(IHasTrailers item, CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
{
var newItems = LoadLocalTrailers().ToList();
var newItemIds = newItems.Select(i => i.Id).ToList();
var itemsChanged = !LocalTrailerIds.SequenceEqual(newItemIds);
var itemsChanged = !item.LocalTrailerIds.SequenceEqual(newItemIds);
var tasks = newItems.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs: false));
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
LocalTrailerIds = newItemIds;
item.LocalTrailerIds = newItemIds;
return itemsChanged || results.Contains(true);
}
@ -1134,6 +1128,11 @@ namespace MediaBrowser.Controller.Entities
return changed;
}
public virtual string GetClientTypeName()
{
return GetType().Name;
}
/// <summary>
/// Determines if the item is considered new based on user settings
/// </summary>
@ -1187,6 +1186,7 @@ namespace MediaBrowser.Controller.Entities
if (existing != null)
{
existing.Type = PersonType.GuestStar;
existing.SortOrder = person.SortOrder ?? existing.SortOrder;
return;
}
}
@ -1203,23 +1203,35 @@ namespace MediaBrowser.Controller.Entities
else
{
// Was there, if no role and we have one - fill it in
if (string.IsNullOrWhiteSpace(existing.Role) && !string.IsNullOrWhiteSpace(person.Role)) existing.Role = person.Role;
if (string.IsNullOrWhiteSpace(existing.Role) && !string.IsNullOrWhiteSpace(person.Role))
{
existing.Role = person.Role;
}
existing.SortOrder = person.SortOrder ?? existing.SortOrder;
}
}
else
{
var existing = People.FirstOrDefault(p =>
string.Equals(p.Name, person.Name, StringComparison.OrdinalIgnoreCase) &&
string.Equals(p.Type, person.Type, StringComparison.OrdinalIgnoreCase));
// Check for dupes based on the combination of Name and Type
if (!People.Any(p => string.Equals(p.Name, person.Name, StringComparison.OrdinalIgnoreCase) && string.Equals(p.Type, person.Type, StringComparison.OrdinalIgnoreCase)))
if (existing == null)
{
People.Add(person);
}
else
{
existing.SortOrder = person.SortOrder ?? existing.SortOrder;
}
}
}
/// <summary>
/// Adds the tagline.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="tagline">The tagline.</param>
/// <exception cref="System.ArgumentNullException">tagline</exception>
public void AddTagline(string tagline)
@ -1737,5 +1749,14 @@ namespace MediaBrowser.Controller.Entities
// See if we can avoid a file system lookup by looking for the file in ResolveArgs
return metaFileEntry == null ? FileSystem.GetLastWriteTimeUtc(imagePath) : FileSystem.GetLastWriteTimeUtc(metaFileEntry);
}
/// <summary>
/// Gets the file system path to delete when the item is to be deleted
/// </summary>
/// <returns></returns>
public virtual IEnumerable<string> GetDeletePaths()
{
return new[] { Path };
}
}
}

@ -16,7 +16,7 @@ namespace MediaBrowser.Controller.Entities
/// <param name="url">The URL.</param>
/// <param name="isDirectLink">if set to <c>true</c> [is direct link].</param>
/// <exception cref="System.ArgumentNullException">url</exception>
public static void AddTrailerUrl(this BaseItem item, string url, bool isDirectLink)
public static void AddTrailerUrl(this IHasTrailers item, string url, bool isDirectLink)
{
if (string.IsNullOrWhiteSpace(url))
{

@ -90,7 +90,7 @@ namespace MediaBrowser.Controller.Entities
item.Id = item.Path.GetMBId(item.GetType());
}
if (_children.Any(i => i.Id == item.Id))
if (ActualChildren.Any(i => i.Id == item.Id))
{
throw new ArgumentException(string.Format("A child with the Id {0} already exists.", item.Id));
}
@ -108,14 +108,14 @@ namespace MediaBrowser.Controller.Entities
await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
await ItemRepository.SaveChildren(Id, _children.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
}
protected void AddChildrenInternal(IEnumerable<BaseItem> children)
{
lock (_childrenSyncLock)
{
var newChildren = _children.ToList();
var newChildren = ActualChildren.ToList();
newChildren.AddRange(children);
_children = newChildren;
}
@ -124,7 +124,7 @@ namespace MediaBrowser.Controller.Entities
{
lock (_childrenSyncLock)
{
var newChildren = _children.ToList();
var newChildren = ActualChildren.ToList();
newChildren.Add(child);
_children = newChildren;
}
@ -134,7 +134,7 @@ namespace MediaBrowser.Controller.Entities
{
lock (_childrenSyncLock)
{
_children = _children.Except(children).ToList();
_children = ActualChildren.Except(children).ToList();
}
}
@ -519,7 +519,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// The children
/// </summary>
private IReadOnlyList<BaseItem> _children = new List<BaseItem>();
private IReadOnlyList<BaseItem> _children;
/// <summary>
/// The _children sync lock
/// </summary>
@ -532,15 +532,10 @@ namespace MediaBrowser.Controller.Entities
{
get
{
return _children;
return _children ?? (_children = LoadChildrenInternal());
}
}
public void LoadSavedChildren()
{
_children = LoadChildrenInternal();
}
/// <summary>
/// thread-safe access to the actual children of this folder - without regard to user
/// </summary>
@ -758,7 +753,7 @@ namespace MediaBrowser.Controller.Entities
AddChildrenInternal(newItems);
await ItemRepository.SaveChildren(Id, _children.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
//force the indexes to rebuild next time
if (IndexCache != null)
@ -1007,8 +1002,7 @@ namespace MediaBrowser.Controller.Entities
return result;
}
var initialCount = _children.Count;
var list = new List<BaseItem>(initialCount);
var list = new List<BaseItem>();
AddChildrenToList(user, includeLinkedChildren, list, false, null);
@ -1038,16 +1032,13 @@ namespace MediaBrowser.Controller.Entities
}
}
if (recursive)
if (recursive && child.IsFolder)
{
var folder = child as Folder;
var folder = (Folder)child;
if (folder != null)
if (folder.AddChildrenToList(user, includeLinkedChildren, list, true, filter))
{
if (folder.AddChildrenToList(user, includeLinkedChildren, list, true, filter))
{
hasLinkedChildren = true;
}
hasLinkedChildren = true;
}
}
}
@ -1073,7 +1064,6 @@ namespace MediaBrowser.Controller.Entities
return hasLinkedChildren;
}
private int _lastRecursiveCount;
/// <summary>
/// Gets allowed recursive children of an item
/// </summary>
@ -1101,13 +1091,10 @@ namespace MediaBrowser.Controller.Entities
throw new ArgumentNullException("user");
}
var initialCount = _lastRecursiveCount == 0 ? _children.Count : _lastRecursiveCount;
var list = new List<BaseItem>(initialCount);
var list = new List<BaseItem>();
var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, true, filter);
_lastRecursiveCount = list.Count;
return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list;
}
@ -1127,8 +1114,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>IEnumerable{BaseItem}.</returns>
public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter)
{
var initialCount = _lastRecursiveCount == 0 ? _children.Count : _lastRecursiveCount;
var list = new List<BaseItem>(initialCount);
var list = new List<BaseItem>();
AddChildrenToList(list, true, filter);
@ -1150,14 +1136,11 @@ namespace MediaBrowser.Controller.Entities
list.Add(child);
}
if (recursive)
if (recursive && child.IsFolder)
{
var folder = child as Folder;
var folder = (Folder)child;
if (folder != null)
{
folder.AddChildrenToList(list, true, filter);
}
folder.AddChildrenToList(list, true, filter);
}
}
}

@ -4,16 +4,26 @@ using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
public class Game : BaseItem, IHasSoundtracks
public class Game : BaseItem, IHasSoundtracks, IHasTrailers
{
public List<Guid> SoundtrackIds { get; set; }
public Game()
{
MultiPartGameFiles = new List<string>();
SoundtrackIds = new List<Guid>();
RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>();
}
public List<Guid> LocalTrailerIds { get; set; }
/// <summary>
/// Gets or sets the remote trailers.
/// </summary>
/// <value>The remote trailers.</value>
public List<MediaUrl> RemoteTrailers { get; set; }
/// <summary>
/// Gets the type of the media.
/// </summary>
@ -84,5 +94,15 @@ namespace MediaBrowser.Controller.Entities
}
return base.GetUserDataKey();
}
public override IEnumerable<string> GetDeletePaths()
{
if (!IsInMixedFolder)
{
return new[] { System.IO.Path.GetDirectoryName(Path) };
}
return base.GetDeletePaths();
}
}
}

@ -9,7 +9,7 @@ namespace MediaBrowser.Controller.Entities
{
public GameGenre()
{
UserItemCounts = new Dictionary<Guid, ItemByNameCounts>();
UserItemCountList = new List<ItemByNameCounts>();
}
/// <summary>
@ -22,6 +22,6 @@ namespace MediaBrowser.Controller.Entities
}
[IgnoreDataMember]
public Dictionary<Guid, ItemByNameCounts> UserItemCounts { get; set; }
public List<ItemByNameCounts> UserItemCountList { get; set; }
}
}

@ -1,7 +1,7 @@
using System.Runtime.Serialization;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Dto;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities
{
@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.Entities
{
public Genre()
{
UserItemCounts = new Dictionary<Guid, ItemByNameCounts>();
UserItemCountList = new List<ItemByNameCounts>();
}
/// <summary>
@ -25,6 +25,6 @@ namespace MediaBrowser.Controller.Entities
}
[IgnoreDataMember]
public Dictionary<Guid, ItemByNameCounts> UserItemCounts { get; set; }
public List<ItemByNameCounts> UserItemCountList { get; set; }
}
}

@ -0,0 +1,14 @@
namespace MediaBrowser.Controller.Entities
{
/// <summary>
/// Interface IHasAspectRatio
/// </summary>
public interface IHasAspectRatio
{
/// <summary>
/// Gets or sets the aspect ratio.
/// </summary>
/// <value>The aspect ratio.</value>
string AspectRatio { get; set; }
}
}

@ -0,0 +1,18 @@

namespace MediaBrowser.Controller.Entities
{
public interface IHasBudget
{
/// <summary>
/// Gets or sets the budget.
/// </summary>
/// <value>The budget.</value>
double? Budget { get; set; }
/// <summary>
/// Gets or sets the revenue.
/// </summary>
/// <value>The revenue.</value>
double? Revenue { get; set; }
}
}

@ -0,0 +1,21 @@
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
public interface IHasTrailers
{
/// <summary>
/// Gets or sets the remote trailers.
/// </summary>
/// <value>The remote trailers.</value>
List<MediaUrl> RemoteTrailers { get; set; }
/// <summary>
/// Gets or sets the local trailer ids.
/// </summary>
/// <value>The local trailer ids.</value>
List<Guid> LocalTrailerIds { get; set; }
}
}

@ -1,6 +1,7 @@
using MediaBrowser.Model.Dto;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MediaBrowser.Controller.Entities
{
@ -9,26 +10,37 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public interface IItemByName
{
Dictionary<Guid, ItemByNameCounts> UserItemCounts { get; set; }
List<ItemByNameCounts> UserItemCountList { get; set; }
}
public static class IItemByNameExtensions
public interface IHasDualAccess : IItemByName
{
public static ItemByNameCounts GetItemByNameCounts(this IItemByName item, User user)
bool IsAccessedByName { get; }
}
public static class ItemByNameExtensions
{
public static ItemByNameCounts GetItemByNameCounts(this IItemByName item, Guid userId)
{
if (user == null)
if (userId == Guid.Empty)
{
throw new ArgumentNullException("user");
throw new ArgumentNullException("userId");
}
ItemByNameCounts counts;
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 (item.UserItemCounts.TryGetValue(user.Id, out counts))
if (current != null)
{
return counts;
item.UserItemCountList.Remove(current);
}
return null;
counts.UserId = userId;
item.UserItemCountList.Add(counts);
}
}
}

@ -40,7 +40,6 @@ namespace MediaBrowser.Controller.Entities
IndexName = indexName;
Parent = parent;
LoadSavedChildren();
}
/// <summary>

@ -1,10 +1,26 @@

using System;
using MediaBrowser.Model.Entities;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities.Movies
{
/// <summary>
/// Class BoxSet
/// </summary>
public class BoxSet : Folder
public class BoxSet : Folder, IHasTrailers
{
public BoxSet()
{
RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>();
}
public List<Guid> LocalTrailerIds { get; set; }
/// <summary>
/// Gets or sets the remote trailers.
/// </summary>
/// <value>The remote trailers.</value>
public List<MediaUrl> RemoteTrailers { get; set; }
}
}

@ -11,7 +11,7 @@ namespace MediaBrowser.Controller.Entities.Movies
/// <summary>
/// Class Movie
/// </summary>
public class Movie : Video, IHasCriticRating, IHasSoundtracks
public class Movie : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasTrailers
{
public List<Guid> SpecialFeatureIds { get; set; }
@ -21,8 +21,26 @@ namespace MediaBrowser.Controller.Entities.Movies
{
SpecialFeatureIds = new List<Guid>();
SoundtrackIds = new List<Guid>();
RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>();
}
public List<Guid> LocalTrailerIds { get; set; }
public List<MediaUrl> RemoteTrailers { get; set; }
/// <summary>
/// Gets or sets the budget.
/// </summary>
/// <value>The budget.</value>
public double? Budget { get; set; }
/// <summary>
/// Gets or sets the revenue.
/// </summary>
/// <value>The revenue.</value>
public double? Revenue { get; set; }
/// <summary>
/// Gets or sets the critic rating.
/// </summary>

@ -4,7 +4,7 @@ using System;
namespace MediaBrowser.Controller.Entities
{
public class MusicVideo : Video, IHasArtist, IHasMusicGenres
public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasBudget
{
/// <summary>
/// Gets or sets the artist.
@ -18,6 +18,18 @@ namespace MediaBrowser.Controller.Entities
/// <value>The album.</value>
public string Album { get; set; }
/// <summary>
/// Gets or sets the budget.
/// </summary>
/// <value>The budget.</value>
public double? Budget { get; set; }
/// <summary>
/// Gets or sets the revenue.
/// </summary>
/// <value>The revenue.</value>
public double? Revenue { get; set; }
/// <summary>
/// Determines whether the specified name has artist.
/// </summary>

@ -1,7 +1,7 @@
using System.Runtime.Serialization;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Dto;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities
{
@ -12,11 +12,11 @@ namespace MediaBrowser.Controller.Entities
{
public Person()
{
UserItemCounts = new Dictionary<Guid, ItemByNameCounts>();
UserItemCountList = new List<ItemByNameCounts>();
}
[IgnoreDataMember]
public Dictionary<Guid, ItemByNameCounts> UserItemCounts { get; set; }
public List<ItemByNameCounts> UserItemCountList { get; set; }
/// <summary>
/// Gets the user data key.
@ -49,6 +49,12 @@ namespace MediaBrowser.Controller.Entities
/// <value>The type.</value>
public string Type { get; set; }
/// <summary>
/// Gets or sets the sort order - ascending
/// </summary>
/// <value>The sort order.</value>
public int? SortOrder { get; set; }
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>

@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.Entities
{
public Studio()
{
UserItemCounts = new Dictionary<Guid, ItemByNameCounts>();
UserItemCountList = new List<ItemByNameCounts>();
}
/// <summary>
@ -25,6 +25,6 @@ namespace MediaBrowser.Controller.Entities
}
[IgnoreDataMember]
public Dictionary<Guid, ItemByNameCounts> UserItemCounts { get; set; }
public List<ItemByNameCounts> UserItemCountList { get; set; }
}
}

@ -50,6 +50,33 @@ namespace MediaBrowser.Controller.Entities.TV
get { return true; }
}
[IgnoreDataMember]
public int? AiredSeasonNumber
{
get
{
return AirsAfterSeasonNumber ?? AirsBeforeSeasonNumber ?? PhysicalSeasonNumber;
}
}
[IgnoreDataMember]
public int? PhysicalSeasonNumber
{
get
{
var value = ParentIndexNumber;
if (value.HasValue)
{
return value;
}
var season = Parent as Season;
return season != null ? season.IndexNumber : null;
}
}
/// <summary>
/// We roll up into series
/// </summary>
@ -59,7 +86,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
get
{
return Season;
return FindParent<Season>();
}
}
@ -151,20 +178,6 @@ namespace MediaBrowser.Controller.Entities.TV
get { return _series ?? (_series = FindParent<Series>()); }
}
/// <summary>
/// The _season
/// </summary>
private Season _season;
/// <summary>
/// This Episode's Season Instance
/// </summary>
/// <value>The season.</value>
[IgnoreDataMember]
public Season Season
{
get { return _season ?? (_season = FindParent<Season>()); }
}
/// <summary>
/// This is the ending episode number for double episodes.
/// </summary>
@ -221,5 +234,46 @@ namespace MediaBrowser.Controller.Entities.TV
{
get { return LocationType == Model.Entities.LocationType.Virtual && IsUnaired; }
}
[IgnoreDataMember]
public Guid? SeasonId
{
get
{
// First see if the parent is a Season
var season = Parent as Season;
if (season != null)
{
return season.Id;
}
var seasonNumber = ParentIndexNumber;
// Parent is a Series
if (seasonNumber.HasValue)
{
var series = Parent as Series;
if (series != null)
{
season = series.Children.OfType<Season>()
.FirstOrDefault(i => i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber.Value);
if (season != null)
{
return season.Id;
}
}
}
return null;
}
}
public override IEnumerable<string> GetDeletePaths()
{
return new[] { Path };
}
}
}

@ -1,9 +1,9 @@
using System.Linq;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities.TV
@ -94,6 +94,22 @@ namespace MediaBrowser.Controller.Entities.TV
get { return _series ?? (_series = FindParent<Series>()); }
}
[IgnoreDataMember]
public string SeriesPath
{
get
{
var series = Series;
if (series != null)
{
return series.Path;
}
return System.IO.Path.GetDirectoryName(Path);
}
}
/// <summary>
/// Our rating comes from our series
/// </summary>
@ -149,16 +165,34 @@ namespace MediaBrowser.Controller.Entities.TV
return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name;
}
private IEnumerable<Episode> GetEpisodes()
{
var series = Series;
if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
{
var seasonNumber = IndexNumber;
if (seasonNumber.HasValue)
{
return series.RecursiveChildren.OfType<Episode>()
.Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value);
}
}
return Children.OfType<Episode>();
}
[IgnoreDataMember]
public bool IsMissingSeason
{
get { return LocationType == Model.Entities.LocationType.Virtual && Children.OfType<Episode>().All(i => i.IsMissingEpisode); }
get { return LocationType == Model.Entities.LocationType.Virtual && GetEpisodes().All(i => i.IsMissingEpisode); }
}
[IgnoreDataMember]
public bool IsUnaired
{
get { return Children.OfType<Episode>().All(i => i.IsUnaired); }
get { return GetEpisodes().All(i => i.IsUnaired); }
}
[IgnoreDataMember]
@ -170,7 +204,13 @@ namespace MediaBrowser.Controller.Entities.TV
[IgnoreDataMember]
public bool IsMissingOrVirtualUnaired
{
get { return LocationType == Model.Entities.LocationType.Virtual && Children.OfType<Episode>().All(i => i.IsVirtualUnaired || i.IsMissingEpisode); }
get { return LocationType == Model.Entities.LocationType.Virtual && GetEpisodes().All(i => i.IsVirtualUnaired || i.IsMissingEpisode); }
}
[IgnoreDataMember]
public bool IsSpecialSeason
{
get { return (IndexNumber ?? -1) == 0; }
}
}
}

@ -4,6 +4,7 @@ using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities.TV
@ -11,7 +12,7 @@ namespace MediaBrowser.Controller.Entities.TV
/// <summary>
/// Class Series
/// </summary>
public class Series : Folder, IHasSoundtracks
public class Series : Folder, IHasSoundtracks, IHasTrailers
{
public List<Guid> SpecialFeatureIds { get; set; }
public List<Guid> SoundtrackIds { get; set; }
@ -24,8 +25,14 @@ namespace MediaBrowser.Controller.Entities.TV
SpecialFeatureIds = new List<Guid>();
SoundtrackIds = new List<Guid>();
RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>();
}
public List<Guid> LocalTrailerIds { get; set; }
public List<MediaUrl> RemoteTrailers { get; set; }
/// <summary>
/// Gets or sets the status.
/// </summary>
@ -94,5 +101,14 @@ namespace MediaBrowser.Controller.Entities.TV
return args;
}
[IgnoreDataMember]
public bool ContainsEpisodesWithoutSeasonFolders
{
get
{
return Children.OfType<Video>().Any();
}
}
}
}

@ -8,7 +8,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Class Trailer
/// </summary>
public class Trailer : Video, IHasCriticRating, IHasSoundtracks
public class Trailer : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasTrailers
{
public List<Guid> SoundtrackIds { get; set; }
@ -17,8 +17,25 @@ namespace MediaBrowser.Controller.Entities
RemoteTrailers = new List<MediaUrl>();
Taglines = new List<string>();
SoundtrackIds = new List<Guid>();
LocalTrailerIds = new List<Guid>();
}
public List<Guid> LocalTrailerIds { get; set; }
public List<MediaUrl> RemoteTrailers { get; set; }
/// <summary>
/// Gets or sets the budget.
/// </summary>
/// <value>The budget.</value>
public double? Budget { get; set; }
/// <summary>
/// Gets or sets the revenue.
/// </summary>
/// <value>The revenue.</value>
public double? Revenue { get; set; }
/// <summary>
/// Gets or sets the critic rating.
/// </summary>

@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Class Video
/// </summary>
public class Video : BaseItem, IHasMediaStreams
public class Video : BaseItem, IHasMediaStreams, IHasAspectRatio
{
public bool IsMultiPart { get; set; }
@ -65,6 +65,12 @@ namespace MediaBrowser.Controller.Entities
return GetPlayableStreamFiles(Path);
}
/// <summary>
/// Gets or sets the aspect ratio.
/// </summary>
/// <value>The aspect ratio.</value>
public string AspectRatio { get; set; }
/// <summary>
/// Should be overridden to return the proper folder where metadata lives
/// </summary>
@ -252,5 +258,17 @@ namespace MediaBrowser.Controller.Entities
}).ToList();
}
public override IEnumerable<string> GetDeletePaths()
{
if (!IsInMixedFolder)
{
if (VideoType == VideoType.VideoFile || VideoType == VideoType.Iso)
{
return new[] { System.IO.Path.GetDirectoryName(Path) };
}
}
return base.GetDeletePaths();
}
}
}

@ -12,11 +12,11 @@ namespace MediaBrowser.Controller.Entities
{
public Year()
{
UserItemCounts = new Dictionary<Guid, ItemByNameCounts>();
UserItemCountList = new List<ItemByNameCounts>();
}
[IgnoreDataMember]
public Dictionary<Guid, ItemByNameCounts> UserItemCounts { get; set; }
public List<ItemByNameCounts> UserItemCountList { get; set; }
/// <summary>
/// Gets the user data key.

@ -60,7 +60,7 @@ namespace MediaBrowser.Controller.Library
/// </summary>
/// <param name="name">The name.</param>
/// <returns>Task{Artist}.</returns>
Artist GetArtist(string name);
MusicArtist GetArtist(string name);
/// <summary>
/// Gets a Studio
@ -302,5 +302,18 @@ namespace MediaBrowser.Controller.Library
/// <param name="updateType">Type of the update.</param>
/// <returns>Task.</returns>
Task SaveMetadata(BaseItem item, ItemUpdateType updateType);
/// <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);
}
}

@ -257,7 +257,7 @@ namespace MediaBrowser.Controller.Library
if (match != null)
{
return ParseEpisodeNumber(match.Value);
return ParseEpisodeNumber(match.Groups["epnumber"].Value);
}
}

@ -0,0 +1,73 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.LiveTv
{
public class Channel : BaseItem, IItemByName
{
public Channel()
{
UserItemCountList = new List<ItemByNameCounts>();
}
/// <summary>
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
public override string GetUserDataKey()
{
return "Channel-" + Name;
}
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
/// <summary>
/// Gets or sets the number.
/// </summary>
/// <value>The number.</value>
public string ChannelNumber { get; set; }
/// <summary>
/// Get or sets the Id.
/// </summary>
/// <value>The id of the channel.</value>
public string ChannelId { get; set; }
/// <summary>
/// Gets or sets the name of the service.
/// </summary>
/// <value>The name of the service.</value>
public string ServiceName { get; set; }
/// <summary>
/// Gets or sets the type of the channel.
/// </summary>
/// <value>The type of the channel.</value>
public ChannelType ChannelType { get; set; }
protected override string CreateSortName()
{
double number = 0;
if (!string.IsNullOrEmpty(ChannelNumber))
{
double.TryParse(ChannelNumber, out number);
}
return number.ToString("000-") + (Name ?? string.Empty);
}
public override string MediaType
{
get
{
return ChannelType == ChannelType.Radio ? Model.Entities.MediaType.Audio : Model.Entities.MediaType.Video;
}
}
}
}

@ -25,12 +25,6 @@ namespace MediaBrowser.Controller.LiveTv
/// <value>The id of the channel.</value>
public string Id { get; set; }
/// <summary>
/// Gets or sets the name of the service.
/// </summary>
/// <value>The name of the service.</value>
public string ServiceName { get; set; }
/// <summary>
/// Gets or sets the type of the channel.
/// </summary>

@ -1,6 +1,8 @@
using System.Threading.Tasks;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.LiveTv
{
@ -15,6 +17,27 @@ namespace MediaBrowser.Controller.LiveTv
/// <value>The services.</value>
IReadOnlyList<ILiveTvService> Services { get; }
/// <summary>
/// Schedules the recording.
/// </summary>
/// <param name="programId">The program identifier.</param>
/// <returns>Task.</returns>
Task ScheduleRecording(string programId);
/// <summary>
/// Deletes the recording.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>Task.</returns>
Task DeleteRecording(string id);
/// <summary>
/// Cancels the timer.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>Task.</returns>
Task CancelTimer(string id);
/// <summary>
/// Adds the parts.
/// </summary>
@ -22,10 +45,65 @@ namespace MediaBrowser.Controller.LiveTv
void AddParts(IEnumerable<ILiveTvService> services);
/// <summary>
/// Gets the channel info dto.
/// Gets the channels.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>IEnumerable{Channel}.</returns>
QueryResult<ChannelInfoDto> GetChannels(ChannelQuery query);
/// <summary>
/// Gets the recording.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{RecordingInfoDto}.</returns>
Task<RecordingInfoDto> GetRecording(string id, CancellationToken cancellationToken);
/// <summary>
/// Gets the timer.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{TimerInfoDto}.</returns>
Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken);
/// <summary>
/// Gets the recordings.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>QueryResult{RecordingInfoDto}.</returns>
Task<QueryResult<RecordingInfoDto>> GetRecordings(RecordingQuery query, CancellationToken cancellationToken);
/// <summary>
/// Gets the timers.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{QueryResult{TimerInfoDto}}.</returns>
Task<QueryResult<TimerInfoDto>> GetTimers(TimerQuery query, CancellationToken cancellationToken);
/// <summary>
/// Gets the channel.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>Channel.</returns>
Channel GetChannel(string id);
/// <summary>
/// Gets the channel.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="userId">The user identifier.</param>
/// <returns>Channel.</returns>
ChannelInfoDto GetChannelInfoDto(string id, string userId);
/// <summary>
/// Gets the programs.
/// </summary>
/// <param name="info">The info.</param>
/// <returns>ChannelInfoDto.</returns>
ChannelInfoDto GetChannelInfoDto(ChannelInfo info);
/// <param name="query">The query.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>IEnumerable{ProgramInfo}.</returns>
Task<QueryResult<ProgramInfoDto>> GetPrograms(ProgramQuery query, CancellationToken cancellationToken);
}
}

@ -1,4 +1,4 @@
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Common.Net;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@ -23,20 +23,89 @@ namespace MediaBrowser.Controller.LiveTv
/// <returns>Task{IEnumerable{ChannelInfo}}.</returns>
Task<IEnumerable<ChannelInfo>> GetChannelsAsync(CancellationToken cancellationToken);
/// <summary>
/// Cancels the timer asynchronous.
/// </summary>
/// <param name="timerId">The timer identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task CancelTimerAsync(string timerId, CancellationToken cancellationToken);
/// <summary>
/// Deletes the recording asynchronous.
/// </summary>
/// <param name="recordingId">The recording identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task DeleteRecordingAsync(string recordingId, CancellationToken cancellationToken);
/// <summary>
/// Creates the timer asynchronous.
/// </summary>
/// <param name="info">The information.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken);
/// <summary>
/// Creates the series timer asynchronous.
/// </summary>
/// <param name="info">The information.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken);
/// <summary>
/// Updates the series timer asynchronous.
/// </summary>
/// <param name="info">The information.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task UpdateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken);
/// <summary>
/// Gets the channel image asynchronous.
/// </summary>
/// <param name="channelId">The channel identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{Stream}.</returns>
Task<ImageResponseInfo> GetChannelImageAsync(string channelId, CancellationToken cancellationToken);
/// <summary>
/// Gets the program image asynchronous.
/// </summary>
/// <param name="programId">The program identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{ImageResponseInfo}.</returns>
Task<ImageResponseInfo> GetProgramImageAsync(string programId, CancellationToken cancellationToken);
/// <summary>
/// Gets the recordings asynchronous.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RecordingInfo}}.</returns>
Task<IEnumerable<RecordingInfo>> GetRecordingsAsync(RecordingQuery query, CancellationToken cancellationToken);
Task<IEnumerable<RecordingInfo>> GetRecordingsAsync(CancellationToken cancellationToken);
/// <summary>
/// Gets the channel guides.
/// Gets the recordings asynchronous.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RecordingInfo}}.</returns>
Task<IEnumerable<TimerInfo>> GetTimersAsync(CancellationToken cancellationToken);
/// <summary>
/// Gets the series timers asynchronous.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{SeriesTimerInfo}}.</returns>
Task<IEnumerable<SeriesTimerInfo>> GetSeriesTimersAsync(CancellationToken cancellationToken);
/// <summary>
/// Gets the programs asynchronous.
/// </summary>
/// <param name="channelIdList">The channel identifier list.</param>
/// <param name="channelId">The channel identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{ChannelGuide}}.</returns>
Task<IEnumerable<ChannelGuide>> GetChannelGuidesAsync(IEnumerable<string> channelIdList, CancellationToken cancellationToken);
/// <returns>Task{IEnumerable{ProgramInfo}}.</returns>
Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, CancellationToken cancellationToken);
}
}

@ -0,0 +1,19 @@
using System.IO;
namespace MediaBrowser.Controller.LiveTv
{
public class ImageResponseInfo
{
/// <summary>
/// Gets or sets the stream.
/// </summary>
/// <value>The stream.</value>
public Stream Stream { get; set; }
/// <summary>
/// Gets or sets the type of the MIME.
/// </summary>
/// <value>The type of the MIME.</value>
public string MimeType { get; set; }
}
}

@ -0,0 +1,98 @@
using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.LiveTv
{
public class ProgramInfo
{
/// <summary>
/// Id of the program.
/// </summary>
public string Id { get; set; }
/// <summary>
/// Gets or sets the channel identifier.
/// </summary>
/// <value>The channel identifier.</value>
public string ChannelId { get; set; }
/// <summary>
/// Name of the program
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the official rating.
/// </summary>
/// <value>The official rating.</value>
public string OfficialRating { get; set; }
/// <summary>
/// Description of the progam.
/// </summary>
public string Description { get; set; }
/// <summary>
/// The start date of the program, in UTC.
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// The end date of the program, in UTC.
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// Gets or sets the aspect ratio.
/// </summary>
/// <value>The aspect ratio.</value>
public string AspectRatio { get; set; }
/// <summary>
/// Genre of the program.
/// </summary>
public List<string> Genres { get; set; }
/// <summary>
/// Gets or sets the quality.
/// </summary>
/// <value>The quality.</value>
public ProgramVideoQuality Quality { get; set; }
/// <summary>
/// Gets or sets the original air date.
/// </summary>
/// <value>The original air date.</value>
public DateTime? OriginalAirDate { get; set; }
/// <summary>
/// Gets or sets the audio.
/// </summary>
/// <value>The audio.</value>
public ProgramAudio Audio { get; set; }
/// <summary>
/// Gets or sets the community rating.
/// </summary>
/// <value>The community rating.</value>
public float? CommunityRating { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is repeat.
/// </summary>
/// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value>
public bool IsRepeat { get; set; }
/// <summary>
/// Gets or sets the episode title.
/// </summary>
/// <value>The episode title.</value>
public string EpisodeTitle { get; set; }
public ProgramInfo()
{
Genres = new List<string>();
}
}
}

@ -0,0 +1,102 @@
using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.LiveTv
{
public class RecordingInfo
{
/// <summary>
/// Id of the recording.
/// </summary>
public string Id { get; set; }
/// <summary>
/// ChannelId of the recording.
/// </summary>
public string ChannelId { get; set; }
/// <summary>
/// ChannelName of the recording.
/// </summary>
public string ChannelName { get; set; }
/// <summary>
/// Gets or sets the type of the channel.
/// </summary>
/// <value>The type of the channel.</value>
public ChannelType ChannelType { get; set; }
/// <summary>
/// Name of the recording.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the path.
/// </summary>
/// <value>The path.</value>
public string Path { get; set; }
/// <summary>
/// Description of the recording.
/// </summary>
public string Description { get; set; }
/// <summary>
/// The start date of the recording, in UTC.
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// The end date of the recording, in UTC.
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// Gets or sets the program identifier.
/// </summary>
/// <value>The program identifier.</value>
public string ProgramId { get; set; }
/// <summary>
/// Gets or sets the status.
/// </summary>
/// <value>The status.</value>
public RecordingStatus Status { get; set; }
/// <summary>
/// Genre of the program.
/// </summary>
public List<string> Genres { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is repeat.
/// </summary>
/// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value>
public bool IsRepeat { get; set; }
/// <summary>
/// Gets or sets the episode title.
/// </summary>
/// <value>The episode title.</value>
public string EpisodeTitle { get; set; }
/// <summary>
/// Gets or sets the official rating.
/// </summary>
/// <value>The official rating.</value>
public string OfficialRating { get; set; }
/// <summary>
/// Gets or sets the community rating.
/// </summary>
/// <value>The community rating.</value>
public float? CommunityRating { get; set; }
public RecordingInfo()
{
Genres = new List<string>();
}
}
}

@ -0,0 +1,85 @@
using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.LiveTv
{
public class SeriesTimerInfo
{
/// <summary>
/// Id of the recording.
/// </summary>
public string Id { get; set; }
/// <summary>
/// ChannelId of the recording.
/// </summary>
public string ChannelId { get; set; }
/// <summary>
/// ChannelName of the recording.
/// </summary>
public string ChannelName { get; set; }
/// <summary>
/// Gets or sets the program identifier.
/// </summary>
/// <value>The program identifier.</value>
public string ProgramId { get; set; }
/// <summary>
/// Name of the recording.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Description of the recording.
/// </summary>
public string Description { get; set; }
/// <summary>
/// The start date of the recording, in UTC.
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// The end date of the recording, in UTC.
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// Gets or sets the pre padding seconds.
/// </summary>
/// <value>The pre padding seconds.</value>
public int PrePaddingSeconds { get; set; }
/// <summary>
/// Gets or sets the post padding seconds.
/// </summary>
/// <value>The post padding seconds.</value>
public int PostPaddingSeconds { get; set; }
/// <summary>
/// Gets or sets the type of the recurrence.
/// </summary>
/// <value>The type of the recurrence.</value>
public RecurrenceType RecurrenceType { get; set; }
/// <summary>
/// Gets or sets the days.
/// </summary>
/// <value>The days.</value>
public List<DayOfWeek> Days { get; set; }
/// <summary>
/// Gets or sets the priority.
/// </summary>
/// <value>The priority.</value>
public int Priority { get; set; }
public SeriesTimerInfo()
{
Days = new List<DayOfWeek>();
}
}
}

@ -0,0 +1,73 @@
using MediaBrowser.Model.LiveTv;
using System;
namespace MediaBrowser.Controller.LiveTv
{
public class TimerInfo
{
/// <summary>
/// Id of the recording.
/// </summary>
public string Id { get; set; }
/// <summary>
/// Gets or sets the series timer identifier.
/// </summary>
/// <value>The series timer identifier.</value>
public string SeriesTimerId { get; set; }
/// <summary>
/// ChannelId of the recording.
/// </summary>
public string ChannelId { get; set; }
/// <summary>
/// ChannelName of the recording.
/// </summary>
public string ChannelName { get; set; }
/// <summary>
/// Gets or sets the program identifier.
/// </summary>
/// <value>The program identifier.</value>
public string ProgramId { get; set; }
/// <summary>
/// Name of the recording.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Description of the recording.
/// </summary>
public string Description { get; set; }
/// <summary>
/// The start date of the recording, in UTC.
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// The end date of the recording, in UTC.
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// Gets or sets the status.
/// </summary>
/// <value>The status.</value>
public RecordingStatus Status { get; set; }
/// <summary>
/// Gets or sets the pre padding seconds.
/// </summary>
/// <value>The pre padding seconds.</value>
public int PrePaddingSeconds { get; set; }
/// <summary>
/// Gets or sets the post padding seconds.
/// </summary>
/// <value>The post padding seconds.</value>
public int PostPaddingSeconds { get; set; }
}
}

@ -89,8 +89,11 @@
<Compile Include="Entities\GameGenre.cs" />
<Compile Include="Entities\GameSystem.cs" />
<Compile Include="Entities\IByReferenceItem.cs" />
<Compile Include="Entities\IHasAspectRatio.cs" />
<Compile Include="Entities\IHasBudget.cs" />
<Compile Include="Entities\IHasCriticRating.cs" />
<Compile Include="Entities\IHasSoundtracks.cs" />
<Compile Include="Entities\IHasTrailers.cs" />
<Compile Include="Entities\IItemByName.cs" />
<Compile Include="Entities\ILibraryItem.cs" />
<Compile Include="Entities\ImageSourceInfo.cs" />
@ -103,18 +106,24 @@
<Compile Include="Library\ItemUpdateType.cs" />
<Compile Include="Library\IUserDataManager.cs" />
<Compile Include="Library\UserDataSaveEventArgs.cs" />
<Compile Include="LiveTv\Channel.cs" />
<Compile Include="LiveTv\ChannelInfo.cs" />
<Compile Include="LiveTv\ILiveTvManager.cs" />
<Compile Include="LiveTv\ILiveTvService.cs" />
<Compile Include="LiveTv\ImageResponseInfo.cs" />
<Compile Include="LiveTv\ProgramInfo.cs" />
<Compile Include="LiveTv\RecordingInfo.cs" />
<Compile Include="LiveTv\SeriesTimerInfo.cs" />
<Compile Include="LiveTv\TimerInfo.cs" />
<Compile Include="Localization\ILocalizationManager.cs" />
<Compile Include="Notifications\INotificationsRepository.cs" />
<Compile Include="Notifications\NotificationUpdateEventArgs.cs" />
<Compile Include="Providers\IDynamicInfoProvider.cs" />
<Compile Include="Providers\IImageProvider.cs" />
<Compile Include="Providers\NameParser.cs" />
<Compile Include="Session\ISessionManager.cs" />
<Compile Include="Drawing\ImageExtensions.cs" />
<Compile Include="Entities\AggregateFolder.cs" />
<Compile Include="Entities\Audio\Artist.cs" />
<Compile Include="Entities\Audio\Audio.cs" />
<Compile Include="Entities\Audio\MusicAlbum.cs" />
<Compile Include="Entities\Audio\MusicArtist.cs" />

@ -69,6 +69,12 @@ namespace MediaBrowser.Controller.Providers
item.People.Clear();
item.Tags.Clear();
var hasTrailers = item as IHasTrailers;
if (hasTrailers != null)
{
hasTrailers.RemoteTrailers.Clear();
}
//Fetch(item, metadataFile, settings, Encoding.GetEncoding("ISO-8859-1"), cancellationToken);
Fetch(item, metadataFile, settings, Encoding.UTF8, cancellationToken);
}
@ -161,10 +167,14 @@ namespace MediaBrowser.Controller.Providers
case "Budget":
{
var text = reader.ReadElementContentAsString();
double value;
if (double.TryParse(text, NumberStyles.Any, _usCulture, out value))
var hasBudget = item as IHasBudget;
if (hasBudget != null)
{
item.Budget = value;
double value;
if (double.TryParse(text, NumberStyles.Any, _usCulture, out value))
{
hasBudget.Budget = value;
}
}
break;
@ -173,10 +183,14 @@ namespace MediaBrowser.Controller.Providers
case "Revenue":
{
var text = reader.ReadElementContentAsString();
double value;
if (double.TryParse(text, NumberStyles.Any, _usCulture, out value))
var hasBudget = item as IHasBudget;
if (hasBudget != null)
{
item.Revenue = value;
double value;
if (double.TryParse(text, NumberStyles.Any, _usCulture, out value))
{
hasBudget.Revenue = value;
}
}
break;
@ -375,9 +389,10 @@ namespace MediaBrowser.Controller.Providers
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
var hasAspectRatio = item as IHasAspectRatio;
if (!string.IsNullOrWhiteSpace(val) && hasAspectRatio != null)
{
item.AspectRatio = val;
hasAspectRatio.AspectRatio = val;
}
break;
}
@ -474,9 +489,26 @@ namespace MediaBrowser.Controller.Providers
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
var hasTrailers = item as IHasTrailers;
if (hasTrailers != null)
{
item.AddTrailerUrl(val, false);
if (!string.IsNullOrWhiteSpace(val))
{
hasTrailers.AddTrailerUrl(val, false);
}
}
break;
}
case "Trailers":
{
using (var subtree = reader.ReadSubtree())
{
var hasTrailers = item as IHasTrailers;
if (hasTrailers != null)
{
FetchDataFromTrailersNode(subtree, hasTrailers);
}
}
break;
}
@ -921,6 +953,35 @@ namespace MediaBrowser.Controller.Providers
}
}
private void FetchDataFromTrailersNode(XmlReader reader, IHasTrailers item)
{
reader.MoveToContent();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Trailer":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
item.AddTrailerUrl(val, false);
}
break;
}
default:
reader.Skip();
break;
}
}
}
}
protected async Task FetchChaptersFromXmlNode(BaseItem item, XmlReader reader, IItemRepository repository, CancellationToken cancellationToken)
{
var runtime = item.RunTimeTicks ?? 0;
@ -1071,9 +1132,10 @@ namespace MediaBrowser.Controller.Providers
/// <returns>IEnumerable{PersonInfo}.</returns>
private IEnumerable<PersonInfo> GetPersonsFromXmlNode(XmlReader reader)
{
var names = new List<string>();
var name = string.Empty;
var type = "Actor"; // If type is not specified assume actor
var role = string.Empty;
int? sortOrder = null;
reader.MoveToContent();
@ -1084,7 +1146,7 @@ namespace MediaBrowser.Controller.Providers
switch (reader.Name)
{
case "Name":
names.AddRange(SplitNames(reader.ReadElementContentAsString()));
name = reader.ReadElementContentAsString() ?? string.Empty;
break;
case "Type":
@ -1108,6 +1170,20 @@ namespace MediaBrowser.Controller.Providers
}
break;
}
case "SortOrder":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
int intVal;
if (int.TryParse(val, NumberStyles.Integer, _usCulture, out intVal))
{
sortOrder = intVal;
}
}
break;
}
default:
reader.Skip();
@ -1116,7 +1192,15 @@ namespace MediaBrowser.Controller.Providers
}
}
return names.Select(n => new PersonInfo { Name = n.Trim(), Role = role, Type = type });
var personInfo = new PersonInfo
{
Name = name.Trim(),
Role = role,
Type = type,
SortOrder = sortOrder
};
return new[] { personInfo };
}
/// <summary>

@ -0,0 +1,39 @@
using System;
using System.Text.RegularExpressions;
namespace MediaBrowser.Controller.Providers
{
public static class NameParser
{
static readonly Regex[] NameMatches = new[] {
new Regex(@"(?<name>.*)\((?<year>\d{4})\)"), // matches "My Movie (2001)" and gives us the name and the year
new Regex(@"(?<name>.*)(\.(?<year>\d{4})(\.|$)).*$"),
new Regex(@"(?<name>.*)") // last resort matches the whole string as the name
};
/// <summary>
/// Parses the name.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="justName">Name of the just.</param>
/// <param name="year">The year.</param>
public static void ParseName(string name, out string justName, out int? year)
{
justName = null;
year = null;
foreach (var re in NameMatches)
{
Match m = re.Match(name);
if (m.Success)
{
justName = m.Groups["name"].Value.Trim();
string y = m.Groups["year"] != null ? m.Groups["year"].Value : null;
int temp;
year = Int32.TryParse(y, out temp) ? temp : (int?)null;
break;
}
}
}
}
}

@ -224,27 +224,36 @@
<Compile Include="..\MediaBrowser.Model\IO\IZipClient.cs">
<Link>IO\IZipClient.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\ChannelGuide.cs">
<Link>LiveTv\ChannelGuide.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\ChannelInfoDto.cs">
<Link>LiveTv\ChannelInfoDto.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\ChannelQuery.cs">
<Link>LiveTv\ChannelQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\ChannelType.cs">
<Link>LiveTv\ChannelType.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvServiceInfo.cs">
<Link>LiveTv\LiveTvServiceInfo.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\ProgramInfo.cs">
<Link>LiveTv\ProgramInfo.cs</Link>
<Compile Include="..\MediaBrowser.Model\LiveTv\ProgramInfoDto.cs">
<Link>LiveTv\ProgramInfoDto.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\RecordingInfo.cs">
<Link>LiveTv\RecordingInfo.cs</Link>
<Compile Include="..\MediaBrowser.Model\LiveTv\ProgramQuery.cs">
<Link>LiveTv\ProgramQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\RecordingInfoDto.cs">
<Link>LiveTv\RecordingInfoDto.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\RecordingQuery.cs">
<Link>LiveTv\RecordingQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\RecordingStatus.cs">
<Link>LiveTv\RecordingStatus.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\TimerInfoDto.cs">
<Link>LiveTv\TimerInfoDto.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Logging\ILogger.cs">
<Link>Logging\ILogger.cs</Link>
</Compile>
@ -311,6 +320,9 @@
<Compile Include="..\MediaBrowser.Model\Querying\ArtistsQuery.cs">
<Link>Querying\ArtistsQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\EpisodeQuery.cs">
<Link>Querying\EpisodeQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\ItemCountsQuery.cs">
<Link>Querying\ItemCountsQuery.cs</Link>
</Compile>
@ -323,9 +335,6 @@
<Compile Include="..\MediaBrowser.Model\Querying\ItemQuery.cs">
<Link>Querying\ItemQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\ItemReviewsResult.cs">
<Link>Querying\ItemReviewsResult.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\ItemsByNameQuery.cs">
<Link>Querying\ItemsByNameQuery.cs</Link>
</Compile>
@ -341,6 +350,9 @@
<Compile Include="..\MediaBrowser.Model\Querying\PersonsQuery.cs">
<Link>Querying\PersonsQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\QueryResult.cs">
<Link>Querying\QueryResult.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\SessionQuery.cs">
<Link>Querying\SessionQuery.cs</Link>
</Compile>

@ -211,27 +211,36 @@
<Compile Include="..\MediaBrowser.Model\IO\IZipClient.cs">
<Link>IO\IZipClient.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\ChannelGuide.cs">
<Link>LiveTv\ChannelGuide.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\ChannelInfoDto.cs">
<Link>LiveTv\ChannelInfoDto.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\ChannelQuery.cs">
<Link>LiveTv\ChannelQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\ChannelType.cs">
<Link>LiveTv\ChannelType.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvServiceInfo.cs">
<Link>LiveTv\LiveTvServiceInfo.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\ProgramInfo.cs">
<Link>LiveTv\ProgramInfo.cs</Link>
<Compile Include="..\MediaBrowser.Model\LiveTv\ProgramInfoDto.cs">
<Link>LiveTv\ProgramInfoDto.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\RecordingInfo.cs">
<Link>LiveTv\RecordingInfo.cs</Link>
<Compile Include="..\MediaBrowser.Model\LiveTv\ProgramQuery.cs">
<Link>LiveTv\ProgramQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\RecordingInfoDto.cs">
<Link>LiveTv\RecordingInfoDto.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\RecordingQuery.cs">
<Link>LiveTv\RecordingQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\RecordingStatus.cs">
<Link>LiveTv\RecordingStatus.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\LiveTv\TimerInfoDto.cs">
<Link>LiveTv\TimerInfoDto.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Logging\ILogger.cs">
<Link>Logging\ILogger.cs</Link>
</Compile>
@ -298,6 +307,9 @@
<Compile Include="..\MediaBrowser.Model\Querying\ArtistsQuery.cs">
<Link>Querying\ArtistsQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\EpisodeQuery.cs">
<Link>Querying\EpisodeQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\ItemCountsQuery.cs">
<Link>Querying\ItemCountsQuery.cs</Link>
</Compile>
@ -310,9 +322,6 @@
<Compile Include="..\MediaBrowser.Model\Querying\ItemQuery.cs">
<Link>Querying\ItemQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\ItemReviewsResult.cs">
<Link>Querying\ItemReviewsResult.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\ItemsByNameQuery.cs">
<Link>Querying\ItemsByNameQuery.cs</Link>
</Compile>
@ -328,6 +337,9 @@
<Compile Include="..\MediaBrowser.Model\Querying\PersonsQuery.cs">
<Link>Querying\PersonsQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\QueryResult.cs">
<Link>Querying\QueryResult.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Querying\SessionQuery.cs">
<Link>Querying\SessionQuery.cs</Link>
</Compile>

@ -83,7 +83,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="startIndex">The start index.</param>
/// <param name="limit">The limit.</param>
/// <returns>Task{ItemReviewsResult}.</returns>
Task<ItemReviewsResult> GetCriticReviews(string itemId, CancellationToken cancellationToken, int? startIndex = null, int? limit = null);
Task<QueryResult<ItemReview>> GetCriticReviews(string itemId, CancellationToken cancellationToken, int? startIndex = null, int? limit = null);
/// <summary>
/// Gets the theme songs async.
@ -225,6 +225,20 @@ namespace MediaBrowser.Model.ApiClient
/// <returns>Task{ItemCounts}.</returns>
Task<ItemCounts> GetItemCountsAsync(ItemCountsQuery query);
/// <summary>
/// Gets the episodes asynchronous.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>Task{ItemsResult}.</returns>
Task<ItemsResult> GetEpisodesAsync(EpisodeQuery query);
/// <summary>
/// Gets the seasons asynchronous.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>Task{ItemsResult}.</returns>
Task<ItemsResult> GetSeasonsAsync(SeasonQuery query);
/// <summary>
/// Queries for items
/// </summary>

@ -312,6 +312,12 @@ namespace MediaBrowser.Model.Dto
/// <value>The series id.</value>
public string SeriesId { get; set; }
/// <summary>
/// Gets or sets the season identifier.
/// </summary>
/// <value>The season identifier.</value>
public string SeasonId { get; set; }
/// <summary>
/// Gets or sets the special feature count.
/// </summary>

@ -1,4 +1,5 @@

using System;
namespace MediaBrowser.Model.Dto
{
/// <summary>
@ -6,6 +7,8 @@ namespace MediaBrowser.Model.Dto
/// </summary>
public class ItemByNameCounts
{
public Guid UserId { get; set; }
/// <summary>
/// Gets or sets the total count.
/// </summary>

@ -1,23 +0,0 @@
using System.Collections.Generic;
namespace MediaBrowser.Model.LiveTv
{
public class ChannelGuide
{
/// <summary>
/// Gets or sets the name of the service.
/// </summary>
/// <value>The name of the service.</value>
public string ServiceName { get; set; }
/// <summary>
/// ChannelId for the EPG.
/// </summary>
public string ChannelId { get; set; }
/// <summary>
/// List of all the programs for a specific channel
/// </summary>
public List<ProgramInfo> Programs { get; set; }
}
}

@ -1,4 +1,8 @@

using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Model.LiveTv
{
/// <summary>
@ -18,6 +22,12 @@ namespace MediaBrowser.Model.LiveTv
/// <value>The identifier.</value>
public string Id { get; set; }
/// <summary>
/// Gets or sets the image tags.
/// </summary>
/// <value>The image tags.</value>
public Dictionary<ImageType, Guid> ImageTags { get; set; }
/// <summary>
/// Gets or sets the number.
/// </summary>
@ -35,5 +45,28 @@ namespace MediaBrowser.Model.LiveTv
/// </summary>
/// <value>The type of the channel.</value>
public ChannelType ChannelType { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public string Type { get; set; }
/// <summary>
/// Gets or sets the type of the media.
/// </summary>
/// <value>The type of the media.</value>
public string MediaType { get; set; }
/// <summary>
/// Gets or sets the user data.
/// </summary>
/// <value>The user data.</value>
public UserItemDataDto UserData { get; set; }
public ChannelInfoDto()
{
ImageTags = new Dictionary<ImageType, Guid>();
}
}
}

@ -0,0 +1,27 @@

namespace MediaBrowser.Model.LiveTv
{
/// <summary>
/// Class ChannelQuery.
/// </summary>
public class ChannelQuery
{
/// <summary>
/// Gets or sets the name of the service.
/// </summary>
/// <value>The name of the service.</value>
public string ServiceName { get; set; }
/// <summary>
/// Gets or sets the type of the channel.
/// </summary>
/// <value>The type of the channel.</value>
public ChannelType? ChannelType { get; set; }
/// <summary>
/// Gets or sets the user identifier.
/// </summary>
/// <value>The user identifier.</value>
public string UserId { get; set; }
}
}

@ -1,37 +0,0 @@
using System;
namespace MediaBrowser.Model.LiveTv
{
public class ProgramInfo
{
/// <summary>
/// Id of the program.
/// </summary>
public string Id { get; set; }
/// <summary>
/// Name of the program
/// </summary>
public string Name { get; set; }
/// <summary>
/// Description of the progam.
/// </summary>
public string Description { get; set; }
/// <summary>
/// The start date of the program, in UTC.
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// The end date of the program, in UTC.
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// Genre of the program.
/// </summary>
public string Genre { get; set; }
}
}

@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
namespace MediaBrowser.Model.LiveTv
{
public class ProgramInfoDto
{
/// <summary>
/// Id of the program.
/// </summary>
public string Id { get; set; }
/// <summary>
/// Gets or sets the external identifier.
/// </summary>
/// <value>The external identifier.</value>
public string ExternalId { get; set; }
/// <summary>
/// Gets or sets the channel identifier.
/// </summary>
/// <value>The channel identifier.</value>
public string ChannelId { get; set; }
/// <summary>
/// Gets or sets the community rating.
/// </summary>
/// <value>The community rating.</value>
public float? CommunityRating { get; set; }
/// <summary>
/// Gets or sets the aspect ratio.
/// </summary>
/// <value>The aspect ratio.</value>
public string AspectRatio { get; set; }
/// <summary>
/// Gets or sets the official rating.
/// </summary>
/// <value>The official rating.</value>
public string OfficialRating { get; set; }
/// <summary>
/// Gets or sets the name of the service.
/// </summary>
/// <value>The name of the service.</value>
public string ServiceName { get; set; }
/// <summary>
/// Name of the program
/// </summary>
public string Name { get; set; }
/// <summary>
/// Description of the progam.
/// </summary>
public string Description { get; set; }
/// <summary>
/// The start date of the program, in UTC.
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// The end date of the program, in UTC.
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// Genre of the program.
/// </summary>
public List<string> Genres { get; set; }
/// <summary>
/// Gets or sets the quality.
/// </summary>
/// <value>The quality.</value>
public ProgramVideoQuality Quality { get; set; }
/// <summary>
/// Gets or sets the audio.
/// </summary>
/// <value>The audio.</value>
public ProgramAudio Audio { get; set; }
/// <summary>
/// Gets or sets the original air date.
/// </summary>
/// <value>The original air date.</value>
public DateTime? OriginalAirDate { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is repeat.
/// </summary>
/// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value>
public bool IsRepeat { get; set; }
/// <summary>
/// Gets or sets the episode title.
/// </summary>
/// <value>The episode title.</value>
public string EpisodeTitle { get; set; }
public ProgramInfoDto()
{
Genres = new List<string>();
}
}
public enum ProgramVideoQuality
{
StandardDefinition,
HighDefinition
}
public enum ProgramAudio
{
Stereo
}
}

@ -0,0 +1,31 @@
namespace MediaBrowser.Model.LiveTv
{
/// <summary>
/// Class ProgramQuery.
/// </summary>
public class ProgramQuery
{
/// <summary>
/// Gets or sets the name of the service.
/// </summary>
/// <value>The name of the service.</value>
public string ServiceName { get; set; }
/// <summary>
/// Gets or sets the channel identifier.
/// </summary>
/// <value>The channel identifier.</value>
public string[] ChannelIdList { get; set; }
/// <summary>
/// Gets or sets the user identifier.
/// </summary>
/// <value>The user identifier.</value>
public string UserId { get; set; }
public ProgramQuery()
{
ChannelIdList = new string[] { };
}
}
}

@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
namespace MediaBrowser.Model.LiveTv
{
public class RecordingInfoDto
{
/// <summary>
/// Id of the recording.
/// </summary>
public string Id { get; set; }
/// <summary>
/// Gets or sets the external identifier.
/// </summary>
/// <value>The external identifier.</value>
public string ExternalId { get; set; }
/// <summary>
/// Gets or sets the program identifier.
/// </summary>
/// <value>The program identifier.</value>
public string ProgramId { get; set; }
/// <summary>
/// ChannelId of the recording.
/// </summary>
public string ChannelId { get; set; }
/// <summary>
/// ChannelName of the recording.
/// </summary>
public string ChannelName { get; set; }
/// <summary>
/// Name of the recording.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the path.
/// </summary>
/// <value>The path.</value>
public string Path { get; set; }
/// <summary>
/// Description of the recording.
/// </summary>
public string Description { get; set; }
/// <summary>
/// The start date of the recording, in UTC.
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// The end date of the recording, in UTC.
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// Gets or sets the status.
/// </summary>
/// <value>The status.</value>
public RecordingStatus Status { get; set; }
/// <summary>
/// Genre of the program.
/// </summary>
public List<string> Genres { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is repeat.
/// </summary>
/// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value>
public bool IsRepeat { get; set; }
/// <summary>
/// Gets or sets the episode title.
/// </summary>
/// <value>The episode title.</value>
public string EpisodeTitle { get; set; }
/// <summary>
/// Gets or sets the duration ms.
/// </summary>
/// <value>The duration ms.</value>
public int DurationMs { get; set; }
/// <summary>
/// Gets or sets the type of the media.
/// </summary>
/// <value>The type of the media.</value>
public string MediaType { get; set; }
/// <summary>
/// Gets or sets the type of the channel.
/// </summary>
/// <value>The type of the channel.</value>
public ChannelType ChannelType { get; set; }
/// <summary>
/// Gets or sets the official rating.
/// </summary>
/// <value>The official rating.</value>
public string OfficialRating { get; set; }
/// <summary>
/// Gets or sets the community rating.
/// </summary>
/// <value>The community rating.</value>
public float? CommunityRating { get; set; }
public RecordingInfoDto()
{
Genres = new List<string>();
}
}
}

@ -6,9 +6,30 @@
public class RecordingQuery
{
/// <summary>
/// Gets or sets a value indicating whether this instance has recorded.
/// Gets or sets the channel identifier.
/// </summary>
/// <value><c>null</c> if [has recorded] contains no value, <c>true</c> if [has recorded]; otherwise, <c>false</c>.</value>
public bool? HasRecorded { get; set; }
/// <value>The channel identifier.</value>
public string ChannelId { get; set; }
/// <summary>
/// Gets or sets the name of the service.
/// </summary>
/// <value>The name of the service.</value>
public string ServiceName { get; set; }
}
public class TimerQuery
{
/// <summary>
/// Gets or sets the channel identifier.
/// </summary>
/// <value>The channel identifier.</value>
public string ChannelId { get; set; }
/// <summary>
/// Gets or sets the name of the service.
/// </summary>
/// <value>The name of the service.</value>
public string ServiceName { get; set; }
}
}

@ -0,0 +1,22 @@

namespace MediaBrowser.Model.LiveTv
{
public enum RecordingStatus
{
Pending,
InProgress,
Completed,
CompletedWithError,
Conflicted,
Deleted
}
public enum RecurrenceType
{
Manual,
NewProgramEventsOneChannel,
AllProgramEventsOneChannel,
NewProgramEventsAllChannels,
AllProgramEventsAllChannels
}
}

@ -1,15 +1,20 @@
using System;
using System.Collections.Generic;
namespace MediaBrowser.Model.LiveTv
{
public class RecordingInfo
public class TimerInfoDto
{
/// <summary>
/// Id of the recording.
/// </summary>
public string Id { get; set; }
/// <summary>
/// Gets or sets the external identifier.
/// </summary>
/// <value>The external identifier.</value>
public string ExternalId { get; set; }
/// <summary>
/// ChannelId of the recording.
/// </summary>
@ -20,6 +25,12 @@ namespace MediaBrowser.Model.LiveTv
/// </summary>
public string ChannelName { get; set; }
/// <summary>
/// Gets or sets the program identifier.
/// </summary>
/// <value>The program identifier.</value>
public string ProgramId { get; set; }
/// <summary>
/// Name of the recording.
/// </summary>
@ -41,38 +52,33 @@ namespace MediaBrowser.Model.LiveTv
public DateTime EndDate { get; set; }
/// <summary>
/// Status of the recording.
/// </summary>
public string Status { get; set; } //TODO: Enum for status?? Difference NextPvr,Argus,...
/// <summary>
/// Quality of the Recording.
/// </summary>
public string Quality { get; set; } // TODO: Enum for quality?? Difference NextPvr,Argus,...
/// <summary>
/// Recurring recording?
/// Gets or sets the status.
/// </summary>
public bool Recurring { get; set; }
/// <value>The status.</value>
public RecordingStatus Status { get; set; }
/// <summary>
/// Parent recurring.
/// Gets or sets the series timer identifier.
/// </summary>
public string RecurringParent { get; set; }
/// <value>The series timer identifier.</value>
public string SeriesTimerId { get; set; }
/// <summary>
/// Start date for the recurring, in UTC.
/// Gets or sets the pre padding seconds.
/// </summary>
public DateTime RecurrringStartDate { get; set; }
/// <value>The pre padding seconds.</value>
public int PrePaddingSeconds { get; set; }
/// <summary>
/// End date for the recurring, in UTC
/// Gets or sets the post padding seconds.
/// </summary>
public DateTime RecurringEndDate { get; set; }
/// <value>The post padding seconds.</value>
public int PostPaddingSeconds { get; set; }
/// <summary>
/// When do we need the recording?
/// Gets or sets the duration ms.
/// </summary>
public List<string> DayMask { get; set; }
/// <value>The duration ms.</value>
public int DurationMs { get; set; }
}
}
}

@ -60,9 +60,13 @@
<Compile Include="Dto\ItemCounts.cs" />
<Compile Include="Dto\ItemIndex.cs" />
<Compile Include="Entities\PackageReviewInfo.cs" />
<Compile Include="LiveTv\ChannelGuide.cs" />
<Compile Include="LiveTv\ProgramInfo.cs" />
<Compile Include="LiveTv\ChannelInfoDto.cs" />
<Compile Include="LiveTv\ChannelQuery.cs" />
<Compile Include="LiveTv\ProgramInfoDto.cs" />
<Compile Include="LiveTv\ProgramQuery.cs" />
<Compile Include="LiveTv\RecordingQuery.cs" />
<Compile Include="LiveTv\RecordingStatus.cs" />
<Compile Include="LiveTv\TimerInfoDto.cs" />
<Compile Include="Providers\ImageProviderInfo.cs" />
<Compile Include="Providers\RemoteImageInfo.cs" />
<Compile Include="Dto\StudioDto.cs" />
@ -76,10 +80,9 @@
<Compile Include="IO\IIsoManager.cs" />
<Compile Include="IO\IIsoMount.cs" />
<Compile Include="IO\IIsoMounter.cs" />
<Compile Include="LiveTv\ChannelInfoDto.cs" />
<Compile Include="LiveTv\ChannelType.cs" />
<Compile Include="LiveTv\LiveTvServiceInfo.cs" />
<Compile Include="LiveTv\RecordingInfo.cs" />
<Compile Include="LiveTv\RecordingInfoDto.cs" />
<Compile Include="Net\WebSocketMessage.cs" />
<Compile Include="Net\WebSocketMessageType.cs" />
<Compile Include="Net\WebSocketState.cs" />
@ -90,11 +93,12 @@
<Compile Include="Notifications\NotificationsSummary.cs" />
<Compile Include="Providers\RemoteImageResult.cs" />
<Compile Include="Querying\ArtistsQuery.cs" />
<Compile Include="Querying\EpisodeQuery.cs" />
<Compile Include="Querying\ItemCountsQuery.cs" />
<Compile Include="Querying\ItemReviewsResult.cs" />
<Compile Include="Querying\ItemsByNameQuery.cs" />
<Compile Include="Entities\BaseItemInfo.cs" />
<Compile Include="Querying\NextUpQuery.cs" />
<Compile Include="Querying\QueryResult.cs" />
<Compile Include="Querying\SessionQuery.cs" />
<Compile Include="Querying\SimilarItemsQuery.cs" />
<Compile Include="Querying\UserQuery.cs" />

@ -0,0 +1,45 @@

namespace MediaBrowser.Model.Querying
{
public class EpisodeQuery
{
public string UserId { get; set; }
public string SeasonId { get; set; }
public string SeriesId { get; set; }
public bool? IsMissing { get; set; }
public bool? IsVirtualUnaired { get; set; }
public int? SeasonNumber { get; set; }
public ItemFields[] Fields { get; set; }
public EpisodeQuery()
{
Fields = new ItemFields[] { };
}
}
public class SeasonQuery
{
public string UserId { get; set; }
public string SeriesId { get; set; }
public bool? IsMissing { get; set; }
public bool? IsVirtualUnaired { get; set; }
public ItemFields[] Fields { get; set; }
public bool? IsSpecialSeason { get; set; }
public SeasonQuery()
{
Fields = new ItemFields[] { };
}
}
}

@ -266,6 +266,10 @@ namespace MediaBrowser.Model.Querying
public double? MinCriticRating { get; set; }
public int? AiredDuringSeason { get; set; }
public DateTime? MinPremiereDate { get; set; }
public DateTime? MaxPremiereDate { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ItemQuery" /> class.

@ -6,6 +6,7 @@ namespace MediaBrowser.Model.Querying
/// </summary>
public static class ItemSortBy
{
public const string AiredEpisodeOrder = "AiredEpisodeOrder";
/// <summary>
/// The album
/// </summary>

@ -1,17 +1,13 @@
using MediaBrowser.Model.Entities;

namespace MediaBrowser.Model.Querying
{
/// <summary>
/// Class ItemReviewsResult
/// </summary>
public class ItemReviewsResult
public class QueryResult<T>
{
/// <summary>
/// Gets or sets the item reviews.
/// Gets or sets the items.
/// </summary>
/// <value>The item reviews.</value>
public ItemReview[] ItemReviews { get; set; }
/// <value>The items.</value>
public T[] Items { get; set; }
/// <summary>
/// The total number of records available
@ -22,9 +18,9 @@ namespace MediaBrowser.Model.Querying
/// <summary>
/// Initializes a new instance of the <see cref="ItemsResult" /> class.
/// </summary>
public ItemReviewsResult()
public QueryResult()
{
ItemReviews = new ItemReview[] { };
Items = new T[] { };
}
}
}
}

@ -92,6 +92,18 @@ namespace MediaBrowser.Model.System
/// <value>The program data path.</value>
public string ProgramDataPath { get; set; }
/// <summary>
/// Gets or sets the items by name path.
/// </summary>
/// <value>The items by name path.</value>
public string ItemsByNamePath { get; set; }
/// <summary>
/// Gets or sets the log path.
/// </summary>
/// <value>The log path.</value>
public string LogPath { get; set; }
/// <summary>
/// Gets or sets the HTTP server port number.
/// </summary>

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

Loading…
Cancel
Save