Merge pull request #954 from MediaBrowser/dev

3.0.5462.0
pull/702/head
Luke 10 years ago
commit 524293ea79

@ -1,22 +1,19 @@
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Devices;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session;
using ServiceStack;
using ServiceStack.Web;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace MediaBrowser.Api.Devices
{
[Route("/Devices", "GET", Summary = "Gets all devices")]
[Authenticated(Roles = "Admin")]
public class GetDevices : IReturn<List<DeviceInfo>>
public class GetDevices : DeviceQuery, IReturn<QueryResult<DeviceInfo>>
{
[ApiMember(Name = "SupportsContentUploading", Description = "SupportsContentUploading", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public bool? SupportsContentUploading { get; set; }
}
[Route("/Devices", "DELETE", Summary = "Deletes a device")]
@ -109,16 +106,7 @@ namespace MediaBrowser.Api.Devices
public object Get(GetDevices request)
{
var devices = _deviceManager.GetDevices();
if (request.SupportsContentUploading.HasValue)
{
var val = request.SupportsContentUploading.Value;
devices = devices.Where(i => _deviceManager.GetCapabilities(i.Id).SupportsContentUploading == val);
}
return ToOptimizedResult(devices.ToList());
return ToOptimizedResult(_deviceManager.GetDevices(request));
}
public object Get(GetCameraUploads request)

@ -0,0 +1,49 @@
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using System;
using System.Linq;
namespace MediaBrowser.Api
{
public interface IHasDtoOptions : IHasItemFields
{
bool? EnableImages { get; set; }
int? ImageTypeLimit { get; set; }
string EnableImageTypes { get; set; }
}
public static class HasDtoOptionsExtensions
{
public static DtoOptions GetDtoOptions(this IHasDtoOptions request)
{
var options = new DtoOptions();
options.Fields = request.GetItemFields().ToList();
options.EnableImages = request.EnableImages ?? true;
if (request.ImageTypeLimit.HasValue)
{
options.ImageTypeLimit = request.ImageTypeLimit.Value;
}
if (string.IsNullOrWhiteSpace(request.EnableImageTypes))
{
if (options.EnableImages)
{
// Get everything
options.ImageTypes = Enum.GetNames(typeof(ImageType))
.Select(i => (ImageType)Enum.Parse(typeof(ImageType), i, true))
.ToList();
}
}
else
{
options.ImageTypes = (request.EnableImageTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)).ToList();
}
return options;
}
}
}

@ -567,7 +567,7 @@ namespace MediaBrowser.Api.Images
private async Task<object> GetImageResult(IHasImages item,
ImageRequest request,
ItemImageInfo image,
ImageOutputFormat format,
ImageFormat format,
List<IImageEnhancer> enhancers,
string contentType,
TimeSpan? cacheDuration,
@ -612,11 +612,11 @@ namespace MediaBrowser.Api.Images
});
}
private ImageOutputFormat GetOutputFormat(ImageRequest request, ItemImageInfo image, List<IImageEnhancer> enhancers)
private ImageFormat GetOutputFormat(ImageRequest request, ItemImageInfo image, List<IImageEnhancer> enhancers)
{
if (!string.IsNullOrWhiteSpace(request.Format))
{
ImageOutputFormat format;
ImageFormat format;
if (Enum.TryParse(request.Format, true, out format))
{
return format;
@ -627,28 +627,28 @@ namespace MediaBrowser.Api.Images
var clientFormats = GetClientSupportedFormats();
if (serverFormats.Contains(ImageOutputFormat.Webp) &&
clientFormats.Contains(ImageOutputFormat.Webp))
if (serverFormats.Contains(ImageFormat.Webp) &&
clientFormats.Contains(ImageFormat.Webp))
{
return ImageOutputFormat.Webp;
return ImageFormat.Webp;
}
if (enhancers.Count > 0)
{
return ImageOutputFormat.Png;
return ImageFormat.Png;
}
if (string.Equals(Path.GetExtension(image.Path), ".jpg", StringComparison.OrdinalIgnoreCase) ||
string.Equals(Path.GetExtension(image.Path), ".jpeg", StringComparison.OrdinalIgnoreCase))
{
return ImageOutputFormat.Jpg;
return ImageFormat.Jpg;
}
// We can't predict if there will be transparency or not, so play it safe
return ImageOutputFormat.Png;
return ImageFormat.Png;
}
private ImageOutputFormat[] GetClientSupportedFormats()
private ImageFormat[] GetClientSupportedFormats()
{
if ((Request.AcceptTypes ?? new string[] { }).Contains("image/webp", StringComparer.OrdinalIgnoreCase))
{
@ -657,32 +657,32 @@ namespace MediaBrowser.Api.Images
// Not displaying properly on iOS
if (userAgent.IndexOf("cfnetwork", StringComparison.OrdinalIgnoreCase) == -1)
{
return new[] { ImageOutputFormat.Webp, ImageOutputFormat.Jpg, ImageOutputFormat.Png };
return new[] { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png };
}
}
return new[] { ImageOutputFormat.Jpg, ImageOutputFormat.Png };
return new[] { ImageFormat.Jpg, ImageFormat.Png };
}
private string GetMimeType(ImageOutputFormat format, string path)
private string GetMimeType(ImageFormat format, string path)
{
if (format == ImageOutputFormat.Bmp)
if (format == ImageFormat.Bmp)
{
return Common.Net.MimeTypes.GetMimeType("i.bmp");
}
if (format == ImageOutputFormat.Gif)
if (format == ImageFormat.Gif)
{
return Common.Net.MimeTypes.GetMimeType("i.gif");
}
if (format == ImageOutputFormat.Jpg)
if (format == ImageFormat.Jpg)
{
return Common.Net.MimeTypes.GetMimeType("i.jpg");
}
if (format == ImageOutputFormat.Png)
if (format == ImageFormat.Png)
{
return Common.Net.MimeTypes.GetMimeType("i.png");
}
if (format == ImageOutputFormat.Webp)
if (format == ImageFormat.Webp)
{
return Common.Net.MimeTypes.GetMimeType("i.webp");
}

@ -37,12 +37,6 @@ namespace MediaBrowser.Api
{
}
[Route("/Items/RemoteSearch/Trailer", "POST")]
[Authenticated]
public class GetTrailerRemoteSearchResults : RemoteSearchQuery<TrailerInfo>, IReturn<List<RemoteSearchResult>>
{
}
[Route("/Items/RemoteSearch/AdultVideo", "POST")]
[Authenticated]
public class GetAdultVideoRemoteSearchResults : RemoteSearchQuery<ItemLookupInfo>, IReturn<List<RemoteSearchResult>>
@ -162,13 +156,6 @@ namespace MediaBrowser.Api
return ToOptimizedResult(result);
}
public object Post(GetTrailerRemoteSearchResults request)
{
var result = _providerManager.GetRemoteSearchResults<Trailer, TrailerInfo>(request, CancellationToken.None).Result;
return ToOptimizedResult(result);
}
public object Post(GetMusicAlbumRemoteSearchResults request)
{
var result = _providerManager.GetRemoteSearchResults<MusicAlbum, AlbumInfo>(request, CancellationToken.None).Result;

@ -276,7 +276,7 @@ namespace MediaBrowser.Api.Library
var fields = Enum.GetNames(typeof(ItemFields))
.Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
.ToList();
var result = new ItemsResult
{
TotalRecordCount = items.Count,
@ -353,7 +353,7 @@ namespace MediaBrowser.Api.Library
.ToList();
BaseItem parent = item.Parent;
while (parent != null)
{
if (user != null)
@ -607,7 +607,7 @@ namespace MediaBrowser.Api.Library
}
}
}
var dtos = themeSongIds.Select(_libraryManager.GetItemById)
.OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, fields, user, item));

@ -77,6 +77,7 @@
<Compile Include="Dlna\DlnaServerService.cs" />
<Compile Include="Dlna\DlnaService.cs" />
<Compile Include="FilterService.cs" />
<Compile Include="IHasDtoOptions.cs" />
<Compile Include="Library\ChapterService.cs" />
<Compile Include="Playback\Hls\MpegDashService.cs" />
<Compile Include="Playback\MediaInfoService.cs" />

@ -200,6 +200,19 @@ namespace MediaBrowser.Api.Movies
.ToList();
}
if (item is Video)
{
var imdbId = item.GetProviderId(MetadataProviders.Imdb);
// Use imdb id to try to filter duplicates of the same item
if (!string.IsNullOrWhiteSpace(imdbId))
{
list = list
.Where(i => !string.Equals(imdbId, i.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase))
.ToList();
}
}
var items = SimilarItemsHelper.GetSimilaritems(item, list, getSimilarityScore).ToList();
IEnumerable<BaseItem> returnItems = items;
@ -208,7 +221,7 @@ namespace MediaBrowser.Api.Movies
{
returnItems = returnItems.Take(request.Limit.Value);
}
var result = new ItemsResult
{
Items = returnItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray(),

@ -92,7 +92,7 @@ namespace MediaBrowser.Api.Movies
Logger,
// Strip out secondary versions
request, item => (item is Movie || item is Trailer) && !((Video)item).PrimaryVersionId.HasValue,
request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue,
SimilarItemsHelper.GetSimiliarityScore);

@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Querying;
using ServiceStack;
using System.Collections.Generic;
@ -20,6 +21,11 @@ namespace MediaBrowser.Api.Music
{
}
[Route("/Playlists/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given playlist")]
public class GetInstantMixFromPlaylist : BaseGetSimilarItemsFromItem
{
}
[Route("/Artists/{Name}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given artist")]
public class GetInstantMixFromArtist : BaseGetSimilarItems
{
@ -109,6 +115,17 @@ namespace MediaBrowser.Api.Music
return GetResult(items, user, request);
}
public object Get(GetInstantMixFromPlaylist request)
{
var playlist = (Playlist)_libraryManager.GetItemById(request.Id);
var user = _userManager.GetUserById(request.UserId.Value);
var items = _musicManager.GetInstantMixFromPlaylist(playlist, user);
return GetResult(items, user, request);
}
public object Get(GetInstantMixFromMusicGenre request)
{
var user = _userManager.GetUserById(request.UserId.Value);

@ -1,5 +1,4 @@
using MediaBrowser.Common.Constants;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Serialization;
@ -103,6 +102,7 @@ namespace MediaBrowser.Api
private readonly IHttpClient _httpClient;
private readonly INetworkManager _netManager;
private readonly IJsonSerializer _serializer;
private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
public PackageReviewService(IHttpClient client, INetworkManager net, IJsonSerializer serializer)
{
@ -132,7 +132,7 @@ namespace MediaBrowser.Api
parms += "&title=true";
}
var result = _httpClient.Get(Constants.MbAdminUrl + "/service/packageReview/retrieve" + parms, CancellationToken.None).Result;
var result = _httpClient.Get(MbAdminUrl + "/service/packageReview/retrieve" + parms, CancellationToken.None).Result;
var reviews = _serializer.DeserializeFromStream<List<PackageReviewInfo>>(result);
@ -153,7 +153,7 @@ namespace MediaBrowser.Api
{ "review", reviewText },
};
Task.WaitAll(_httpClient.Post(Constants.MbAdminUrl + "/service/packageReview/update", review, CancellationToken.None));
Task.WaitAll(_httpClient.Post(MbAdminUrl + "/service/packageReview/update", review, CancellationToken.None));
}
}
}

@ -173,9 +173,10 @@ namespace MediaBrowser.Api
public object Get(GetPackage request)
{
var packages = _installationManager.GetAvailablePackages(CancellationToken.None, applicationVersion: _appHost.ApplicationVersion).Result;
var list = packages.ToList();
var result = packages.FirstOrDefault(p => string.Equals(p.guid, request.AssemblyGuid ?? "none", StringComparison.OrdinalIgnoreCase))
?? packages.FirstOrDefault(p => p.name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
var result = list.FirstOrDefault(p => string.Equals(p.guid, request.AssemblyGuid ?? "none", StringComparison.OrdinalIgnoreCase))
?? list.FirstOrDefault(p => p.name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
return ToOptimizedResult(result);
}
@ -243,4 +244,4 @@ namespace MediaBrowser.Api
}
}
}
}

@ -2011,11 +2011,6 @@ namespace MediaBrowser.Api.Playback
state.EstimateContentLength = transcodingProfile.EstimateContentLength;
state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
if (state.VideoRequest != null && string.IsNullOrWhiteSpace(state.VideoRequest.Profile))
{
state.VideoRequest.Profile = transcodingProfile.VideoProfile;
}
}
}

@ -151,7 +151,7 @@ namespace MediaBrowser.Api
{
items = items.Take(request.Limit.Value).ToArray();
}
var dtos = items
.Select(i => _dtoService.GetBaseItemDto(i.Item2, request.GetItemFields().ToList(), user))
.ToArray();

@ -238,6 +238,12 @@ namespace MediaBrowser.Api.Session
[ApiMember(Name = "SupportsContentUploading", Description = "Determines whether camera upload is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool SupportsContentUploading { get; set; }
[ApiMember(Name = "SupportsSync", Description = "Determines whether sync is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool SupportsSync { get; set; }
[ApiMember(Name = "SupportsUniqueIdentifier", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool SupportsUniqueIdentifier { get; set; }
}
[Route("/Sessions/Logout", "POST", Summary = "Reports that a session has ended")]
@ -516,7 +522,11 @@ namespace MediaBrowser.Api.Session
MessageCallbackUrl = request.MessageCallbackUrl,
SupportsContentUploading = request.SupportsContentUploading
SupportsContentUploading = request.SupportsContentUploading,
SupportsSync = request.SupportsSync,
SupportsUniqueIdentifier = request.SupportsUniqueIdentifier
});
}
}

@ -61,6 +61,7 @@ namespace MediaBrowser.Api
public void Post(ReportStartupWizardComplete request)
{
_config.Configuration.IsStartupWizardCompleted = true;
_config.Configuration.EnableLocalizedGuids = true;
_config.SaveConfiguration();
}

@ -19,7 +19,7 @@ namespace MediaBrowser.Api
/// Class GetNextUpEpisodes
/// </summary>
[Route("/Shows/NextUp", "GET", Summary = "Gets a list of next up episodes")]
public class GetNextUpEpisodes : IReturn<ItemsResult>, IHasItemFields
public class GetNextUpEpisodes : IReturn<ItemsResult>, IHasDtoOptions
{
/// <summary>
/// Gets or sets the user id.
@ -58,10 +58,19 @@ namespace MediaBrowser.Api
/// <value>The parent id.</value>
[ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ParentId { get; set; }
[ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableImages { get; set; }
[ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? ImageTypeLimit { get; set; }
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string EnableImageTypes { get; set; }
}
[Route("/Shows/Upcoming", "GET", Summary = "Gets a list of upcoming episodes")]
public class GetUpcomingEpisodes : IReturn<ItemsResult>, IHasItemFields
public class GetUpcomingEpisodes : IReturn<ItemsResult>, IHasDtoOptions
{
/// <summary>
/// Gets or sets the user id.
@ -97,6 +106,15 @@ namespace MediaBrowser.Api
/// <value>The parent id.</value>
[ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ParentId { get; set; }
[ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableImages { get; set; }
[ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? ImageTypeLimit { get; set; }
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string EnableImageTypes { get; set; }
}
[Route("/Shows/{Id}/Similar", "GET", Summary = "Finds tv shows similar to a given one.")]
@ -252,9 +270,9 @@ namespace MediaBrowser.Api
var pagedItems = ApplyPaging(previousEpisodes, request.StartIndex, request.Limit);
var fields = request.GetItemFields().ToList();
var options = request.GetDtoOptions();
var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray();
var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, options, user)).ToArray();
var result = new ItemsResult
{
@ -283,9 +301,9 @@ namespace MediaBrowser.Api
var user = _userManager.GetUserById(request.UserId);
var fields = request.GetItemFields().ToList();
var options = request.GetDtoOptions();
var returnItems = result.Items.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray();
var returnItems = result.Items.Select(i => _dtoService.GetBaseItemDto(i, options, user)).ToArray();
return ToOptimizedSerializedResultUsingCache(new ItemsResult
{

@ -89,7 +89,7 @@ namespace MediaBrowser.Api.UserLibrary
if (request.UserId.HasValue)
{
var user = UserManager.GetUserById(request.UserId.Value);
return DtoService.GetBaseItemDto(item, fields.ToList(), user);
}

@ -127,11 +127,11 @@ namespace MediaBrowser.Api.UserLibrary
}
var fields = request.GetItemFields().ToList();
var tuples = ibnItems.Select(i => new Tuple<TItemType, List<BaseItem>>(i, i.GetTaggedItems(libraryItems).ToList()));
var dtos = tuples.Select(i => GetDto(i.Item1, user, fields, i.Item2));
var dtoOptions = request.GetDtoOptions();
var dtos = tuples.Select(i => GetDto(i.Item1, user, dtoOptions, i.Item2));
result.Items = dtos.Where(i => i != null).ToArray();
@ -332,12 +332,12 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <param name="fields">The fields.</param>
/// <param name="options">The options.</param>
/// <param name="libraryItems">The library items.</param>
/// <returns>Task{DtoBaseItem}.</returns>
private BaseItemDto GetDto(TItemType item, User user, List<ItemFields> fields, List<BaseItem> libraryItems)
private BaseItemDto GetDto(TItemType item, User user, DtoOptions options, List<BaseItem> libraryItems)
{
var dto = DtoService.GetItemByNameDto(item, fields, libraryItems, user);
var dto = DtoService.GetItemByNameDto(item, options, libraryItems, user);
return dto;
}

@ -7,8 +7,13 @@ using System.Linq;
namespace MediaBrowser.Api.UserLibrary
{
public abstract class BaseItemsRequest : IHasItemFields
public abstract class BaseItemsRequest : IHasDtoOptions
{
protected BaseItemsRequest()
{
EnableImages = true;
}
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
/// </summary>
@ -116,6 +121,15 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "Years", Description = "Optional. If specified, results will be filtered based on production year. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Years { get; set; }
[ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableImages { get; set; }
[ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? ImageTypeLimit { get; set; }
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string EnableImageTypes { get; set; }
public string[] GetGenres()
{
return (Genres ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);

@ -321,15 +321,14 @@ namespace MediaBrowser.Api.UserLibrary
var result = await GetItemsToSerialize(request, user, parentItem).ConfigureAwait(false);
var isFiltered = result.Item2;
var dtoOptions = request.GetDtoOptions();
if (isFiltered)
{
var currentFields = request.GetItemFields().ToList();
return new ItemsResult
{
TotalRecordCount = result.Item1.TotalRecordCount,
Items = result.Item1.Items.Select(i => _dtoService.GetBaseItemDto(i, currentFields, user)).ToArray()
Items = result.Item1.Items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray()
};
}
@ -363,9 +362,7 @@ namespace MediaBrowser.Api.UserLibrary
var pagedItems = ApplyPaging(request, itemsArray);
var fields = request.GetItemFields().ToList();
var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray();
var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray();
return new ItemsResult
{
@ -880,7 +877,7 @@ namespace MediaBrowser.Api.UserLibrary
var hasTrailers = i as IHasTrailers;
if (hasTrailers != null)
{
trailerCount = hasTrailers.LocalTrailerIds.Count;
trailerCount = hasTrailers.GetTrailerIds().Count;
}
var ok = val ? trailerCount > 0 : trailerCount == 0;

@ -221,7 +221,7 @@ namespace MediaBrowser.Api.UserLibrary
}
[Route("/Users/{UserId}/Items/Latest", "GET", Summary = "Gets latest media")]
public class GetLatestMedia : IReturn<List<BaseItemDto>>, IHasItemFields
public class GetLatestMedia : IReturn<List<BaseItemDto>>, IHasDtoOptions
{
/// <summary>
/// Gets or sets the user id.
@ -251,6 +251,15 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "GroupItems", Description = "Whether or not to group items into a parent container.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool GroupItems { get; set; }
[ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableImages { get; set; }
[ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? ImageTypeLimit { get; set; }
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string EnableImageTypes { get; set; }
public GetLatestMedia()
{
Limit = 20;
@ -362,7 +371,7 @@ namespace MediaBrowser.Api.UserLibrary
}
}
var fields = request.GetItemFields().ToList();
var options = request.GetDtoOptions();
var dtos = list.Select(i =>
{
@ -375,7 +384,7 @@ namespace MediaBrowser.Api.UserLibrary
childCount = i.Item2.Count;
}
var dto = _dtoService.GetBaseItemDto(item, fields, user);
var dto = _dtoService.GetBaseItemDto(item, options, user);
dto.ChildCount = childCount;
@ -506,7 +515,7 @@ namespace MediaBrowser.Api.UserLibrary
var hasTrailers = item as IHasTrailers;
if (hasTrailers != null)
{
trailerIds = hasTrailers.LocalTrailerIds;
trailerIds = hasTrailers.GetTrailerIds();
}
var dtos = trailerIds

@ -1,4 +1,5 @@
using MediaBrowser.Common.Configuration;
using System.Net.Sockets;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
@ -134,9 +135,22 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
request.Referer = options.Referer;
}
//request.ServicePoint.BindIPEndPointDelegate = BindIPEndPointCallback;
return request;
}
private static IPEndPoint BindIPEndPointCallback(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount)
{
// Prefer local ipv4
if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
{
return new IPEndPoint(IPAddress.IPv6Any, 0);
}
return new IPEndPoint(IPAddress.Any, 0);
}
private void AddRequestHeaders(HttpWebRequest request, HttpRequestOptions options)
{
foreach (var header in options.RequestHeaders.ToList())

@ -15,6 +15,11 @@ namespace MediaBrowser.Common.Implementations.Logging
/// <returns>StringBuilder.</returns>
public static StringBuilder GetLogMessage(Exception exception)
{
if (exception == null)
{
throw new ArgumentNullException("exception");
}
var messageText = new StringBuilder();
messageText.AppendLine(exception.Message);

@ -24,14 +24,28 @@ namespace MediaBrowser.Common.Implementations.Networking
/// <returns>IPAddress.</returns>
public IEnumerable<string> GetLocalIpAddresses()
{
var list = GetIPsDefault().Where(i => !IPAddress.IsLoopback(i)).Select(i => i.ToString()).ToList();
var list = GetIPsDefault()
.Where(i => !IPAddress.IsLoopback(i))
.Select(i => i.ToString())
.Where(FilterIpAddress)
.ToList();
if (list.Count > 0)
{
return list;
}
return GetLocalIpAddressesFallback();
return GetLocalIpAddressesFallback().Where(FilterIpAddress);
}
private bool FilterIpAddress(string address)
{
if (address.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return true;
}
private bool IsInPrivateAddressSpace(string endpoint)

@ -17,7 +17,9 @@ namespace MediaBrowser.Common.Implementations.Security
/// </summary>
public class PluginSecurityManager : ISecurityManager
{
private const string MBValidateUrl = Constants.Constants.MbAdminUrl + "service/registration/validate";
private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
private const string MBValidateUrl = MbAdminUrl + "service/registration/validate";
/// <summary>
/// The _is MB supporter
@ -160,7 +162,7 @@ namespace MediaBrowser.Common.Implementations.Security
return new SupporterInfo();
}
var url = Constants.Constants.MbAdminUrl + "/service/supporter/retrieve?key=" + key;
var url = MbAdminUrl + "/service/supporter/retrieve?key=" + key;
using (var stream = await _httpClient.Get(url, CancellationToken.None).ConfigureAwait(false))
{

@ -161,7 +161,7 @@ namespace MediaBrowser.Common.Implementations.Updates
{ "systemid", _applicationHost.SystemId }
};
using (var json = await _httpClient.Post(Constants.Constants.MbAdminUrl + "service/package/retrieveall", data, cancellationToken).ConfigureAwait(false))
using (var json = await _httpClient.Post(MbAdminUrl + "service/package/retrieveall", data, cancellationToken).ConfigureAwait(false))
{
cancellationToken.ThrowIfCancellationRequested();
@ -172,6 +172,7 @@ namespace MediaBrowser.Common.Implementations.Updates
}
private Tuple<List<PackageInfo>, DateTime> _lastPackageListResult;
private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
/// <summary>
/// Gets all available packages.
@ -203,7 +204,7 @@ namespace MediaBrowser.Common.Implementations.Updates
}
}
using (var json = await _httpClient.Get(Constants.Constants.MbAdminUrl + "service/MB3Packages.json", cancellationToken).ConfigureAwait(false))
using (var json = await _httpClient.Get(MbAdminUrl + "service/MB3Packages.json", cancellationToken).ConfigureAwait(false))
{
cancellationToken.ThrowIfCancellationRequested();

@ -1,8 +0,0 @@

namespace MediaBrowser.Common.Constants
{
public static class Constants
{
public const string MbAdminUrl = "http://www.mb3admin.com/admin/";
}
}

@ -1,5 +1,4 @@
using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
@ -55,28 +54,6 @@ namespace MediaBrowser.Common.Extensions
return sb.ToString();
}
/// <summary>
/// Removes the accent.
/// </summary>
/// <param name="text">The text.</param>
/// <returns>System.String.</returns>
public static string RemoveAccent(this string text)
{
var normalizedString = text.Normalize(NormalizationForm.FormD);
var stringBuilder = new StringBuilder();
foreach (var c in normalizedString)
{
var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
if (unicodeCategory != UnicodeCategory.NonSpacingMark)
{
stringBuilder.Append(c);
}
}
return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
}
/// <summary>
/// Gets the M d5.
/// </summary>
@ -96,6 +73,8 @@ namespace MediaBrowser.Common.Extensions
/// <param name="str">The STR.</param>
/// <param name="type">The type.</param>
/// <returns>Guid.</returns>
/// <exception cref="System.ArgumentNullException">type</exception>
[Obsolete("Use LibraryManager.GetNewItemId")]
public static Guid GetMBId(this string str, Type type)
{
if (type == null)
@ -107,35 +86,5 @@ namespace MediaBrowser.Common.Extensions
return key.GetMD5();
}
/// <summary>
/// Gets the attribute value.
/// </summary>
/// <param name="str">The STR.</param>
/// <param name="attrib">The attrib.</param>
/// <returns>System.String.</returns>
/// <exception cref="System.ArgumentNullException">attrib</exception>
public static string GetAttributeValue(this string str, string attrib)
{
if (string.IsNullOrEmpty(str))
{
throw new ArgumentNullException("str");
}
if (string.IsNullOrEmpty(attrib))
{
throw new ArgumentNullException("attrib");
}
string srch = "[" + attrib + "=";
int start = str.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
if (start > -1)
{
start += srch.Length;
int end = str.IndexOf(']', start);
return str.Substring(start, end - start);
}
return null;
}
}
}

@ -1,122 +0,0 @@
using MediaBrowser.Common.Extensions;
using System;
using System.IO;
namespace MediaBrowser.Common.IO
{
/// <summary>
/// This is a wrapper for storing large numbers of files within a directory on a file system.
/// Simply pass a filename into GetResourcePath and it will return a full path location of where the file should be stored.
/// </summary>
public class FileSystemRepository
{
/// <summary>
/// Gets or sets the path.
/// </summary>
/// <value>The path.</value>
protected string Path { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="FileSystemRepository" /> class.
/// </summary>
/// <param name="path">The path.</param>
/// <exception cref="System.ArgumentNullException"></exception>
public FileSystemRepository(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException();
}
Path = path;
}
/// <summary>
/// Gets the full path of where a resource should be stored within the repository
/// </summary>
/// <param name="uniqueName">Name of the unique.</param>
/// <param name="fileExtension">The file extension.</param>
/// <returns>System.String.</returns>
/// <exception cref="System.ArgumentNullException">
/// </exception>
public string GetResourcePath(string uniqueName, string fileExtension)
{
if (string.IsNullOrEmpty(uniqueName))
{
throw new ArgumentNullException("uniqueName");
}
if (string.IsNullOrEmpty(fileExtension))
{
throw new ArgumentNullException("fileExtension");
}
var filename = uniqueName.GetMD5() + fileExtension;
return GetResourcePath(filename);
}
/// <summary>
/// Gets the resource path.
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>System.String.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
public string GetResourcePath(string filename)
{
if (string.IsNullOrEmpty(filename))
{
throw new ArgumentNullException("filename");
}
var prefix = filename.Substring(0, 1);
var path = System.IO.Path.Combine(Path, prefix);
return System.IO.Path.Combine(path, filename);
}
/// <summary>
/// Determines if a resource is present in the repository
/// </summary>
/// <param name="uniqueName">Name of the unique.</param>
/// <param name="fileExtension">The file extension.</param>
/// <returns><c>true</c> if the specified unique name contains resource; otherwise, <c>false</c>.</returns>
public bool ContainsResource(string uniqueName, string fileExtension)
{
return ContainsFilePath(GetResourcePath(uniqueName, fileExtension));
}
/// <summary>
/// Determines if a file with a given name is present in the repository
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns><c>true</c> if the specified filename contains filename; otherwise, <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
public bool ContainsFilename(string filename)
{
if (string.IsNullOrEmpty(filename))
{
throw new ArgumentNullException();
}
return ContainsFilePath(GetResourcePath(filename));
}
/// <summary>
/// Determines if a file is present in the repository
/// </summary>
/// <param name="path">The path.</param>
/// <returns><c>true</c> if [contains file path] [the specified path]; otherwise, <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
public bool ContainsFilePath(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException();
}
return File.Exists(path);
}
}
}

@ -57,12 +57,10 @@
<Compile Include="Configuration\ConfigurationUpdateEventArgs.cs" />
<Compile Include="Configuration\IConfigurationManager.cs" />
<Compile Include="Configuration\IConfigurationFactory.cs" />
<Compile Include="Constants\Constants.cs" />
<Compile Include="Events\EventHelper.cs" />
<Compile Include="Extensions\BaseExtensions.cs" />
<Compile Include="Extensions\ResourceNotFoundException.cs" />
<Compile Include="IDependencyContainer.cs" />
<Compile Include="IO\FileSystemRepository.cs" />
<Compile Include="IO\IFileSystem.cs" />
<Compile Include="IO\ProgressStream.cs" />
<Compile Include="IO\StreamDefaults.cs" />

@ -15,11 +15,13 @@ namespace MediaBrowser.Controller.Collections
public Dictionary<string, string> ProviderIds { get; set; }
public List<Guid> ItemIdList { get; set; }
public List<Guid> UserIds { get; set; }
public CollectionCreationOptions()
{
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
ItemIdList = new List<Guid>();
UserIds = new List<Guid>();
}
}
}

@ -1,5 +1,6 @@
using MediaBrowser.Model.Devices;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session;
using System;
using System.Collections.Generic;
@ -58,8 +59,9 @@ namespace MediaBrowser.Controller.Devices
/// <summary>
/// Gets the devices.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>IEnumerable&lt;DeviceInfo&gt;.</returns>
IEnumerable<DeviceInfo> GetDevices();
QueryResult<DeviceInfo> GetDevices(DeviceQuery query);
/// <summary>
/// Deletes the device.

@ -1,22 +0,0 @@
using MediaBrowser.Controller.Drawing;
using System;
using System.IO;
namespace MediaBrowser.Controller.Dlna
{
public class DlnaIconResponse : IDisposable
{
public Stream Stream { get; set; }
public ImageFormat Format { get; set; }
public void Dispose()
{
if (Stream != null)
{
Stream.Dispose();
Stream = null;
}
}
}
}

@ -1,4 +1,5 @@
using MediaBrowser.Model.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Model.Dlna;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Dlna
@ -69,6 +70,6 @@ namespace MediaBrowser.Controller.Dlna
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>DlnaIconResponse.</returns>
DlnaIconResponse GetIcon(string filename);
ImageStream GetIcon(string filename);
}
}

@ -94,6 +94,6 @@ namespace MediaBrowser.Controller.Drawing
/// Gets the supported image output formats.
/// </summary>
/// <returns>ImageOutputFormat[].</returns>
ImageOutputFormat[] GetSupportedImageOutputFormats();
ImageFormat[] GetSupportedImageOutputFormats();
}
}

@ -1,11 +0,0 @@

namespace MediaBrowser.Controller.Drawing
{
public enum ImageFormat
{
Jpg,
Png,
Gif,
Bmp
}
}

@ -29,7 +29,7 @@ namespace MediaBrowser.Controller.Drawing
public List<IImageEnhancer> Enhancers { get; set; }
public ImageOutputFormat OutputFormat { get; set; }
public ImageFormat OutputFormat { get; set; }
public bool AddPlayedIndicator { get; set; }

@ -0,0 +1,28 @@
using MediaBrowser.Model.Drawing;
using System;
using System.IO;
namespace MediaBrowser.Controller.Drawing
{
public class ImageStream : IDisposable
{
/// <summary>
/// Gets or sets the stream.
/// </summary>
/// <value>The stream.</value>
public Stream Stream { get; set; }
/// <summary>
/// Gets or sets the format.
/// </summary>
/// <value>The format.</value>
public ImageFormat Format { get; set; }
public void Dispose()
{
if (Stream != null)
{
Stream.Dispose();
}
}
}
}

@ -22,7 +22,8 @@ namespace MediaBrowser.Controller.Dto
/// </summary>
/// <param name="dto">The dto.</param>
/// <param name="item">The item.</param>
void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item);
/// <param name="fields">The fields.</param>
void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item, List<ItemFields> fields);
/// <summary>
/// Gets the base item dto.
@ -34,6 +35,16 @@ namespace MediaBrowser.Controller.Dto
/// <returns>Task{BaseItemDto}.</returns>
BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null);
/// <summary>
/// Gets the base item dto.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="options">The options.</param>
/// <param name="user">The user.</param>
/// <param name="owner">The owner.</param>
/// <returns>BaseItemDto.</returns>
BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null);
/// <summary>
/// Gets the chapter information dto.
/// </summary>
@ -51,12 +62,13 @@ namespace MediaBrowser.Controller.Dto
/// <summary>
/// Gets the item by name dto.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="item">The item.</param>
/// <param name="fields">The fields.</param>
/// <param name="options">The options.</param>
/// <param name="taggedItems">The tagged items.</param>
/// <param name="user">The user.</param>
/// <returns>BaseItemDto.</returns>
BaseItemDto GetItemByNameDto<T>(T item, List<ItemFields> fields, List<BaseItem> taggedItems, User user = null)
BaseItemDto GetItemByNameDto<T>(T item, DtoOptions options, List<BaseItem> taggedItems, User user = null)
where T : BaseItem, IItemByName;
}
}

@ -53,16 +53,6 @@ namespace MediaBrowser.Controller.Entities
public static string ThemeSongFilename = "theme";
public static string ThemeVideosFolderName = "backdrops";
public static List<KeyValuePair<string, ExtraType>> ExtraSuffixes = new List<KeyValuePair<string, ExtraType>>
{
new KeyValuePair<string,ExtraType>("-trailer", ExtraType.Trailer),
new KeyValuePair<string,ExtraType>("-deleted", ExtraType.DeletedScene),
new KeyValuePair<string,ExtraType>("-behindthescenes", ExtraType.BehindTheScenes),
new KeyValuePair<string,ExtraType>("-interview", ExtraType.Interview),
new KeyValuePair<string,ExtraType>("-scene", ExtraType.Scene),
new KeyValuePair<string,ExtraType>("-sample", ExtraType.Sample)
};
public List<ItemImageInfo> ImageInfos { get; set; }
[IgnoreDataMember]
@ -222,6 +212,20 @@ namespace MediaBrowser.Controller.Entities
}
}
[IgnoreDataMember]
public virtual string FileNameWithoutExtension
{
get
{
if (LocationType == LocationType.FileSystem)
{
return System.IO.Path.GetFileNameWithoutExtension(Path);
}
return null;
}
}
/// <summary>
/// This is just a helper for convenience
/// </summary>
@ -361,6 +365,15 @@ namespace MediaBrowser.Controller.Entities
}
}
public bool ContainsPerson(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException("name");
}
return People.Any(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase));
}
public string GetInternalMetadataPath()
{
return GetInternalMetadataPath(ConfigurationManager.ApplicationPaths.InternalMetadataPath);
@ -593,118 +606,6 @@ namespace MediaBrowser.Controller.Entities
return PlayAccess.Full;
}
/// <summary>
/// Loads local trailers from the file system
/// </summary>
/// <returns>List{Video}.</returns>
private IEnumerable<Trailer> LoadLocalTrailers(List<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
{
var files = fileSystemChildren.OfType<DirectoryInfo>()
.Where(i => string.Equals(i.Name, TrailerFolderName, StringComparison.OrdinalIgnoreCase))
.SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
.ToList();
var extraTypes = new List<ExtraType> { ExtraType.Trailer };
var suffixes = ExtraSuffixes.Where(i => extraTypes.Contains(i.Value))
.Select(i => i.Key)
.ToList();
files.AddRange(fileSystemChildren.OfType<FileInfo>()
.Where(i =>
{
var nameEithoutExtension = FileSystem.GetFileNameWithoutExtension(i);
if (!suffixes.Any(s => nameEithoutExtension.EndsWith(s, StringComparison.OrdinalIgnoreCase)))
{
return false;
}
return !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase);
}));
return LibraryManager.ResolvePaths<Trailer>(files, directoryService, null).Select(video =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(video.Id) as Trailer;
if (dbItem != null)
{
video = dbItem;
}
if (video != null)
{
video.ExtraType = ExtraType.Trailer;
}
return video;
// Sort them so that the list can be easily compared for changes
}).OrderBy(i => i.Path).ToList();
}
protected IEnumerable<Video> LoadSpecialFeatures(List<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
{
var files = fileSystemChildren.OfType<DirectoryInfo>()
.Where(i => string.Equals(i.Name, "extras", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "specials", StringComparison.OrdinalIgnoreCase))
.SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
.ToList();
var extraTypes = new List<ExtraType> { ExtraType.BehindTheScenes, ExtraType.DeletedScene, ExtraType.Interview, ExtraType.Sample, ExtraType.Scene, ExtraType.Clip };
var suffixes = ExtraSuffixes.Where(i => extraTypes.Contains(i.Value))
.Select(i => i.Key)
.ToList();
files.AddRange(fileSystemChildren.OfType<FileInfo>()
.Where(i =>
{
var nameEithoutExtension = FileSystem.GetFileNameWithoutExtension(i);
if (!suffixes.Any(s => nameEithoutExtension.EndsWith(s, StringComparison.OrdinalIgnoreCase)))
{
return false;
}
return !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase);
}));
return LibraryManager.ResolvePaths<Video>(files, directoryService, null).Select(video =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(video.Id) as Video;
if (dbItem != null)
{
video = dbItem;
}
if (video != null)
{
SetExtraTypeFromFilename(video);
}
return video;
// Sort them so that the list can be easily compared for changes
}).OrderBy(i => i.Path).ToList();
}
private void SetExtraTypeFromFilename(Video item)
{
var name = System.IO.Path.GetFileNameWithoutExtension(item.Path) ?? string.Empty;
foreach (var suffix in ExtraSuffixes)
{
if (name.EndsWith(suffix.Key, StringComparison.OrdinalIgnoreCase))
{
item.ExtraType = suffix.Value;
return;
}
}
item.ExtraType = ExtraType.Clip;
}
/// <summary>
/// Loads the theme songs.
/// </summary>
@ -721,7 +622,9 @@ namespace MediaBrowser.Controller.Entities
.Where(i => string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))
);
return LibraryManager.ResolvePaths<Audio.Audio>(files, directoryService, null).Select(audio =>
return LibraryManager.ResolvePaths(files, directoryService, null)
.OfType<Audio.Audio>()
.Select(audio =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio;
@ -731,10 +634,7 @@ namespace MediaBrowser.Controller.Entities
audio = dbItem;
}
if (audio != null)
{
audio.ExtraType = ExtraType.ThemeSong;
}
audio.ExtraType = ExtraType.ThemeSong;
return audio;
@ -752,7 +652,9 @@ namespace MediaBrowser.Controller.Entities
.Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
.SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly));
return LibraryManager.ResolvePaths<Video>(files, directoryService, null).Select(item =>
return LibraryManager.ResolvePaths(files, directoryService, null)
.OfType<Video>()
.Select(item =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(item.Id) as Video;
@ -762,10 +664,7 @@ namespace MediaBrowser.Controller.Entities
item = dbItem;
}
if (item != null)
{
item.ExtraType = ExtraType.ThemeVideo;
}
item.ExtraType = ExtraType.ThemeVideo;
return item;
@ -870,7 +769,8 @@ namespace MediaBrowser.Controller.Entities
private async Task<bool> RefreshLocalTrailers(IHasTrailers item, MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
var newItems = LoadLocalTrailers(fileSystemChildren, options.DirectoryService).ToList();
var newItems = LibraryManager.FindTrailers(this, fileSystemChildren, options.DirectoryService).ToList();
var newItemIds = newItems.Select(i => i.Id).ToList();
var itemsChanged = !item.LocalTrailerIds.SequenceEqual(newItemIds);
@ -995,6 +895,28 @@ namespace MediaBrowser.Controller.Entities
return Id.ToString();
}
internal virtual bool IsValidFromResolver(BaseItem newItem)
{
var current = this;
var currentAsPlaceHolder = current as ISupportsPlaceHolders;
if (currentAsPlaceHolder != null)
{
var newHasPlaceHolder = newItem as ISupportsPlaceHolders;
if (newHasPlaceHolder != null)
{
if (currentAsPlaceHolder.IsPlaceHolder != newHasPlaceHolder.IsPlaceHolder)
{
return false;
}
}
}
return current.IsInMixedFolder == newItem.IsInMixedFolder;
}
/// <summary>
/// Gets the preferred metadata language.
/// </summary>
@ -1390,7 +1312,7 @@ namespace MediaBrowser.Controller.Entities
/// <param name="resetPosition">if set to <c>true</c> [reset position].</param>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
public virtual async Task MarkPlayed(User user,
public virtual async Task MarkPlayed(User user,
DateTime? datePlayed,
bool resetPosition)
{
@ -1778,7 +1700,8 @@ namespace MediaBrowser.Controller.Entities
Name = Name,
ProviderIds = ProviderIds,
IndexNumber = IndexNumber,
ParentIndexNumber = ParentIndexNumber
ParentIndexNumber = ParentIndexNumber,
Year = ProductionYear
};
}
@ -1820,9 +1743,42 @@ namespace MediaBrowser.Controller.Entities
if (pct > 0)
{
pct = userData.PlaybackPositionTicks / pct;
dto.PlayedPercentage = 100 * pct;
if (pct > 0)
{
dto.PlayedPercentage = 100 * pct;
}
}
}
}
protected Task RefreshMetadataForOwnedVideo(MetadataRefreshOptions options, string path, CancellationToken cancellationToken)
{
var newOptions = new MetadataRefreshOptions(options.DirectoryService)
{
ImageRefreshMode = options.ImageRefreshMode,
MetadataRefreshMode = options.MetadataRefreshMode,
ReplaceAllMetadata = options.ReplaceAllMetadata
};
var id = LibraryManager.GetNewItemId(path, typeof(Video));
// Try to retrieve it from the db. If we don't find it, use the resolved version
var video = LibraryManager.GetItemById(id) as Video;
if (video == null)
{
video = LibraryManager.ResolvePath(new FileInfo(path)) as Video;
newOptions.ForceSave = true;
}
if (video == null)
{
return Task.FromResult(true);
}
return video.RefreshMetadata(newOptions, cancellationToken);
}
}
}

@ -1,5 +1,4 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
@ -59,6 +58,20 @@ namespace MediaBrowser.Controller.Entities
}
}
[IgnoreDataMember]
public override string FileNameWithoutExtension
{
get
{
if (LocationType == LocationType.FileSystem)
{
return System.IO.Path.GetFileName(Path);
}
return null;
}
}
/// <summary>
/// Gets or sets a value indicating whether this instance is physical root.
/// </summary>
@ -103,7 +116,7 @@ namespace MediaBrowser.Controller.Entities
if (item.Id == Guid.Empty)
{
item.Id = item.Path.GetMBId(item.GetType());
item.Id = LibraryManager.GetNewItemId(item.Path, item.GetType());
}
if (ActualChildren.Any(i => i.Id == item.Id))
@ -364,47 +377,7 @@ namespace MediaBrowser.Controller.Entities
private bool IsValidFromResolver(BaseItem current, BaseItem newItem)
{
var currentAsVideo = current as Video;
if (currentAsVideo != null)
{
var newAsVideo = newItem as Video;
if (newAsVideo != null)
{
if (currentAsVideo.IsPlaceHolder != newAsVideo.IsPlaceHolder)
{
return false;
}
if (currentAsVideo.IsMultiPart != newAsVideo.IsMultiPart)
{
return false;
}
if (currentAsVideo.HasLocalAlternateVersions != newAsVideo.HasLocalAlternateVersions)
{
return false;
}
}
}
else
{
var currentAsPlaceHolder = current as ISupportsPlaceHolders;
if (currentAsPlaceHolder != null)
{
var newHasPlaceHolder = newItem as ISupportsPlaceHolders;
if (newHasPlaceHolder != null)
{
if (currentAsPlaceHolder.IsPlaceHolder != newHasPlaceHolder.IsPlaceHolder)
{
return false;
}
}
}
}
return current.IsInMixedFolder == newItem.IsInMixedFolder;
return current.IsValidFromResolver(newItem);
}
/// <summary>
@ -737,7 +710,7 @@ namespace MediaBrowser.Controller.Entities
{
var collectionType = LibraryManager.FindCollectionType(this);
return LibraryManager.ResolvePaths<BaseItem>(GetFileSystemChildren(directoryService), directoryService, this, collectionType);
return LibraryManager.ResolvePaths(GetFileSystemChildren(directoryService), directoryService, this, collectionType);
}
/// <summary>
@ -782,6 +755,12 @@ namespace MediaBrowser.Controller.Entities
private BaseItem RetrieveChild(BaseItem child)
{
if (child.Id == Guid.Empty)
{
Logger.Error("Item found with empty Id: " + (child.Path ?? child.Name));
return null;
}
var item = LibraryManager.GetMemoryItemById(child.Id);
if (item != null)

@ -28,12 +28,14 @@ namespace MediaBrowser.Controller.Entities
SoundtrackIds = new List<Guid>();
RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>();
RemoteTrailerIds = new List<Guid>();
ThemeSongIds = new List<Guid>();
ThemeVideoIds = new List<Guid>();
Tags = new List<string>();
}
public List<Guid> LocalTrailerIds { get; set; }
public List<Guid> RemoteTrailerIds { get; set; }
/// <summary>
/// Gets or sets the tags.
@ -119,5 +121,16 @@ namespace MediaBrowser.Controller.Entities
return id;
}
/// <summary>
/// Gets the trailer ids.
/// </summary>
/// <returns>List&lt;Guid&gt;.</returns>
public List<Guid> GetTrailerIds()
{
var list = LocalTrailerIds.ToList();
list.AddRange(RemoteTrailerIds);
return list;
}
}
}

@ -21,6 +21,12 @@ namespace MediaBrowser.Controller.Entities
/// <value>The path.</value>
string Path { get; set; }
/// <summary>
/// Gets the file name without extension.
/// </summary>
/// <value>The file name without extension.</value>
string FileNameWithoutExtension { get; }
/// <summary>
/// Gets the identifier.
/// </summary>

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
public interface IHasTrailers
public interface IHasTrailers : IHasProviderIds
{
/// <summary>
/// Gets or sets the remote trailers.
@ -17,5 +17,12 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <value>The local trailer ids.</value>
List<Guid> LocalTrailerIds { get; set; }
List<Guid> RemoteTrailerIds { get; set; }
/// <summary>
/// Gets the trailer ids.
/// </summary>
/// <returns>List&lt;Guid&gt;.</returns>
List<Guid> GetTrailerIds();
}
}

@ -15,15 +15,19 @@ namespace MediaBrowser.Controller.Entities.Movies
/// <summary>
/// Class BoxSet
/// </summary>
public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer
public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer, IHasShares
{
public List<Share> Shares { get; set; }
public BoxSet()
{
RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>();
RemoteTrailerIds = new List<Guid>();
DisplayOrder = ItemSortBy.PremiereDate;
Keywords = new List<string>();
Shares = new List<Share>();
}
protected override bool FilterLinkedChildrenPerUser
@ -35,6 +39,7 @@ namespace MediaBrowser.Controller.Entities.Movies
}
public List<Guid> LocalTrailerIds { get; set; }
public List<Guid> RemoteTrailerIds { get; set; }
/// <summary>
/// Gets or sets the remote trailers.
@ -76,6 +81,17 @@ namespace MediaBrowser.Controller.Entities.Movies
}
}
/// <summary>
/// Gets the trailer ids.
/// </summary>
/// <returns>List&lt;Guid&gt;.</returns>
public List<Guid> GetTrailerIds()
{
var list = LocalTrailerIds.ToList();
list.AddRange(RemoteTrailerIds);
return list;
}
public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
{
var children = base.GetChildren(user, includeLinkedChildren);
@ -147,5 +163,20 @@ namespace MediaBrowser.Controller.Entities.Movies
progress.Report(100);
}
public override bool IsVisible(User user)
{
if (base.IsVisible(user))
{
var userId = user.Id.ToString("N");
return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) ||
// Need to support this for boxsets created prior to the creation of Shares
Shares.Count == 0;
}
return false;
}
}
}

@ -36,6 +36,7 @@ namespace MediaBrowser.Controller.Entities.Movies
SoundtrackIds = new List<Guid>();
RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>();
RemoteTrailerIds = new List<Guid>();
ThemeSongIds = new List<Guid>();
ThemeVideoIds = new List<Guid>();
BoxSetIdList = new List<Guid>();
@ -49,6 +50,7 @@ namespace MediaBrowser.Controller.Entities.Movies
public float? Metascore { get; set; }
public List<Guid> LocalTrailerIds { get; set; }
public List<Guid> RemoteTrailerIds { get; set; }
public List<string> Keywords { get; set; }
public List<MediaUrl> RemoteTrailers { get; set; }
@ -89,6 +91,17 @@ namespace MediaBrowser.Controller.Entities.Movies
/// <value>The name of the TMDB collection.</value>
public string TmdbCollectionName { get; set; }
/// <summary>
/// Gets the trailer ids.
/// </summary>
/// <returns>List&lt;Guid&gt;.</returns>
public List<Guid> GetTrailerIds()
{
var list = LocalTrailerIds.ToList();
list.AddRange(RemoteTrailerIds);
return list;
}
/// <summary>
/// Gets the user data key.
/// </summary>
@ -119,7 +132,7 @@ namespace MediaBrowser.Controller.Entities.Movies
private async Task<bool> RefreshSpecialFeatures(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
var newItems = LoadSpecialFeatures(fileSystemChildren, options.DirectoryService).ToList();
var newItems = LibraryManager.FindExtras(this, fileSystemChildren, options.DirectoryService).ToList();
var newItemIds = newItems.Select(i => i.Id).ToList();
var itemsChanged = !SpecialFeatureIds.SequenceEqual(newItemIds);
@ -158,6 +171,22 @@ namespace MediaBrowser.Controller.Entities.Movies
ProductionYear = yearInName;
hasChanges = true;
}
else
{
// Try to get the year from the folder name
if (!IsInMixedFolder)
{
info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));
yearInName = info.Year;
if (yearInName.HasValue)
{
ProductionYear = yearInName;
hasChanges = true;
}
}
}
}
return hasChanges;

@ -89,5 +89,41 @@ namespace MediaBrowser.Controller.Entities
{
return GetItemLookupInfo<MusicVideoInfo>();
}
public override bool BeforeMetadataRefresh()
{
var hasChanges = base.BeforeMetadataRefresh();
if (!ProductionYear.HasValue)
{
var info = LibraryManager.ParseName(Name);
var yearInName = info.Year;
if (yearInName.HasValue)
{
ProductionYear = yearInName;
hasChanges = true;
}
else
{
// Try to get the year from the folder name
if (!IsInMixedFolder)
{
info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));
yearInName = info.Year;
if (yearInName.HasValue)
{
ProductionYear = yearInName;
hasChanges = true;
}
}
}
}
return hasChanges;
}
}
}

@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
public interface IHasShares
{
List<Share> Shares { get; set; }
}
public class Share
{
public string UserId { get; set; }
public bool CanEdit { get; set; }
}
}

@ -36,6 +36,7 @@ namespace MediaBrowser.Controller.Entities.TV
SoundtrackIds = new List<Guid>();
RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>();
RemoteTrailerIds = new List<Guid>();
DisplaySpecialsWithSeasons = true;
}
@ -57,7 +58,8 @@ namespace MediaBrowser.Controller.Entities.TV
public bool DisplaySpecialsWithSeasons { get; set; }
public List<Guid> LocalTrailerIds { get; set; }
public List<Guid> RemoteTrailerIds { get; set; }
public List<MediaUrl> RemoteTrailers { get; set; }
/// <summary>
@ -109,6 +111,17 @@ namespace MediaBrowser.Controller.Entities.TV
return this.GetProviderId(MetadataProviders.Tvdb) ?? this.GetProviderId(MetadataProviders.Tvcom) ?? base.GetUserDataKey();
}
/// <summary>
/// Gets the trailer ids.
/// </summary>
/// <returns>List&lt;Guid&gt;.</returns>
public List<Guid> GetTrailerIds()
{
var list = LocalTrailerIds.ToList();
list.AddRange(RemoteTrailerIds);
return list;
}
// Studio, Genre and Rating will all be the same so makes no sense to index by these
protected override IEnumerable<string> GetIndexByOptions()
{

@ -12,7 +12,8 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Class Trailer
/// </summary>
public class Trailer : Video, IHasCriticRating, IHasSoundtracks, IHasProductionLocations, IHasBudget, IHasTrailers, IHasKeywords, IHasTaglines, IHasMetascore, IHasLookupInfo<TrailerInfo>
[Obsolete]
public class Trailer : Video, IHasCriticRating, IHasSoundtracks, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTaglines, IHasMetascore, IHasLookupInfo<TrailerInfo>
{
public List<Guid> SoundtrackIds { get; set; }
@ -23,15 +24,12 @@ namespace MediaBrowser.Controller.Entities
RemoteTrailers = new List<MediaUrl>();
Taglines = new List<string>();
SoundtrackIds = new List<Guid>();
LocalTrailerIds = new List<Guid>();
Keywords = new List<string>();
ProductionLocations = new List<string>();
}
public float? Metascore { get; set; }
public List<Guid> LocalTrailerIds { get; set; }
public List<MediaUrl> RemoteTrailers { get; set; }
public List<string> Keywords { get; set; }

@ -4,6 +4,7 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Connect;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Users;
using System;
using System.IO;
using System.Linq;
@ -287,7 +288,7 @@ namespace MediaBrowser.Controller.Entities
var localTime = date.ToLocalTime();
return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) &&
return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) &&
IsWithinTime(schedule, localTime);
}

@ -63,8 +63,7 @@ namespace MediaBrowser.Controller.Entities
{
CollectionType.Books,
CollectionType.HomeVideos,
CollectionType.Photos,
CollectionType.Trailers
CollectionType.Photos
};
var collectionFolder = folder as ICollectionFolder;

@ -1428,7 +1428,7 @@ namespace MediaBrowser.Controller.Entities
var hasTrailers = item as IHasTrailers;
if (hasTrailers != null)
{
trailerCount = hasTrailers.LocalTrailerIds.Count;
trailerCount = hasTrailers.GetTrailerIds().Count;
}
var ok = val ? trailerCount > 0 : trailerCount == 0;

@ -1,12 +1,10 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -19,24 +17,23 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Class Video
/// </summary>
public class Video : BaseItem,
IHasAspectRatio,
IHasTags,
public class Video : BaseItem,
IHasAspectRatio,
IHasTags,
ISupportsPlaceHolders,
IHasMediaSources,
IHasShortOverview,
IHasPreferredMetadataLanguage,
IThemeMedia
{
public bool IsMultiPart { get; set; }
public bool HasLocalAlternateVersions { get; set; }
public Guid? PrimaryVersionId { get; set; }
public List<Guid> AdditionalPartIds { get; set; }
public List<Guid> LocalAlternateVersionIds { get; set; }
public List<string> AdditionalParts { get; set; }
public List<string> LocalAlternateVersions { get; set; }
public List<LinkedChild> LinkedAlternateVersions { get; set; }
public bool IsThemeMedia { get; set; }
public string FormatName { get; set; }
public long? Size { get; set; }
public string Container { get; set; }
@ -56,12 +53,12 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <value>The timestamp.</value>
public TransportStreamTimestamp? Timestamp { get; set; }
public Video()
{
PlayableStreamFileNames = new List<string>();
AdditionalPartIds = new List<Guid>();
LocalAlternateVersionIds = new List<Guid>();
AdditionalParts = new List<string>();
LocalAlternateVersions = new List<string>();
Tags = new List<string>();
SubtitleFiles = new List<string>();
LinkedAlternateVersions = new List<LinkedChild>();
@ -78,11 +75,31 @@ namespace MediaBrowser.Controller.Entities
{
get
{
return LinkedAlternateVersions.Count + LocalAlternateVersionIds.Count + 1;
return LinkedAlternateVersions.Count + LocalAlternateVersions.Count + 1;
}
}
public List<LinkedChild> LinkedAlternateVersions { get; set; }
[IgnoreDataMember]
public bool IsStacked
{
get { return AdditionalParts.Count > 0; }
}
[IgnoreDataMember]
public bool HasLocalAlternateVersions
{
get { return LocalAlternateVersions.Count > 0; }
}
public IEnumerable<Guid> GetAdditionalPartIds()
{
return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
}
public IEnumerable<Guid> GetLocalAlternateVersionIds()
{
return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
}
/// <summary>
/// Gets the linked children.
@ -90,7 +107,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>IEnumerable{BaseItem}.</returns>
public IEnumerable<Video> GetAlternateVersions()
{
var filesWithinSameDirectory = LocalAlternateVersionIds
var filesWithinSameDirectory = GetLocalAlternateVersionIds()
.Select(i => LibraryManager.GetItemById(i))
.Where(i => i != null)
.OfType<Video>();
@ -116,7 +133,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>IEnumerable{Video}.</returns>
public IEnumerable<Video> GetAdditionalParts()
{
return AdditionalPartIds
return GetAdditionalPartIds()
.Select(i => LibraryManager.GetItemById(i))
.Where(i => i != null)
.OfType<Video>()
@ -200,7 +217,7 @@ namespace MediaBrowser.Controller.Entities
{
get
{
if (IsMultiPart)
if (IsStacked)
{
return System.IO.Path.GetDirectoryName(Path);
}
@ -218,6 +235,46 @@ namespace MediaBrowser.Controller.Entities
}
}
[IgnoreDataMember]
public override string FileNameWithoutExtension
{
get
{
if (LocationType == LocationType.FileSystem)
{
if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd || VideoType == VideoType.HdDvd)
{
return System.IO.Path.GetFileName(Path);
}
return System.IO.Path.GetFileNameWithoutExtension(Path);
}
return null;
}
}
internal override bool IsValidFromResolver(BaseItem newItem)
{
var current = this;
var newAsVideo = newItem as Video;
if (newAsVideo != null)
{
if (!current.AdditionalParts.SequenceEqual(newAsVideo.AdditionalParts, StringComparer.OrdinalIgnoreCase))
{
return false;
}
if (!current.LocalAlternateVersions.SequenceEqual(newAsVideo.LocalAlternateVersions, StringComparer.OrdinalIgnoreCase))
{
return false;
}
}
return base.IsValidFromResolver(newItem);
}
public string MainFeaturePlaylistName { get; set; }
/// <summary>
@ -263,37 +320,34 @@ namespace MediaBrowser.Controller.Entities
{
var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
if (IsStacked)
{
var tasks = AdditionalParts
.Select(i => RefreshMetadataForOwnedVideo(options, i, cancellationToken));
await Task.WhenAll(tasks).ConfigureAwait(false);
}
// Must have a parent to have additional parts or alternate versions
// In other words, it must be part of the Parent/Child tree
// The additional parts won't have additional parts themselves
if (LocationType == LocationType.FileSystem && Parent != null)
{
if (IsMultiPart)
{
var additionalPartsChanged = await RefreshAdditionalParts(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
if (additionalPartsChanged)
{
hasChanges = true;
}
}
else
if (!IsStacked)
{
RefreshLinkedAlternateVersions();
var additionalPartsChanged = await RefreshAlternateVersionsWithinSameDirectory(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
var tasks = LocalAlternateVersions
.Select(i => RefreshMetadataForOwnedVideo(options, i, cancellationToken));
if (additionalPartsChanged)
{
hasChanges = true;
}
await Task.WhenAll(tasks).ConfigureAwait(false);
}
}
return hasChanges;
}
private bool RefreshLinkedAlternateVersions()
private void RefreshLinkedAlternateVersions()
{
foreach (var child in LinkedAlternateVersions)
{
@ -303,111 +357,13 @@ namespace MediaBrowser.Controller.Entities
child.ItemId = null;
}
}
return false;
}
/// <summary>
/// Refreshes the additional parts.
/// </summary>
/// <param name="options">The options.</param>
/// <param name="fileSystemChildren">The file system children.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{System.Boolean}.</returns>
private async Task<bool> RefreshAdditionalParts(MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
var newItems = LoadAdditionalParts(fileSystemChildren, options.DirectoryService).ToList();
var newItemIds = newItems.Select(i => i.Id).ToList();
var itemsChanged = !AdditionalPartIds.SequenceEqual(newItemIds);
var tasks = newItems.Select(i => i.RefreshMetadata(options, cancellationToken));
await Task.WhenAll(tasks).ConfigureAwait(false);
AdditionalPartIds = newItemIds;
return itemsChanged;
}
/// <summary>
/// Loads the additional parts.
/// </summary>
/// <returns>IEnumerable{Video}.</returns>
private IEnumerable<Video> LoadAdditionalParts(IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
{
var files = LibraryManager.GetAdditionalParts(Path, VideoType, fileSystemChildren);
return LibraryManager.ResolvePaths<Video>(files, directoryService, null).Select(video =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(video.Id) as Video;
if (dbItem != null)
{
video = dbItem;
}
return video;
// Sort them so that the list can be easily compared for changes
}).OrderBy(i => i.Path).ToList();
}
private async Task<bool> RefreshAlternateVersionsWithinSameDirectory(MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
var newItems = HasLocalAlternateVersions ?
LoadAlternateVersionsWithinSameDirectory(fileSystemChildren, options.DirectoryService).ToList() :
new List<Video>();
var newItemIds = newItems.Select(i => i.Id).ToList();
var itemsChanged = !LocalAlternateVersionIds.SequenceEqual(newItemIds);
var tasks = newItems.Select(i => RefreshAlternateVersion(options, i, cancellationToken));
await Task.WhenAll(tasks).ConfigureAwait(false);
LocalAlternateVersionIds = newItemIds;
return itemsChanged;
}
private Task RefreshAlternateVersion(MetadataRefreshOptions options, Video video, CancellationToken cancellationToken)
{
var currentImagePath = video.GetImagePath(ImageType.Primary);
var ownerImagePath = this.GetImagePath(ImageType.Primary);
var newOptions = new MetadataRefreshOptions(options.DirectoryService)
{
ImageRefreshMode = options.ImageRefreshMode,
MetadataRefreshMode = options.MetadataRefreshMode,
ReplaceAllMetadata = options.ReplaceAllMetadata
};
if (!string.Equals(currentImagePath, ownerImagePath, StringComparison.OrdinalIgnoreCase))
{
newOptions.ForceSave = true;
if (string.IsNullOrWhiteSpace(ownerImagePath))
{
video.ImageInfos.Clear();
}
else
{
video.SetImagePath(ImageType.Primary, ownerImagePath);
}
}
return video.RefreshMetadata(newOptions, cancellationToken);
}
public override async Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
{
await base.UpdateToRepository(updateReason, cancellationToken).ConfigureAwait(false);
foreach (var item in LocalAlternateVersionIds.Select(i => LibraryManager.GetItemById(i)))
foreach (var item in GetLocalAlternateVersionIds().Select(i => LibraryManager.GetItemById(i)))
{
item.ImageInfos = ImageInfos;
item.Overview = Overview;
@ -422,56 +378,6 @@ namespace MediaBrowser.Controller.Entities
}
}
/// <summary>
/// Loads the additional parts.
/// </summary>
/// <returns>IEnumerable{Video}.</returns>
private IEnumerable<Video> LoadAlternateVersionsWithinSameDirectory(IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
{
IEnumerable<FileSystemInfo> files;
// Only support this for video files. For folder rips, they'll have to use the linking feature
if (VideoType == VideoType.VideoFile || VideoType == VideoType.Iso)
{
var path = Path;
var filenamePrefix = System.IO.Path.GetFileName(System.IO.Path.GetDirectoryName(path));
files = fileSystemChildren.Where(i =>
{
if ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
{
return false;
}
return !string.Equals(i.FullName, path, StringComparison.OrdinalIgnoreCase) &&
LibraryManager.IsVideoFile(i.FullName) &&
i.Name.StartsWith(filenamePrefix + " - ", StringComparison.OrdinalIgnoreCase);
});
}
else
{
files = new List<FileSystemInfo>();
}
return LibraryManager.ResolvePaths<Video>(files, directoryService, null).Select(video =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(video.Id) as Video;
if (dbItem != null)
{
video = dbItem;
}
video.PrimaryVersionId = Id;
return video;
// Sort them so that the list can be easily compared for changes
}).OrderBy(i => i.Path).ToList();
}
public override IEnumerable<string> GetDeletePaths()
{
if (!IsInMixedFolder)
@ -539,7 +445,7 @@ namespace MediaBrowser.Controller.Entities
var mediaStreams = ItemRepository.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList();
var locationType = i.LocationType;
var info = new MediaSourceInfo
{
Id = i.Id.ToString("N"),

@ -24,7 +24,9 @@ namespace MediaBrowser.Controller.Library
/// <param name="parent">The parent.</param>
/// <param name="collectionType">Type of the collection.</param>
/// <returns>BaseItem.</returns>
BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null, string collectionType = null);
BaseItem ResolvePath(FileSystemInfo fileInfo,
Folder parent = null,
string collectionType = null);
/// <summary>
/// Resolves a set of files into a list of BaseItem
@ -35,8 +37,10 @@ namespace MediaBrowser.Controller.Library
/// <param name="parent">The parent.</param>
/// <param name="collectionType">Type of the collection.</param>
/// <returns>List{``0}.</returns>
List<T> ResolvePaths<T>(IEnumerable<FileSystemInfo> files, IDirectoryService directoryService, Folder parent, string collectionType = null)
where T : BaseItem;
IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemInfo> files,
IDirectoryService directoryService,
Folder parent, string
collectionType = null);
/// <summary>
/// Gets the root folder.
@ -229,46 +233,6 @@ namespace MediaBrowser.Controller.Library
/// <returns>BaseItem.</returns>
BaseItem RetrieveItem(Guid id);
/// <summary>
/// Validates the artists.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task.</returns>
Task ValidateArtists(CancellationToken cancellationToken, IProgress<double> progress);
/// <summary>
/// Validates the music genres.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task.</returns>
Task ValidateMusicGenres(CancellationToken cancellationToken, IProgress<double> progress);
/// <summary>
/// Validates the game genres.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task.</returns>
Task ValidateGameGenres(CancellationToken cancellationToken, IProgress<double> progress);
/// <summary>
/// Validates the genres.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task.</returns>
Task ValidateGenres(CancellationToken cancellationToken, IProgress<double> progress);
/// <summary>
/// Validates the studios.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task.</returns>
Task ValidateStudios(CancellationToken cancellationToken, IProgress<double> progress);
/// <summary>
/// Occurs when [item added].
/// </summary>
@ -405,14 +369,31 @@ namespace MediaBrowser.Controller.Library
ItemLookupInfo ParseName(string name);
/// <summary>
/// Gets the additional parts.
/// Gets the new item identifier.
/// </summary>
/// <param name="file">The file.</param>
/// <param name="key">The key.</param>
/// <param name="type">The type.</param>
/// <param name="files">The files.</param>
/// <returns>IEnumerable&lt;System.String&gt;.</returns>
IEnumerable<FileSystemInfo> GetAdditionalParts(string file,
VideoType type,
IEnumerable<FileSystemInfo> files);
/// <returns>Guid.</returns>
Guid GetNewItemId(string key, Type type);
/// <summary>
/// Finds the trailers.
/// </summary>
/// <param name="owner">The owner.</param>
/// <param name="fileSystemChildren">The file system children.</param>
/// <param name="directoryService">The directory service.</param>
/// <returns>IEnumerable&lt;Trailer&gt;.</returns>
IEnumerable<Video> FindTrailers(BaseItem owner, List<FileSystemInfo> fileSystemChildren,
IDirectoryService directoryService);
/// <summary>
/// Finds the extras.
/// </summary>
/// <param name="owner">The owner.</param>
/// <param name="fileSystemChildren">The file system children.</param>
/// <param name="directoryService">The directory service.</param>
/// <returns>IEnumerable&lt;Video&gt;.</returns>
IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemInfo> fileSystemChildren,
IDirectoryService directoryService);
}
}

@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Playlists;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Library
@ -28,6 +29,13 @@ namespace MediaBrowser.Controller.Library
/// <returns>IEnumerable{Audio}.</returns>
IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user);
/// <summary>
/// Gets the instant mix from playlist.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable&lt;Audio&gt;.</returns>
IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user);
/// <summary>
/// Gets the instant mix from genre.
/// </summary>
/// <param name="genres">The genres.</param>

@ -164,5 +164,19 @@ namespace MediaBrowser.Controller.Library
/// <param name="pin">The pin.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
Task<PinRedeemResult> RedeemPasswordResetPin(string pin);
/// <summary>
/// Gets the user policy.
/// </summary>
/// <param name="userId">The user identifier.</param>
/// <returns>UserPolicy.</returns>
UserPolicy GetUserPolicy(string userId);
/// <summary>
/// Updates the user policy.
/// </summary>
/// <param name="userId">The user identifier.</param>
/// <param name="userPolicy">The user policy.</param>
Task UpdateUserPolicy(string userId, UserPolicy userPolicy);
}
}

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Drawing;
namespace MediaBrowser.Controller.LiveTv
{
@ -109,7 +110,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="channelId">The channel identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{Stream}.</returns>
Task<StreamResponseInfo> GetChannelImageAsync(string channelId, CancellationToken cancellationToken);
Task<ImageStream> GetChannelImageAsync(string channelId, CancellationToken cancellationToken);
/// <summary>
/// Gets the recording image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to RecordingInfo
@ -117,7 +118,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="recordingId">The recording identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{ImageResponseInfo}.</returns>
Task<StreamResponseInfo> GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken);
Task<ImageStream> GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken);
/// <summary>
/// Gets the program image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to ProgramInfo
@ -126,7 +127,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="channelId">The channel identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{ImageResponseInfo}.</returns>
Task<StreamResponseInfo> GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken);
Task<ImageStream> GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken);
/// <summary>
/// Gets the recordings asynchronous.

@ -1,20 +0,0 @@
using MediaBrowser.Controller.Drawing;
using System.IO;
namespace MediaBrowser.Controller.LiveTv
{
public class StreamResponseInfo
{
/// <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 ImageFormat Format { get; set; }
}
}

@ -55,7 +55,6 @@
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="Microsoft.CSharp" />
@ -106,7 +105,6 @@
<Compile Include="Devices\IDeviceRepository.cs" />
<Compile Include="Dlna\ControlRequest.cs" />
<Compile Include="Dlna\ControlResponse.cs" />
<Compile Include="Dlna\DlnaIconResponse.cs" />
<Compile Include="Dlna\EventSubscriptionResponse.cs" />
<Compile Include="Dlna\IConnectionManager.cs" />
<Compile Include="Dlna\IContentDirectory.cs" />
@ -114,9 +112,9 @@
<Compile Include="Dlna\IEventManager.cs" />
<Compile Include="Dlna\IUpnpService.cs" />
<Compile Include="Drawing\IImageProcessor.cs" />
<Compile Include="Drawing\ImageFormat.cs" />
<Compile Include="Drawing\ImageProcessingOptions.cs" />
<Compile Include="Drawing\ImageProcessorExtensions.cs" />
<Compile Include="Drawing\ImageStream.cs" />
<Compile Include="Dto\IDtoService.cs" />
<Compile Include="Entities\AdultVideo.cs" />
<Compile Include="Entities\Audio\IHasAlbumArtist.cs" />
@ -164,6 +162,7 @@
<Compile Include="Entities\IHasAwards.cs" />
<Compile Include="Entities\Photo.cs" />
<Compile Include="Entities\PhotoAlbum.cs" />
<Compile Include="Entities\Share.cs" />
<Compile Include="Entities\UserView.cs" />
<Compile Include="Entities\UserViewBuilder.cs" />
<Compile Include="FileOrganization\IFileOrganizationService.cs" />
@ -192,7 +191,6 @@
<Compile Include="LiveTv\LiveTvException.cs" />
<Compile Include="LiveTv\LiveTvServiceStatusInfo.cs" />
<Compile Include="LiveTv\LiveTvTunerInfo.cs" />
<Compile Include="LiveTv\StreamResponseInfo.cs" />
<Compile Include="LiveTv\LiveTvProgram.cs" />
<Compile Include="LiveTv\LiveTvVideoRecording.cs" />
<Compile Include="LiveTv\ProgramInfo.cs" />
@ -270,7 +268,6 @@
<Compile Include="Providers\MetadataStatus.cs" />
<Compile Include="Providers\ISeriesOrderManager.cs" />
<Compile Include="Session\ISessionManager.cs" />
<Compile Include="Drawing\ImageExtensions.cs" />
<Compile Include="Entities\AggregateFolder.cs" />
<Compile Include="Entities\Audio\Audio.cs" />
<Compile Include="Entities\Audio\MusicAlbum.cs" />

@ -10,12 +10,6 @@ namespace MediaBrowser.Controller.Persistence
/// </summary>
public interface IUserRepository : IRepository
{
/// <summary>
/// Opens the connection to the repository
/// </summary>
/// <returns>Task.</returns>
Task Initialize();
/// <summary>
/// Deletes the user.
/// </summary>

@ -11,10 +11,17 @@ using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Playlists
{
public class Playlist : Folder
public class Playlist : Folder, IHasShares
{
public string OwnerUserId { get; set; }
public List<Share> Shares { get; set; }
public Playlist()
{
Shares = new List<Share>();
}
[IgnoreDataMember]
protected override bool FilterLinkedChildrenPerUser
{
@ -166,7 +173,15 @@ namespace MediaBrowser.Controller.Playlists
public override bool IsVisible(User user)
{
return base.IsVisible(user) && string.Equals(user.Id.ToString("N"), OwnerUserId);
if (base.IsVisible(user))
{
var userId = user.Id.ToString("N");
return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) ||
string.Equals(OwnerUserId, userId, StringComparison.OrdinalIgnoreCase);
}
return false;
}
}
}

@ -819,6 +819,19 @@ namespace MediaBrowser.Controller.Providers
break;
}
case "Shares":
{
using (var subtree = reader.ReadSubtree())
{
var hasShares = item as IHasShares;
if (hasShares != null)
{
FetchFromSharesNode(subtree, hasShares);
}
}
break;
}
case "Format3D":
{
var video = item as Video;
@ -853,6 +866,71 @@ namespace MediaBrowser.Controller.Providers
}
}
private void FetchFromSharesNode(XmlReader reader, IHasShares item)
{
reader.MoveToContent();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Share":
{
using (var subtree = reader.ReadSubtree())
{
var share = GetShareFromNode(subtree);
if (share != null)
{
item.Shares.Add(share);
}
}
break;
}
default:
reader.Skip();
break;
}
}
}
}
private Share GetShareFromNode(XmlReader reader)
{
var share = new Share();
reader.MoveToContent();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "UserId":
{
share.UserId = reader.ReadElementContentAsString();
break;
}
case "CanEdit":
{
share.CanEdit = string.Equals(reader.ReadElementContentAsString(), true.ToString(), StringComparison.OrdinalIgnoreCase);
break;
}
default:
reader.Skip();
break;
}
}
}
return share;
}
private void FetchFromCountriesNode(XmlReader reader, T item)
{
reader.MoveToContent();
@ -1340,6 +1418,12 @@ namespace MediaBrowser.Controller.Providers
}
}
// This is valid
if (!string.IsNullOrWhiteSpace(linkedItem.Path))
{
return linkedItem;
}
return string.IsNullOrWhiteSpace(linkedItem.ItemName) || string.IsNullOrWhiteSpace(linkedItem.ItemType) ? null : linkedItem;
}

@ -1,7 +1,7 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
using System.Drawing;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Providers
@ -49,6 +49,6 @@ namespace MediaBrowser.Controller.Providers
/// <param name="imageIndex">Index of the image.</param>
/// <returns>Task{Image}.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
Task<Image> EnhanceImageAsync(IHasImages item, Image originalImage, ImageType imageType, int imageIndex);
Task<ImageStream> EnhanceImageAsync(IHasImages item, ImageStream originalImage, ImageType imageType, int imageIndex);
}
}

@ -1,5 +1,5 @@
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
using System.Collections.Generic;

@ -1,5 +1,5 @@
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;

@ -1,5 +1,8 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using System.Collections.Generic;
using System.IO;
namespace MediaBrowser.Controller.Resolvers
{
@ -20,4 +23,24 @@ namespace MediaBrowser.Controller.Resolvers
/// <value>The priority.</value>
ResolverPriority Priority { get; }
}
public interface IMultiItemResolver
{
MultiItemResolverResult ResolveMultiple(Folder parent,
List<FileSystemInfo> files,
string collectionType,
IDirectoryService directoryService);
}
public class MultiItemResolverResult
{
public List<BaseItem> Items { get; set; }
public List<FileSystemInfo> ExtraFiles { get; set; }
public MultiItemResolverResult()
{
Items = new List<BaseItem>();
ExtraFiles = new List<FileSystemInfo>();
}
}
}

@ -1,6 +1,5 @@
using System.IO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Devices;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Sync;
using System.Collections.Generic;
@ -29,7 +28,7 @@ namespace MediaBrowser.Controller.Sync
/// <param name="id">The identifier.</param>
/// <returns>SyncJob.</returns>
SyncJob GetJob(string id);
/// <summary>
/// Cancels the job.
/// </summary>
@ -53,5 +52,12 @@ namespace MediaBrowser.Controller.Sync
/// <param name="item">The item.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
bool SupportsSync(BaseItem item);
/// <summary>
/// Gets the device profile.
/// </summary>
/// <param name="targetId">The target identifier.</param>
/// <returns>DeviceProfile.</returns>
DeviceProfile GetDeviceProfile(string targetId);
}
}

@ -27,6 +27,13 @@ namespace MediaBrowser.Controller.Sync
/// <returns>Task.</returns>
Task Update(SyncJob job);
/// <summary>
/// Deletes the job.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>Task.</returns>
Task DeleteJob(string id);
/// <summary>
/// Gets the jobs.
/// </summary>
@ -54,5 +61,12 @@ namespace MediaBrowser.Controller.Sync
/// <param name="jobItem">The job item.</param>
/// <returns>Task.</returns>
Task Update(SyncJobItem jobItem);
/// <summary>
/// Gets the job items.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>IEnumerable&lt;SyncJobItem&gt;.</returns>
QueryResult<SyncJobItem> GetJobItems(SyncJobItemQuery query);
}
}

@ -1,4 +1,5 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
@ -23,6 +24,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
private readonly IServerConfigurationManager _config;
private readonly IUserManager _userManager;
private readonly ILocalizationManager _localization;
private readonly IChannelManager _channelManager;
public ContentDirectory(IDlnaManager dlna,
IUserDataManager userDataManager,
@ -31,7 +33,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
IServerConfigurationManager config,
IUserManager userManager,
ILogger logger,
IHttpClient httpClient, ILocalizationManager localization)
IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager)
: base(logger, httpClient)
{
_dlna = dlna;
@ -41,6 +43,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
_config = config;
_userManager = userManager;
_localization = localization;
_channelManager = channelManager;
}
private int SystemUpdateId
@ -77,7 +80,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
user,
SystemUpdateId,
_config,
_localization)
_localization,
_channelManager)
.ProcessControlRequest(request);
}

@ -1,13 +1,16 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Dlna.Didl;
using MediaBrowser.Dlna.Server;
using MediaBrowser.Dlna.Service;
using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@ -26,6 +29,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
public class ControlHandler : BaseControlHandler
{
private readonly ILibraryManager _libraryManager;
private readonly IChannelManager _channelManager;
private readonly IUserDataManager _userDataManager;
private readonly User _user;
@ -41,13 +45,14 @@ namespace MediaBrowser.Dlna.ContentDirectory
private readonly DeviceProfile _profile;
public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization)
public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager)
: base(config, logger)
{
_libraryManager = libraryManager;
_userDataManager = userDataManager;
_user = user;
_systemUpdateId = systemUpdateId;
_channelManager = channelManager;
_profile = profile;
_didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, userDataManager, localization);
@ -208,14 +213,15 @@ namespace MediaBrowser.Dlna.ContentDirectory
var serverItem = GetItemFromObjectId(id, user);
var item = serverItem.Item;
var totalCount = 0;
int totalCount;
if (string.Equals(flag, "BrowseMetadata"))
{
totalCount = 1;
if (item.IsFolder || serverItem.StubType.HasValue)
{
var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requested).ConfigureAwait(false));
totalCount = 1;
result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id));
}
@ -235,18 +241,19 @@ namespace MediaBrowser.Dlna.ContentDirectory
foreach (var i in childrenResult.Items)
{
var displayStubType = GetDisplayStubType(i, serverItem.Item);
var childItem = i.Item;
var displayStubType = i.StubType;
if (i.IsFolder || displayStubType.HasValue)
if (childItem.IsFolder || displayStubType.HasValue)
{
var childCount = (await GetUserItems(i, displayStubType, user, sortCriteria, null, 0).ConfigureAwait(false))
var childCount = (await GetUserItems(childItem, displayStubType, user, sortCriteria, null, 0).ConfigureAwait(false))
.TotalRecordCount;
result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, i, displayStubType, item, childCount, filter));
result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, childItem, displayStubType, item, childCount, filter));
}
else
{
result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, i, item, serverItem.StubType, deviceId, filter));
result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, childItem, item, serverItem.StubType, deviceId, filter));
}
}
}
@ -262,24 +269,6 @@ namespace MediaBrowser.Dlna.ContentDirectory
};
}
private StubType? GetDisplayStubType(BaseItem item, BaseItem context)
{
if (context == null || context.IsFolder)
{
var movie = item as Movie;
if (movie != null)
{
if (movie.LocalTrailerIds.Count > 0 ||
movie.SpecialFeatureIds.Count > 0)
{
return StubType.Folder;
}
}
}
return null;
}
private async Task<IEnumerable<KeyValuePair<string, string>>> HandleSearch(Headers sparams, User user, string deviceId)
{
var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", ""));
@ -408,16 +397,49 @@ namespace MediaBrowser.Dlna.ContentDirectory
}).ConfigureAwait(false);
}
private async Task<QueryResult<BaseItem>> GetUserItems(BaseItem item, StubType? stubType, User user, SortCriteria sort, int? startIndex, int? limit)
private async Task<QueryResult<ServerItem>> GetUserItems(BaseItem item, StubType? stubType, User user, SortCriteria sort, int? startIndex, int? limit)
{
if (stubType.HasValue)
{
var movie = item as Movie;
if (stubType.Value == StubType.People)
{
var items = item.People.Select(i =>
{
try
{
return _libraryManager.GetPerson(i.Name);
}
catch
{
return null;
}
}).Where(i => i != null).ToArray();
var result = new QueryResult<ServerItem>
{
Items = items.Select(i => new ServerItem { Item = i, StubType = StubType.Folder }).ToArray(),
TotalRecordCount = items.Length
};
if (movie != null)
return ApplyPaging(result, startIndex, limit);
}
if (stubType.Value == StubType.Folder)
{
return await GetMovieItems(movie).ConfigureAwait(false);
var movie = item as Movie;
if (movie != null)
{
return ApplyPaging(await GetMovieItems(movie).ConfigureAwait(false), startIndex, limit);
}
}
var person = item as Person;
if (person != null)
{
return await GetItemsFromPerson(person, user, startIndex, limit).ConfigureAwait(false);
}
return ApplyPaging(new QueryResult<ServerItem>(), startIndex, limit);
}
var folder = (Folder)item;
@ -428,7 +450,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
sortOrders.Add(ItemSortBy.SortName);
}
return await folder.GetItems(new InternalItemsQuery
var queryResult = await folder.GetItems(new InternalItemsQuery
{
Limit = limit,
StartIndex = startIndex,
@ -438,22 +460,138 @@ namespace MediaBrowser.Dlna.ContentDirectory
Filter = FilterUnsupportedContent
}).ConfigureAwait(false);
var serverItems = queryResult
.Items
.Select(i => new ServerItem
{
Item = i,
StubType = GetDisplayStubType(i, item)
})
.ToArray();
return new QueryResult<ServerItem>
{
TotalRecordCount = queryResult.TotalRecordCount,
Items = serverItems
};
}
private async Task<QueryResult<ServerItem>> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
{
var items = user.RootFolder.GetRecursiveChildren(user)
.Where(i => i is Movie || i is Series)
.Where(i => i.ContainsPerson(person.Name))
.ToList();
var trailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery
{
ContentTypes = new[] { ChannelMediaContentType.MovieExtra },
ExtraTypes = new[] { ExtraType.Trailer },
UserId = user.Id.ToString("N")
}, CancellationToken.None).ConfigureAwait(false);
var currentIds = items.Select(i => i.GetProviderId(MetadataProviders.Imdb))
.ToList();
var trailersToAdd = trailerResult.Items
.Where(i => i.ContainsPerson(person.Name))
.Where(i =>
{
// Try to filter out dupes using imdb id
var imdb = i.GetProviderId(MetadataProviders.Imdb);
if (!string.IsNullOrWhiteSpace(imdb) &&
currentIds.Contains(imdb, StringComparer.OrdinalIgnoreCase))
{
return false;
}
return true;
});
items.AddRange(trailersToAdd);
items = _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending)
.Skip(startIndex ?? 0)
.Take(limit ?? int.MaxValue)
.ToList();
var serverItems = items.Select(i => new ServerItem
{
Item = i,
StubType = null
})
.ToArray();
return new QueryResult<ServerItem>
{
TotalRecordCount = serverItems.Length,
Items = serverItems
};
}
private Task<QueryResult<BaseItem>> GetMovieItems(Movie item)
private QueryResult<ServerItem> ApplyPaging(QueryResult<ServerItem> result, int? startIndex, int? limit)
{
result.Items = result.Items.Skip(startIndex ?? 0).Take(limit ?? int.MaxValue).ToArray();
return result;
}
private StubType? GetDisplayStubType(BaseItem item, BaseItem context)
{
if (context == null || context.IsFolder)
{
var movie = item as Movie;
if (movie != null)
{
if (movie.GetTrailerIds().Count > 0 ||
movie.SpecialFeatureIds.Count > 0)
{
return StubType.Folder;
}
}
if (EnablePeopleDisplay(item))
{
return StubType.Folder;
}
}
return null;
}
private bool EnablePeopleDisplay(BaseItem item)
{
if (item.People.Count > 0)
{
return item is Movie;
}
return false;
}
private Task<QueryResult<ServerItem>> GetMovieItems(Movie item)
{
var list = new List<BaseItem>();
list.Add(item);
list.AddRange(item.LocalTrailerIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null));
list.AddRange(item.GetTrailerIds().Select(i => _libraryManager.GetItemById(i)).Where(i => i != null));
list.AddRange(item.SpecialFeatureIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null));
list.AddRange(item.ThemeVideoIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null));
return Task.FromResult(new QueryResult<BaseItem>
var serverItems = list.Select(i => new ServerItem { Item = i, StubType = null })
.ToList();
serverItems.Add(new ServerItem
{
Items = list.ToArray(),
TotalRecordCount = list.Count
Item = item,
StubType = StubType.People
});
return Task.FromResult(new QueryResult<ServerItem>
{
Items = serverItems.ToArray(),
TotalRecordCount = serverItems.Count
});
}
@ -498,6 +636,11 @@ namespace MediaBrowser.Dlna.ContentDirectory
stubType = StubType.Folder;
id = id.Split(new[] { '_' }, 2)[1];
}
else if (id.StartsWith("people_", StringComparison.OrdinalIgnoreCase))
{
stubType = StubType.People;
id = id.Split(new[] { '_' }, 2)[1];
}
if (Guid.TryParse(id, out itemId))
{
@ -524,6 +667,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
public enum StubType
{
Folder = 0
Folder = 0,
People = 1
}
}

@ -111,7 +111,7 @@ namespace MediaBrowser.Dlna.Didl
}
}
AddCover(item, element);
AddCover(item, null, element);
return element;
}
@ -293,8 +293,17 @@ namespace MediaBrowser.Dlna.Didl
container.AppendChild(res);
}
private string GetDisplayName(BaseItem item, BaseItem context)
private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context)
{
if (itemStubType.HasValue && itemStubType.Value == StubType.People)
{
if (item is Video)
{
return _localization.GetLocalizedString("HeaderCastCrew");
}
return _localization.GetLocalizedString("HeaderPeople");
}
var episode = item as Episode;
var season = context as Season;
@ -460,7 +469,7 @@ namespace MediaBrowser.Dlna.Didl
AddCommonFields(folder, stubType, null, container, filter);
AddCover(folder, container);
AddCover(folder, stubType, container);
return container;
}
@ -491,7 +500,7 @@ namespace MediaBrowser.Dlna.Didl
// MediaMonkey for example won't display content without a title
//if (filter.Contains("dc:title"))
{
AddValue(element, "dc", "title", GetDisplayName(item, context), NS_DC);
AddValue(element, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC);
}
element.AppendChild(CreateObjectClass(element.OwnerDocument, item, itemStubType));
@ -741,8 +750,14 @@ namespace MediaBrowser.Dlna.Didl
}
}
private void AddCover(BaseItem item, XmlElement element)
private void AddCover(BaseItem item, StubType? stubType, XmlElement element)
{
if (stubType.HasValue && stubType.Value == StubType.People)
{
AddEmbeddedImageAsCover("people", element);
return;
}
var imageInfo = GetImageInfo(item);
if (imageInfo == null)
@ -801,6 +816,22 @@ namespace MediaBrowser.Dlna.Didl
}
}
private void AddEmbeddedImageAsCover(string name, XmlElement element)
{
var result = element.OwnerDocument;
var icon = result.CreateElement("upnp", "albumArtURI", NS_UPNP);
var profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
profile.InnerText = _profile.AlbumArtPn;
icon.SetAttributeNode(profile);
icon.InnerText = _serverAddress + "/Dlna/icons/people480.jpg";
element.AppendChild(icon);
icon = result.CreateElement("upnp", "icon", NS_UPNP);
icon.InnerText = _serverAddress + "/Dlna/icons/people48.jpg";
element.AppendChild(icon);
}
private void AddImageResElement(BaseItem item,
XmlElement element,
int maxWidth,

@ -8,6 +8,7 @@ using MediaBrowser.Dlna.Profiles;
using MediaBrowser.Dlna.Server;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dlna.Profiles;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using System;
@ -469,13 +470,13 @@ namespace MediaBrowser.Dlna
return new DescriptionXmlBuilder(profile, serverUuId, "").GetXml();
}
public DlnaIconResponse GetIcon(string filename)
public ImageStream GetIcon(string filename)
{
var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
? ImageFormat.Png
: ImageFormat.Jpg;
return new DlnaIconResponse
return new ImageStream
{
Format = format,
Stream = GetType().Assembly.GetManifestResourceStream("MediaBrowser.Dlna.Images." + filename.ToLower())
@ -522,10 +523,9 @@ namespace MediaBrowser.Dlna
new LgTvProfile(),
new Foobar2000Profile(),
new MediaMonkeyProfile(),
new Windows81Profile(),
//new Windows81Profile(),
//new WindowsMediaCenterProfile(),
new WindowsPhoneProfile(),
new AndroidProfile(),
//new WindowsPhoneProfile(),
new DirectTvProfile(),
new DishHopperJoeyProfile(),
new DefaultProfile(),

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

@ -180,11 +180,6 @@
<ItemGroup>
<EmbeddedResource Include="Profiles\Xml\MediaMonkey.xml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Profiles\Xml\Android.xml" />
<EmbeddedResource Include="Profiles\Xml\Windows 8 RT.xml" />
<EmbeddedResource Include="Profiles\Xml\Windows Phone.xml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Images\logo240.jpg" />
<EmbeddedResource Include="Images\logo240.png" />
@ -198,6 +193,14 @@
<ItemGroup>
<EmbeddedResource Include="Profiles\Xml\Popcorn Hour.xml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Images\people48.jpg" />
<EmbeddedResource Include="Images\people48.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Images\people480.jpg" />
<EmbeddedResource Include="Images\people480.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

@ -91,7 +91,7 @@ namespace MediaBrowser.Dlna.Profiles
},
new DirectPlayProfile
{
Container = "3gpp",
Container = "3gp",
VideoCodec = "h264,mpeg4",
AudioCodec = "aac,he-aac",
Type = DlnaProfileType.Video

@ -16,7 +16,12 @@ namespace MediaBrowser.Dlna.Profiles
Identification = new DeviceIdentification
{
ModelName = "Xbox One",
FriendlyName = "Xbox-SystemOS"
FriendlyName = "Xbox-SystemOS",
Headers = new[]
{
new HttpHeaderInfo {Name = "User-Agent", Value = "NSPlayer", Match = HeaderMatchType.Substring}
}
};
TranscodingProfiles = new[]

File diff suppressed because one or more lines are too long

@ -42,7 +42,7 @@
<DirectPlayProfile container="avi" audioCodec="mp3,ac3,dca" videoCodec="h264,mpeg4,mjpeg" type="Video" />
<DirectPlayProfile container="mkv" audioCodec="mp3,ac3,dca,aac" videoCodec="h264,mpeg4,mjpeg4" type="Video" />
<DirectPlayProfile container="mp4" audioCodec="mp3,aac" videoCodec="h264,mpeg4" type="Video" />
<DirectPlayProfile container="3gpp" audioCodec="aac,he-aac" videoCodec="h264,mpeg4" type="Video" />
<DirectPlayProfile container="3gp" audioCodec="aac,he-aac" videoCodec="h264,mpeg4" type="Video" />
<DirectPlayProfile container="mpg,mpeg" audioCodec="ac3,mp2,mp3,aac" videoCodec="mpeg1video,mpeg2video,h264" type="Video" />
<DirectPlayProfile container="vro,vob" audioCodec="ac3,mp2,mp3" videoCodec="mpeg1video,mpeg2video" type="Video" />
<DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3,eac3" videoCodec="mpeg2video,h264,vc1" type="Video" />

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -4,7 +4,9 @@
<Identification>
<FriendlyName>Xbox-SystemOS</FriendlyName>
<ModelName>Xbox One</ModelName>
<Headers />
<Headers>
<HttpHeaderInfo name="User-Agent" value="NSPlayer" match="Substring" />
</Headers>
</Identification>
<FriendlyName>Media Browser</FriendlyName>
<Manufacturer>Media Browser</Manufacturer>

@ -126,33 +126,29 @@ namespace MediaBrowser.LocalMetadata.Images
private void PopulateImages(IHasImages item, List<LocalImageInfo> images, List<FileSystemInfo> files, bool supportParentSeriesFiles, IDirectoryService directoryService)
{
var imagePrefix = string.Empty;
var imagePrefix = item.FileNameWithoutExtension + "-";
var isInMixedFolder = item.IsInMixedFolder;
var baseItem = item as BaseItem;
if (baseItem != null && baseItem.IsInMixedFolder)
{
imagePrefix = _fileSystem.GetFileNameWithoutExtension(item.Path) + "-";
}
PopulatePrimaryImages(item, images, files, imagePrefix);
PopulateBackdrops(item, images, files, imagePrefix, directoryService);
PopulateScreenshots(images, files, imagePrefix);
PopulatePrimaryImages(item, images, files, imagePrefix, isInMixedFolder);
AddImage(files, images, imagePrefix + "logo", ImageType.Logo);
AddImage(files, images, imagePrefix + "clearart", ImageType.Art);
AddImage(files, images, imagePrefix + "disc", ImageType.Disc);
AddImage(files, images, imagePrefix + "cdart", ImageType.Disc);
AddImage(files, images, imagePrefix + "box", ImageType.Box);
AddImage(files, images, imagePrefix + "back", ImageType.BoxRear);
AddImage(files, images, imagePrefix + "boxrear", ImageType.BoxRear);
AddImage(files, images, imagePrefix + "menu", ImageType.Menu);
AddImage(files, images, "logo", imagePrefix, isInMixedFolder, ImageType.Logo);
AddImage(files, images, "clearart", imagePrefix, isInMixedFolder, ImageType.Art);
AddImage(files, images, "disc", imagePrefix, isInMixedFolder, ImageType.Disc);
AddImage(files, images, "cdart", imagePrefix, isInMixedFolder, ImageType.Disc);
AddImage(files, images, "box", imagePrefix, isInMixedFolder, ImageType.Box);
AddImage(files, images, "back", imagePrefix, isInMixedFolder, ImageType.BoxRear);
AddImage(files, images, "boxrear", imagePrefix, isInMixedFolder, ImageType.BoxRear);
AddImage(files, images, "menu", imagePrefix, isInMixedFolder, ImageType.Menu);
// Banner
AddImage(files, images, imagePrefix + "banner", ImageType.Banner);
AddImage(files, images, "banner", imagePrefix, isInMixedFolder, ImageType.Banner);
// Thumb
AddImage(files, images, imagePrefix + "thumb", ImageType.Thumb);
AddImage(files, images, imagePrefix + "landscape", ImageType.Thumb);
AddImage(files, images, "thumb", imagePrefix, isInMixedFolder, ImageType.Thumb);
AddImage(files, images, "landscape", imagePrefix, isInMixedFolder, ImageType.Thumb);
PopulateBackdrops(item, images, files, imagePrefix, isInMixedFolder, directoryService);
PopulateScreenshots(images, files, imagePrefix, isInMixedFolder);
if (supportParentSeriesFiles)
{
@ -165,54 +161,72 @@ namespace MediaBrowser.LocalMetadata.Images
}
}
private void PopulatePrimaryImages(IHasImages item, List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix)
private void PopulatePrimaryImages(IHasImages item, List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix, bool isInMixedFolder)
{
AddImage(files, images, imagePrefix + "folder", ImageType.Primary);
AddImage(files, images, imagePrefix + "cover", ImageType.Primary);
AddImage(files, images, imagePrefix + "poster", ImageType.Primary);
AddImage(files, images, imagePrefix + "default", ImageType.Primary);
var names = new List<string>
{
"folder",
"cover",
"poster",
"default"
};
// Support plex/xbmc convention
// Support plex/kodi convention
if (item is Series)
{
AddImage(files, images, imagePrefix + "show", ImageType.Primary);
names.Add("show");
}
// Support plex/xbmc convention
// Support plex/kodi convention
if (item is Video && !(item is Episode))
{
AddImage(files, images, imagePrefix + "movie", ImageType.Primary);
names.Add("movie");
}
if (!string.IsNullOrEmpty(item.Path))
foreach (var name in names)
{
var name = _fileSystem.GetFileNameWithoutExtension(item.Path);
AddImage(files, images, imagePrefix + name, ImageType.Primary);
}
if (!string.IsNullOrEmpty(name))
var fileNameWithoutExtension = item.FileNameWithoutExtension;
if (!string.IsNullOrEmpty(fileNameWithoutExtension))
{
AddImage(files, images, fileNameWithoutExtension, ImageType.Primary);
}
if (!isInMixedFolder)
{
foreach (var name in names)
{
AddImage(files, images, name, ImageType.Primary);
AddImage(files, images, name + "-poster", ImageType.Primary);
}
}
}
private void PopulateBackdrops(IHasImages item, List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix, IDirectoryService directoryService)
private void PopulateBackdrops(IHasImages item, List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix, bool isInMixedFolder, IDirectoryService directoryService)
{
PopulateBackdrops(images, files, imagePrefix, "backdrop", "backdrop", ImageType.Backdrop);
PopulateBackdrops(images, files, imagePrefix, "backdrop", "backdrop", isInMixedFolder, ImageType.Backdrop);
if (!string.IsNullOrEmpty(item.Path))
{
var name = _fileSystem.GetFileNameWithoutExtension(item.Path);
var name = item.FileNameWithoutExtension;
if (!string.IsNullOrEmpty(name))
{
AddImage(files, images, imagePrefix + name + "-fanart", ImageType.Backdrop);
// Support without the prefix if it's in it's own folder
if (!isInMixedFolder)
{
AddImage(files, images, name + "-fanart", ImageType.Backdrop);
}
}
}
PopulateBackdrops(images, files, imagePrefix, "fanart", "fanart-", ImageType.Backdrop);
PopulateBackdrops(images, files, imagePrefix, "background", "background-", ImageType.Backdrop);
PopulateBackdrops(images, files, imagePrefix, "art", "art-", ImageType.Backdrop);
PopulateBackdrops(images, files, imagePrefix, "fanart", "fanart-", isInMixedFolder, ImageType.Backdrop);
PopulateBackdrops(images, files, imagePrefix, "background", "background-", isInMixedFolder, ImageType.Backdrop);
PopulateBackdrops(images, files, imagePrefix, "art", "art-", isInMixedFolder, ImageType.Backdrop);
var extraFanartFolder = files
.FirstOrDefault(i => string.Equals(i.Name, "extrafanart", StringComparison.OrdinalIgnoreCase));
@ -245,12 +259,12 @@ namespace MediaBrowser.LocalMetadata.Images
}));
}
private void PopulateScreenshots(List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix)
private void PopulateScreenshots(List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix, bool isInMixedFolder)
{
PopulateBackdrops(images, files, imagePrefix, "screenshot", "screenshot", ImageType.Screenshot);
PopulateBackdrops(images, files, imagePrefix, "screenshot", "screenshot", isInMixedFolder, ImageType.Screenshot);
}
private void PopulateBackdrops(List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix, string firstFileName, string subsequentFileNamePrefix, ImageType type)
private void PopulateBackdrops(List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix, string firstFileName, string subsequentFileNamePrefix, bool isInMixedFolder, ImageType type)
{
AddImage(files, images, imagePrefix + firstFileName, type);
@ -270,6 +284,29 @@ namespace MediaBrowser.LocalMetadata.Images
}
}
}
// Support without the prefix
if (!isInMixedFolder)
{
AddImage(files, images, firstFileName, type);
unfound = 0;
for (var i = 1; i <= 20; i++)
{
// Screenshot Image
var found = AddImage(files, images, subsequentFileNamePrefix + i, type);
if (!found)
{
unfound++;
if (unfound >= 3)
{
break;
}
}
}
}
}
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
@ -310,6 +347,21 @@ namespace MediaBrowser.LocalMetadata.Images
}
}
private bool AddImage(List<FileSystemInfo> files, List<LocalImageInfo> images, string name, string imagePrefix, bool isInMixedFolder, ImageType type)
{
var added = AddImage(files, images, imagePrefix + name, type);
if (!isInMixedFolder)
{
if (AddImage(files, images, name, type))
{
added = true;
}
}
return added;
}
private bool AddImage(IEnumerable<FileSystemInfo> files, List<LocalImageInfo> images, string name, ImageType type)
{
var image = GetImage(files, name) as FileInfo;

@ -72,7 +72,6 @@
<Compile Include="Providers\PlaylistXmlProvider.cs" />
<Compile Include="Providers\SeasonXmlProvider.cs" />
<Compile Include="Providers\SeriesXmlProvider.cs" />
<Compile Include="Providers\TrailerXmlProvider.cs" />
<Compile Include="Providers\VideoXmlProvider.cs" />
<Compile Include="Savers\BoxSetXmlSaver.cs" />
<Compile Include="Savers\ChannelXmlSaver.cs" />

@ -2,7 +2,9 @@
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
namespace MediaBrowser.LocalMetadata.Parsers
@ -20,7 +22,15 @@ namespace MediaBrowser.LocalMetadata.Parsers
{
case "OwnerUserId":
{
item.OwnerUserId = reader.ReadElementContentAsString();
var userId = reader.ReadElementContentAsString();
if (!item.Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)))
{
item.Shares.Add(new Share
{
UserId = userId,
CanEdit = true
});
}
break;
}

@ -1,10 +1,10 @@
using System.IO;
using System.Threading;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
namespace MediaBrowser.LocalMetadata.Providers
{
@ -25,22 +25,10 @@ namespace MediaBrowser.LocalMetadata.Providers
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
{
var fileInfo = FileSystem.GetFileSystemInfo(info.Path);
var directoryInfo = fileInfo as DirectoryInfo;
if (directoryInfo == null)
{
directoryInfo = new DirectoryInfo(Path.GetDirectoryName(info.Path));
}
var directoryPath = directoryInfo.FullName;
var specificFile = Path.Combine(directoryPath, FileSystem.GetFileNameWithoutExtension(info.Path) + ".xml");
var specificFile = Path.ChangeExtension(info.Path, ".xml");
var file = new FileInfo(specificFile);
return info.IsInMixedFolder || file.Exists ? file : new FileInfo(Path.Combine(directoryPath, "game.xml"));
return info.IsInMixedFolder || file.Exists ? file : new FileInfo(Path.Combine(Path.GetDirectoryName(info.Path), "game.xml"));
}
}
}

@ -1,37 +0,0 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace MediaBrowser.LocalMetadata.Providers
{
public class TrailerXmlProvider : BaseXmlProvider<Trailer>
{
private readonly ILogger _logger;
public TrailerXmlProvider(IFileSystem fileSystem, ILogger logger)
: base(fileSystem)
{
_logger = logger;
}
protected override void Fetch(LocalMetadataResult<Trailer> result, string path, CancellationToken cancellationToken)
{
var chapters = new List<ChapterInfo>();
new MovieXmlParser(_logger).Fetch(result.Item, chapters, path, cancellationToken);
result.Chapters = chapters;
}
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
{
return MovieXmlProvider.GetXmlFileInfo(info, FileSystem);
}
}
}

@ -57,11 +57,6 @@ namespace MediaBrowser.LocalMetadata.Savers
builder.Append("<Item>");
if (!string.IsNullOrEmpty(playlist.OwnerUserId))
{
builder.Append("<OwnerUserId>" + SecurityElement.Escape(playlist.OwnerUserId) + "</OwnerUserId>");
}
if (!string.IsNullOrEmpty(playlist.PlaylistMediaType))
{
builder.Append("<PlaylistMediaType>" + SecurityElement.Escape(playlist.PlaylistMediaType) + "</PlaylistMediaType>");

@ -645,6 +645,29 @@ namespace MediaBrowser.LocalMetadata.Savers
{
AddLinkedChildren(playlist, builder, "PlaylistItems", "PlaylistItem");
}
var hasShares = item as IHasShares;
if (hasShares != null)
{
}
}
public static void AddShares(IHasShares item, StringBuilder builder)
{
builder.Append("<Shares>");
foreach (var share in item.Shares)
{
builder.Append("<Share>");
builder.Append("<UserId>" + SecurityElement.Escape(share.UserId) + "</UserId>");
builder.Append("<CanEdit>" + SecurityElement.Escape(share.CanEdit.ToString().ToLower()) + "</CanEdit>");
builder.Append("</Share>");
}
builder.Append("</Shares>");
}
public static void AddChapters(Video item, StringBuilder builder, IItemRepository repository)

@ -299,6 +299,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
// TODO: Output in webp for smaller sizes
// -f image2 -f webp
// Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=30\" -f image2 \"{1}\"", inputPath, "-", vf) :
string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf);

@ -275,6 +275,9 @@
<Compile Include="..\MediaBrowser.Model\Devices\DeviceOptions.cs">
<Link>Devices\DeviceOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Devices\DeviceQuery.cs">
<Link>Devices\DeviceQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Devices\DevicesOptions.cs">
<Link>Devices\DevicesOptions.cs</Link>
</Compile>
@ -347,9 +350,6 @@
<Compile Include="..\MediaBrowser.Model\Dlna\ProfileConditionValue.cs">
<Link>Dlna\ProfileConditionValue.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\Profiles\AndroidProfile.cs">
<Link>Dlna\Profiles\AndroidProfile.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\Profiles\DefaultProfile.cs">
<Link>Dlna\Profiles\DefaultProfile.cs</Link>
</Compile>
@ -404,12 +404,12 @@
<Compile Include="..\MediaBrowser.Model\Drawing\DrawingUtils.cs">
<Link>Drawing\DrawingUtils.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Drawing\ImageFormat.cs">
<Link>Drawing\ImageFormat.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Drawing\ImageOrientation.cs">
<Link>Drawing\ImageOrientation.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Drawing\ImageOutputFormat.cs">
<Link>Drawing\ImageOutputFormat.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Drawing\ImageSize.cs">
<Link>Drawing\ImageSize.cs</Link>
</Compile>
@ -422,6 +422,9 @@
<Compile Include="..\MediaBrowser.Model\Dto\ChapterInfoDto.cs">
<Link>Dto\ChapterInfoDto.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dto\DtoOptions.cs">
<Link>Dto\DtoOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dto\GameSystemSummary.cs">
<Link>Dto\GameSystemSummary.cs</Link>
</Compile>
@ -1031,6 +1034,12 @@
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItem.cs">
<Link>Sync\SyncJobItem.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemQuery.cs">
<Link>SyncJobItemQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemStatus.cs">
<Link>Sync\SyncJobItemStatus.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobQuery.cs">
<Link>Sync\SyncJobQuery.cs</Link>
</Compile>
@ -1040,9 +1049,6 @@
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobStatus.cs">
<Link>Sync\SyncJobStatus.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncLimitType.cs">
<Link>Sync\SyncLimitType.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncQuality.cs">
<Link>Sync\SyncQuality.cs</Link>
</Compile>
@ -1118,6 +1124,9 @@
<Compile Include="..\MediaBrowser.Model\Users\PinRedeemResult.cs">
<Link>Users\PinRedeemResult.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Users\UserPolicy.cs">
<Link>Users\UserPolicy.cs</Link>
</Compile>
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>

@ -240,6 +240,9 @@
<Compile Include="..\MediaBrowser.Model\Devices\DeviceOptions.cs">
<Link>Devices\DeviceOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Devices\DeviceQuery.cs">
<Link>Devices\DeviceQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Devices\DevicesOptions.cs">
<Link>Devices\DevicesOptions.cs</Link>
</Compile>
@ -312,9 +315,6 @@
<Compile Include="..\MediaBrowser.Model\Dlna\ProfileConditionValue.cs">
<Link>Dlna\ProfileConditionValue.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\Profiles\AndroidProfile.cs">
<Link>Dlna\Profiles\AndroidProfile.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\Profiles\DefaultProfile.cs">
<Link>Dlna\Profiles\DefaultProfile.cs</Link>
</Compile>
@ -369,12 +369,12 @@
<Compile Include="..\MediaBrowser.Model\Drawing\DrawingUtils.cs">
<Link>Drawing\DrawingUtils.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Drawing\ImageFormat.cs">
<Link>Drawing\ImageFormat.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Drawing\ImageOrientation.cs">
<Link>Drawing\ImageOrientation.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Drawing\ImageOutputFormat.cs">
<Link>Drawing\ImageOutputFormat.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Drawing\ImageSize.cs">
<Link>Drawing\ImageSize.cs</Link>
</Compile>
@ -387,6 +387,9 @@
<Compile Include="..\MediaBrowser.Model\Dto\ChapterInfoDto.cs">
<Link>Dto\ChapterInfoDto.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dto\DtoOptions.cs">
<Link>Dto\DtoOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dto\GameSystemSummary.cs">
<Link>Dto\GameSystemSummary.cs</Link>
</Compile>
@ -990,6 +993,12 @@
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItem.cs">
<Link>Sync\SyncJobItem.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemQuery.cs">
<Link>Sync\SyncJobItemQuery.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemStatus.cs">
<Link>Sync\SyncJobItemStatus.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobQuery.cs">
<Link>Sync\SyncJobQuery.cs</Link>
</Compile>
@ -999,9 +1008,6 @@
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobStatus.cs">
<Link>Sync\SyncJobStatus.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncLimitType.cs">
<Link>Sync\SyncLimitType.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncQuality.cs">
<Link>Sync\SyncQuality.cs</Link>
</Compile>
@ -1077,6 +1083,9 @@
<Compile Include="..\MediaBrowser.Model\Users\PinRedeemResult.cs">
<Link>Users\PinRedeemResult.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Users\UserPolicy.cs">
<Link>Users\UserPolicy.cs</Link>
</Compile>
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>

@ -3,6 +3,7 @@ namespace MediaBrowser.Model.ApiClient
public enum ConnectionMode
{
Local = 1,
Remote = 2
Remote = 2,
Manual = 3
}
}

@ -14,6 +14,7 @@ using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Search;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.Sync;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Users;
@ -413,7 +414,7 @@ namespace MediaBrowser.Model.ApiClient
/// </summary>
/// <param name="query">The query.</param>
/// <returns>Task{ItemsResult}.</returns>
Task<ItemsResult> GetUpcomingEpisodesAsync(NextUpQuery query);
Task<ItemsResult> GetUpcomingEpisodesAsync(UpcomingEpisodesQuery query);
/// <summary>
/// Gets a genre
@ -1371,6 +1372,20 @@ namespace MediaBrowser.Model.ApiClient
/// <returns>Task&lt;DevicesOptions&gt;.</returns>
Task<DevicesOptions> GetDevicesOptions();
/// <summary>
/// Updates the item.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>Task.</returns>
Task UpdateItem(BaseItemDto item);
/// <summary>
/// Requests the synchronize.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>Task&lt;SyncJob&gt;.</returns>
Task<SyncJob> RequestSync(SyncJobRequest request);
/// <summary>
/// Opens the web socket.
/// </summary>

@ -50,10 +50,14 @@ namespace MediaBrowser.Model.ApiClient
{
existing.RemoteAddress = server.RemoteAddress;
}
if (!existing.IsLocalAddressFixed && !string.IsNullOrEmpty(server.LocalAddress))
if (!string.IsNullOrEmpty(server.LocalAddress))
{
existing.LocalAddress = server.LocalAddress;
}
if (!string.IsNullOrEmpty(server.ManualAddress))
{
existing.LocalAddress = server.ManualAddress;
}
if (!string.IsNullOrEmpty(server.Name))
{
existing.Name = server.Name;
@ -62,9 +66,9 @@ namespace MediaBrowser.Model.ApiClient
{
existing.WakeOnLanInfos = server.WakeOnLanInfos.ToList();
}
if (server.IsLocalAddressFixed)
if (server.LastConnectionMode.HasValue)
{
existing.IsLocalAddressFixed = true;
existing.LastConnectionMode = server.LastConnectionMode;
}
}
else

@ -18,5 +18,10 @@ namespace MediaBrowser.Model.ApiClient
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the endpoint address.
/// </summary>
/// <value>The endpoint address.</value>
public string EndpointAddress { get; set; }
}
}

@ -11,14 +11,14 @@ namespace MediaBrowser.Model.ApiClient
public String Id { get; set; }
public String LocalAddress { get; set; }
public String RemoteAddress { get; set; }
public String ManualAddress { get; set; }
public String UserId { get; set; }
public String AccessToken { get; set; }
public List<WakeOnLanInfo> WakeOnLanInfos { get; set; }
public DateTime DateLastAccessed { get; set; }
public String ExchangeToken { get; set; }
public UserLinkType? UserLinkType { get; set; }
public bool IsLocalAddressFixed { get; set; }
public ConnectionMode? LastConnectionMode { get; set; }
public ServerInfo()
{
@ -30,7 +30,7 @@ namespace MediaBrowser.Model.ApiClient
Name = systemInfo.ServerName;
Id = systemInfo.Id;
if (!IsLocalAddressFixed && !string.IsNullOrEmpty(systemInfo.LocalAddress))
if (!string.IsNullOrEmpty(systemInfo.LocalAddress))
{
LocalAddress = systemInfo.LocalAddress;
}
@ -55,5 +55,20 @@ namespace MediaBrowser.Model.ApiClient
}
}
}
public string GetAddress(ConnectionMode mode)
{
switch (mode)
{
case ConnectionMode.Local:
return LocalAddress;
case ConnectionMode.Manual:
return ManualAddress;
case ConnectionMode.Remote:
return RemoteAddress;
default:
throw new ArgumentException("Unexpected ConnectionMode");
}
}
}
}

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

Loading…
Cancel
Save